From 04f87da71ab532ee1d794cbb2a464d2f35cd118d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Aug 2011 14:50:29 +0200 Subject: block: separate priority boosting from REQ_META Add a new REQ_PRIO to let requests preempt others in the cfq I/O schedule, and lave REQ_META purely for marking requests as metadata in blktrace. All existing callers of REQ_META except for XFS are updated to also set REQ_PRIO for now. Backported to 3.0.x by Ketut Putu Kumajaya Change-Id: Iad5ba7a105438776f74788c0aedaf85210c613f9 --- block/cfq-iosched.c | 20 ++++++++++---------- fs/ext3/inode.c | 4 ++-- fs/ext3/namei.c | 3 ++- fs/ext4/inode.c | 4 ++-- fs/ext4/namei.c | 3 ++- fs/gfs2/log.c | 4 ++-- fs/gfs2/meta_io.c | 6 +++--- fs/gfs2/ops_fstype.c | 2 +- fs/gfs2/quota.c | 2 +- include/linux/blk_types.h | 6 ++++-- 10 files changed, 29 insertions(+), 25 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index bfe3bbe..fb5fe2d 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -129,8 +129,8 @@ struct cfq_queue { unsigned long slice_end; long slice_resid; - /* pending metadata requests */ - int meta_pending; + /* pending priority requests */ + int prio_pending; /* number of requests that are on the dispatch list or inside driver */ int dispatched; @@ -670,8 +670,8 @@ cfq_choose_req(struct cfq_data *cfqd, struct request *rq1, struct request *rq2, if (rq_is_sync(rq1) != rq_is_sync(rq2)) return rq_is_sync(rq1) ? rq1 : rq2; - if ((rq1->cmd_flags ^ rq2->cmd_flags) & REQ_META) - return rq1->cmd_flags & REQ_META ? rq1 : rq2; + if ((rq1->cmd_flags ^ rq2->cmd_flags) & REQ_PRIO) + return rq1->cmd_flags & REQ_PRIO ? rq1 : rq2; s1 = blk_rq_pos(rq1); s2 = blk_rq_pos(rq2); @@ -1598,9 +1598,9 @@ static void cfq_remove_request(struct request *rq) cfqq->cfqd->rq_queued--; cfq_blkiocg_update_io_remove_stats(&(RQ_CFQG(rq))->blkg, rq_data_dir(rq), rq_is_sync(rq)); - if (rq->cmd_flags & REQ_META) { - WARN_ON(!cfqq->meta_pending); - cfqq->meta_pending--; + if (rq->cmd_flags & REQ_PRIO) { + WARN_ON(!cfqq->prio_pending); + cfqq->prio_pending--; } } @@ -3351,7 +3351,7 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, * So both queues are sync. Let the new request get disk time if * it's a metadata request and the current queue is doing regular IO. */ - if ((rq->cmd_flags & REQ_META) && !cfqq->meta_pending) + if ((rq->cmd_flags & REQ_PRIO) && !cfqq->prio_pending) return true; /* @@ -3418,8 +3418,8 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct cfq_io_context *cic = RQ_CIC(rq); cfqd->rq_queued++; - if (rq->cmd_flags & REQ_META) - cfqq->meta_pending++; + if (rq->cmd_flags & REQ_PRIO) + cfqq->prio_pending++; cfq_update_io_thinktime(cfqd, cic); cfq_update_io_seektime(cfqd, cfqq, rq); diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 0aedb27..fdc0e06 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1102,7 +1102,7 @@ struct buffer_head *ext3_bread(handle_t *handle, struct inode *inode, return bh; if (buffer_uptodate(bh)) return bh; - ll_rw_block(READ_META, 1, &bh); + ll_rw_block(READ | REQ_META | REQ_PRIO, 1, &bh); wait_on_buffer(bh); if (buffer_uptodate(bh)) return bh; @@ -2766,7 +2766,7 @@ make_io: */ get_bh(bh); bh->b_end_io = end_buffer_read_sync; - submit_bh(READ_META, bh); + submit_bh(READ | REQ_META | REQ_PRIO, bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) { ext3_error(inode->i_sb, "ext3_get_inode_loc", diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 8c9f82d..7be2c76 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -918,7 +918,8 @@ restart: bh = ext3_getblk(NULL, dir, b++, 0, &err); bh_use[ra_max] = bh; if (bh) - ll_rw_block(READ_META, 1, &bh); + ll_rw_block(READ | REQ_META | REQ_PRIO, + 1, &bh); } } if ((bh = bh_use[ra_ptr++]) == NULL) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0a0c404..294bc8f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1517,7 +1517,7 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode, return bh; if (buffer_uptodate(bh)) return bh; - ll_rw_block(READ_META, 1, &bh); + ll_rw_block(READ | REQ_META | REQ_PRIO, 1, &bh); wait_on_buffer(bh); if (buffer_uptodate(bh)) return bh; @@ -4827,7 +4827,7 @@ make_io: trace_ext4_load_inode(inode); get_bh(bh); bh->b_end_io = end_buffer_read_sync; - submit_bh(READ_META, bh); + submit_bh(READ | REQ_META | REQ_PRIO, bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) { EXT4_ERROR_INODE_BLOCK(inode, block, diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 8b227a9..4098064 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -919,7 +919,8 @@ restart: bh = ext4_getblk(NULL, dir, b++, 0, &err); bh_use[ra_max] = bh; if (bh) - ll_rw_block(READ_META, 1, &bh); + ll_rw_block(READ | REQ_META | REQ_PRIO, + 1, &bh); } } if ((bh = bh_use[ra_ptr++]) == NULL) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 85c6292..5986464 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -624,9 +624,9 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) bh->b_end_io = end_buffer_write_sync; get_bh(bh); if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) - submit_bh(WRITE_SYNC | REQ_META, bh); + submit_bh(WRITE_SYNC | REQ_META | REQ_PRIO, bh); else - submit_bh(WRITE_FLUSH_FUA | REQ_META, bh); + submit_bh(WRITE_FLUSH_FUA | REQ_META | REQ_PRIO, bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 747238c..be29858 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -37,7 +37,7 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb { struct buffer_head *bh, *head; int nr_underway = 0; - int write_op = REQ_META | + int write_op = REQ_META | REQ_PRIO | (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE); BUG_ON(!PageLocked(page)); @@ -225,7 +225,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, } bh->b_end_io = end_buffer_read_sync; get_bh(bh); - submit_bh(READ_SYNC | REQ_META, bh); + submit_bh(READ_SYNC | REQ_META | REQ_PRIO, bh); if (!(flags & DIO_WAIT)) return 0; @@ -435,7 +435,7 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen) if (buffer_uptodate(first_bh)) goto out; if (!buffer_locked(first_bh)) - ll_rw_block(READ_SYNC | REQ_META, 1, &first_bh); + ll_rw_block(READ_SYNC | REQ_META | REQ_PRIO, 1, &first_bh); dblock++; extlen--; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index fa780e6..7b130cd 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -224,7 +224,7 @@ static int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector, int silent) bio->bi_end_io = end_bio_io_page; bio->bi_private = page; - submit_bio(READ_SYNC | REQ_META, bio); + submit_bio(READ_SYNC | REQ_META | REQ_PRIO, bio); wait_on_page_locked(page); bio_put(bio); if (!PageUptodate(page)) { diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 42e8d23..0e8bb13 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -709,7 +709,7 @@ get_a_page: set_buffer_uptodate(bh); if (!buffer_uptodate(bh)) { - ll_rw_block(READ_META, 1, &bh); + ll_rw_block(READ | REQ_META | REQ_PRIO, 1, &bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) goto unlock_out; diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 6395692..cee53e3 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -124,6 +124,7 @@ enum rq_flag_bits { __REQ_SYNC, /* request is sync (sync write or read) */ __REQ_META, /* metadata io request */ + __REQ_PRIO, /* boost priority in cfq */ __REQ_DISCARD, /* request to discard sectors */ __REQ_NOIDLE, /* don't anticipate more IO after this one */ @@ -160,14 +161,15 @@ enum rq_flag_bits { #define REQ_FAILFAST_DRIVER (1 << __REQ_FAILFAST_DRIVER) #define REQ_SYNC (1 << __REQ_SYNC) #define REQ_META (1 << __REQ_META) +#define REQ_PRIO (1 << __REQ_PRIO) #define REQ_DISCARD (1 << __REQ_DISCARD) #define REQ_NOIDLE (1 << __REQ_NOIDLE) #define REQ_FAILFAST_MASK \ (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER) #define REQ_COMMON_MASK \ - (REQ_WRITE | REQ_FAILFAST_MASK | REQ_SYNC | REQ_META | REQ_DISCARD | \ - REQ_NOIDLE | REQ_FLUSH | REQ_FUA | REQ_SECURE) + (REQ_WRITE | REQ_FAILFAST_MASK | REQ_SYNC | REQ_META | REQ_PRIO | \ + REQ_DISCARD | REQ_NOIDLE | REQ_FLUSH | REQ_FUA | REQ_SECURE) #define REQ_CLONE_MASK REQ_COMMON_MASK #define REQ_RAHEAD (1 << __REQ_RAHEAD) -- cgit v1.1 From 9b27e72f7f1d4683fa1d1d74871d78a03a7992ba Mon Sep 17 00:00:00 2001 From: rogersb11 Date: Sat, 13 Feb 2016 23:31:54 -0500 Subject: t0lte: Enable F2FS Change-Id: I1859403f3090df5b1a82392983c9524ce0a3d701 --- arch/arm/configs/cyanogenmod_t0lte_defconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/configs/cyanogenmod_t0lte_defconfig b/arch/arm/configs/cyanogenmod_t0lte_defconfig index 14f8109..b69d4fe 100755 --- a/arch/arm/configs/cyanogenmod_t0lte_defconfig +++ b/arch/arm/configs/cyanogenmod_t0lte_defconfig @@ -2983,6 +2983,12 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +CONFIG_F2FS_FS=y +# CONFIG_F2FS_STAT_FS is not set +CONFIG_F2FS_FS_XATTR=y +# CONFIG_F2FS_FS_POSIX_ACL is not set +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_CHECK_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y -- cgit v1.1 From d4709cc8d054379346d263a1ca69484ac63287a6 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Sun, 14 Feb 2016 22:40:22 +1100 Subject: i9300: enable f2fs support Change-Id: Iaa6bab722bb5ffb31fc9863d1841680f2ea157dd --- arch/arm/configs/cyanogenmod_i9300_defconfig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 1438605..69204ce 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -3044,7 +3044,12 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -# CONFIG_F2FS_FS is not set +CONFIG_F2FS_FS=y +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_CHECK_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y -- cgit v1.1 From 974a591f891c0c7fb45a26edad8d8571e1337cee Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 10 Feb 2014 13:16:29 -0800 Subject: timekeeping: fix 32-bit overflow in get_monotonic_boottime fixed upstream in v3.6 by ec145babe754f9ea1079034a108104b6001e001c get_monotonic_boottime adds three nanonsecond values stored in longs, followed by an s64. If the long values are all close to 1e9 the first three additions can overflow and become negative when added to the s64. Cast the first value to s64 so that all additions are 64 bit. Signed-off-by: Colin Cross [jstultz: Fished this out of the AOSP commong.git tree. This was fixed upstream in v3.6 by ec145babe754f9ea1079034a108104b6001e001c] Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman Change-Id: If4ec90946bb49b022403c14e63903b3027dd5c58 --- kernel/time/timekeeping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e188977..7aa8c3d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1059,7 +1059,7 @@ void get_monotonic_boottime(struct timespec *ts) } while (read_seqretry(&xtime_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); -- cgit v1.1 From 862518ae8df76d87c7e816e272a26ad0a9cb1464 Mon Sep 17 00:00:00 2001 From: Marcelo Leitner Date: Mon, 23 Feb 2015 11:17:13 -0300 Subject: ipv6: addrconf: validate new MTU before applying it Currently we don't check if the new MTU is valid or not and this allows one to configure a smaller than minimum allowed by RFCs or even bigger than interface own MTU, which is a problem as it may lead to packet drops. If you have a daemon like NetworkManager running, this may be exploited by remote attackers by forging RA packets with an invalid MTU, possibly leading to a DoS. (NetworkManager currently only validates for values too small, but not for too big ones.) The fix is just to make sure the new value is valid. That is, between IPV6_MIN_MTU and interface's MTU. Note that similar check is already performed at ndisc_router_discovery(), for when kernel itself parses the RA. Change-Id: I6b70d0c12a77c7932066982f8797d8024f130d7c Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index ae24c6a..5574fc2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4241,6 +4241,21 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, return ret; } +static +int addrconf_sysctl_mtu(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct inet6_dev *idev = ctl->extra1; + int min_mtu = IPV6_MIN_MTU; + struct ctl_table lctl; + + lctl = *ctl; + lctl.extra1 = &min_mtu; + lctl.extra2 = idev ? &idev->dev->mtu : NULL; + + return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos); +} + static void dev_disable_change(struct inet6_dev *idev) { if (!idev || !idev->dev) @@ -4341,7 +4356,7 @@ static struct addrconf_sysctl_table .data = &ipv6_devconf.mtu6, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = addrconf_sysctl_mtu, }, { .procname = "accept_ra", -- cgit v1.1 From 289184d8d4ef53fc254449ee99fdcef3b064aedc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Jul 2015 17:17:57 -0700 Subject: net: fix iterating over hashtable in tcp_nuke_addr() The actual size of the tcp hashinfo table is tcp_hashinfo.ehash_mask + 1 so we need to adjust the loop accordingly to get the sockets hashed into the last bucket. Change-Id: I796b3c7b4a1a7fa35fba9e5192a4a403eb6e17de Signed-off-by: Dmitry Torokhov --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 96bb1dc..58117bd 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3379,7 +3379,7 @@ int tcp_nuke_addr(struct net *net, struct sockaddr *addr) return -EAFNOSUPPORT; } - for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { + 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); -- cgit v1.1 From 5e60df5a7c346658adad7e8eafb1450491a660bb Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 14 Dec 2015 22:03:39 +0100 Subject: net: add validation for the socket syscall protocol argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 郭永刚 reported that one could simply crash the kernel as root by using a simple program: int socket_fd; struct sockaddr_in addr; addr.sin_port = 0; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_family = 10; socket_fd = socket(10,3,0x40000000); connect(socket_fd , &addr,16); AF_INET, AF_INET6 sockets actually only support 8-bit protocol identifiers. inet_sock's skc_protocol field thus is sized accordingly, thus larger protocol identifiers simply cut off the higher bits and store a zero in the protocol fields. This could lead to e.g. NULL function pointer because as a result of the cut off inet_num is zero and we call down to inet_autobind, which is NULL for raw sockets. kernel: Call Trace: kernel: [] ? inet_autobind+0x2e/0x70 kernel: [] inet_dgram_connect+0x54/0x80 kernel: [] SYSC_connect+0xd9/0x110 kernel: [] ? ptrace_notify+0x5b/0x80 kernel: [] ? syscall_trace_enter_phase2+0x108/0x200 kernel: [] SyS_connect+0xe/0x10 kernel: [] tracesys_phase2+0x84/0x89 I found no particular commit which introduced this problem. Change-Id: If01a1f7d3c652e8e67d5090eb8ea91389829b2ea CVE: CVE-2015-8543 Cc: Cong Wang Reported-by: 郭永刚 Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/sock.h | 1 + net/ax25/af_ax25.c | 3 +++ net/decnet/af_decnet.c | 3 +++ net/ipv4/af_inet.c | 3 +++ net/ipv6/af_inet6.c | 3 +++ net/irda/af_irda.c | 3 +++ 6 files changed, 16 insertions(+) diff --git a/include/net/sock.h b/include/net/sock.h index b6abd4f..1831207 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -303,6 +303,7 @@ struct sock { sk_no_check : 2, sk_userlocks : 4, sk_protocol : 8, +#define SK_PROTOCOL_MAX U8_MAX sk_type : 16; kmemcheck_bitfield_end(flags); int sk_wmem_queued; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 86ac37f..6fab760 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -806,6 +806,9 @@ static int ax25_create(struct net *net, struct socket *sock, int protocol, struct sock *sk; ax25_cb *ax25; + if (protocol < 0 || protocol > SK_PROTOCOL_MAX) + return -EINVAL; + if (!net_eq(net, &init_net)) return -EAFNOSUPPORT; diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index d71f0d2..301a2d1 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -680,6 +680,9 @@ static int dn_create(struct net *net, struct socket *sock, int protocol, { struct sock *sk; + if (protocol < 0 || protocol > SK_PROTOCOL_MAX) + return -EINVAL; + if (!net_eq(net, &init_net)) return -EAFNOSUPPORT; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ba78b76..bd58a82 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -293,6 +293,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; + if (protocol < 0 || protocol >= IPPROTO_MAX) + return -EINVAL; + if (!current_has_network()) return -EACCES; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 8ab921a6..e80bb5a 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -124,6 +124,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; + if (protocol < 0 || protocol >= IPPROTO_MAX) + return -EINVAL; + if (!current_has_network()) return -EACCES; diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 402af94..26cf2f9 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -1106,6 +1106,9 @@ static int irda_create(struct net *net, struct socket *sock, int protocol, IRDA_DEBUG(2, "%s()\n", __func__); + if (protocol < 0 || protocol > SK_PROTOCOL_MAX) + return -EINVAL; + if (net != &init_net) return -EAFNOSUPPORT; -- cgit v1.1 From 9c25bb369d3df8816610919ae76faaf5fb04c14a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Dec 2015 15:39:08 -0500 Subject: bluetooth: Validate socket address length in sco_sock_bind(). Change-Id: Id051e0c1a5d89ea90e9b058175c37c952cad70bc Signed-off-by: David S. Miller --- net/bluetooth/sco.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index ac4631d..21aded9 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -476,6 +476,9 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; + if (alen < sizeof(struct sockaddr_sco)) + return -EINVAL; + memset(&sa, 0, sizeof(sa)); len = min_t(unsigned int, sizeof(sa), alen); memcpy(&sa, addr, len); -- cgit v1.1 From 7263d7854a4418052aafc520e66313ffc8a2bedc Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 6 Jan 2014 00:57:54 +0100 Subject: netfilter: nf_conntrack_dccp: fix skb_header_pointer API usages Some occurences in the netfilter tree use skb_header_pointer() in the following way ... struct dccp_hdr _dh, *dh; ... skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); ... where dh itself is a pointer that is being passed as the copy buffer. Instead, we need to use &_dh as the forth argument so that we're copying the data into an actual buffer that sits on the stack. Currently, we probably could overwrite memory on the stack (e.g. with a possibly mal-formed DCCP packet), but unintentionally, as we only want the buffer to be placed into _dh variable. Change-Id: I61e449445e085beec3697ec9ffbad0838fb6bff8 Fixes: 2bc780499aa3 ("[NETFILTER]: nf_conntrack: add DCCP protocol support") Signed-off-by: Daniel Borkmann Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_dccp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 2e664a6..8aa94ee 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -431,7 +431,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, const char *msg; u_int8_t state; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); + dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); BUG_ON(dh == NULL); state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE]; @@ -483,7 +483,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, u_int8_t type, old_state, new_state; enum ct_dccp_roles role; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); + dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); BUG_ON(dh == NULL); type = dh->dccph_type; @@ -575,7 +575,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl, unsigned int cscov; const char *msg; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); + dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); if (dh == NULL) { msg = "nf_ct_dccp: short packet "; goto out_invalid; -- cgit v1.1 From b6ad77af717a40d98627ad297b096d5cb1d36eb9 Mon Sep 17 00:00:00 2001 From: "D.S. Ljungmark" Date: Wed, 25 Mar 2015 09:28:15 +0100 Subject: ipv6: Don't reduce hop limit for an interface A local route may have a lower hop_limit set than global routes do. RFC 3756, Section 4.2.7, "Parameter Spoofing" > 1. The attacker includes a Current Hop Limit of one or another small > number which the attacker knows will cause legitimate packets to > be dropped before they reach their destination. > As an example, one possible approach to mitigate this threat is to > ignore very small hop limits. The nodes could implement a > configurable minimum hop limit, and ignore attempts to set it below > said limit. Change-Id: I8d9d9cd978b8d5587cc1056dc65683291f0a153a Signed-off-by: D.S. Ljungmark Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f01c153..d8878ed 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1284,7 +1284,14 @@ static void ndisc_router_discovery(struct sk_buff *skb) rt->rt6i_expires = jiffies + (HZ * lifetime); if (ra_msg->icmph.icmp6_hop_limit) { - in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; + /* Only set hop_limit on the interface if it is higher than + * the current hop_limit. + */ + if (in6_dev->cnf.hop_limit < ra_msg->icmph.icmp6_hop_limit) { + in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; + } else { + ND_PRINTK2(KERN_WARNING, "RA: Got route advertisement with lower hop_limit than current\n"); + } if (rt) dst_metric_set(&rt->dst, RTAX_HOPLIMIT, ra_msg->icmph.icmp6_hop_limit); -- cgit v1.1 From a7a9d8516ec987f32d62e33594210a0fee392498 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Fri, 28 Mar 2014 13:54:32 +0400 Subject: netfilter: nf_conntrack: reserve two bytes for nf_ct_ext->len "len" contains sizeof(nf_ct_ext) and size of extensions. In a worst case it can contain all extensions. Bellow you can find sizes for all types of extensions. Their sum is definitely bigger than 256. nf_ct_ext_types[0]->len = 24 nf_ct_ext_types[1]->len = 32 nf_ct_ext_types[2]->len = 24 nf_ct_ext_types[3]->len = 32 nf_ct_ext_types[4]->len = 152 nf_ct_ext_types[5]->len = 2 nf_ct_ext_types[6]->len = 16 nf_ct_ext_types[7]->len = 8 I have seen "len" up to 280 and my host has crashes w/o this patch. The right way to fix this problem is reducing the size of the ecache extension (4) and Florian is going to do this, but these changes will be quite large to be appropriate for a stable tree. Change-Id: Id44470ab1d54526993927cdda68342e591a5d6c3 Fixes: 5b423f6a40a0 (netfilter: nf_conntrack: fix racy timer handling with reliable) Cc: Pablo Neira Ayuso Cc: Patrick McHardy Cc: Jozsef Kadlecsik Cc: "David S. Miller" Signed-off-by: Andrey Vagin Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 2dcf317..d918074 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -33,8 +33,8 @@ enum nf_ct_ext_id { /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { struct rcu_head rcu; - u8 offset[NF_CT_EXT_NUM]; - u8 len; + u16 offset[NF_CT_EXT_NUM]; + u16 len; char data[0]; }; -- cgit v1.1 From 6a5c4b88f9d9d3e985c9503af7cbcab0454b0887 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 26 Sep 2014 11:35:42 +0200 Subject: netfilter: conntrack: disable generic tracking for known protocols Given following iptables ruleset: -P FORWARD DROP -A FORWARD -m sctp --dport 9 -j ACCEPT -A FORWARD -p tcp --dport 80 -j ACCEPT -A FORWARD -p tcp -m conntrack -m state ESTABLISHED,RELATED -j ACCEPT One would assume that this allows SCTP on port 9 and TCP on port 80. Unfortunately, if the SCTP conntrack module is not loaded, this allows *all* SCTP communication, to pass though, i.e. -p sctp -j ACCEPT, which we think is a security issue. This is because on the first SCTP packet on port 9, we create a dummy "generic l4" conntrack entry without any port information (since conntrack doesn't know how to extract this information). All subsequent packets that are unknown will then be in established state since they will fallback to proto_generic and will match the 'generic' entry. Our originally proposed version [1] completely disabled generic protocol tracking, but Jozsef suggests to not track protocols for which a more suitable helper is available, hence we now mitigate the issue for in tree known ct protocol helpers only, so that at least NAT and direction information will still be preserved for others. [1] http://www.spinics.net/lists/netfilter-devel/msg33430.html Joint work with Daniel Borkmann. Change-Id: I07168aac05cc01ebbb900aad884a672cadc24569 Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_generic.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index e2091d0..53bf12a 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -14,6 +14,30 @@ static unsigned int nf_ct_generic_timeout __read_mostly = 600*HZ; +static bool nf_generic_should_process(u8 proto) +{ + switch (proto) { +#ifdef CONFIG_NF_CT_PROTO_SCTP_MODULE + case IPPROTO_SCTP: + return false; +#endif +#ifdef CONFIG_NF_CT_PROTO_DCCP_MODULE + case IPPROTO_DCCP: + return false; +#endif +#ifdef CONFIG_NF_CT_PROTO_GRE_MODULE + case IPPROTO_GRE: + return false; +#endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE_MODULE + case IPPROTO_UDPLITE: + return false; +#endif + default: + return true; + } +} + static bool generic_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) @@ -56,7 +80,7 @@ static int packet(struct nf_conn *ct, static bool new(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff) { - return true; + return nf_generic_should_process(nf_ct_protonum(ct)); } #ifdef CONFIG_SYSCTL -- cgit v1.1 From 2acd51f51ffd6e14b24d812da47594f53d917378 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 23 Jan 2015 20:47:00 -0500 Subject: net: llc: use correct size for sysctl timeout entries The timeout entries are sizeof(int) rather than sizeof(long), which means that when they were getting read we'd also leak kernel memory to userspace along with the timeout values. Change-Id: I65ab7dc468480dca04108e077194735fa9dc61b5 Signed-off-by: Sasha Levin Signed-off-by: David S. Miller --- net/llc/sysctl_net_llc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c index e2ebe35..be078ec 100644 --- a/net/llc/sysctl_net_llc.c +++ b/net/llc/sysctl_net_llc.c @@ -17,28 +17,28 @@ static struct ctl_table llc2_timeout_table[] = { { .procname = "ack", .data = &sysctl_llc2_ack_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_ack_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "busy", .data = &sysctl_llc2_busy_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_busy_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "p", .data = &sysctl_llc2_p_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_p_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "rej", .data = &sysctl_llc2_rej_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_rej_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, -- cgit v1.1 From 5080905535c3c977a96a0ef7faaa5095dafd7760 Mon Sep 17 00:00:00 2001 From: Harry Lin Date: Thu, 9 Jul 2015 13:00:54 +1000 Subject: udp: fix behavior of wrong checksums We have two problems in UDP stack related to bogus checksums : 1 ) We return -EGAIN to application even if receive queue is not empty. This breaks applications using edge trigger epoll() 2 ) Under UDP flood, we can loop forever without yielding to other processes, potentially hanging the host, especially on non SMP. This patch is an attempt to make things better. We might in the future add extra support for rt applications wanting to better control time spent doing a recv() in a hostile environment. For example we could validate checksums before queuing packets in socket receive queue. Change-Id: Ic25820228fd735e25a850951e059bf75ae39b653 Signed-off-by: Eruc Dumazet Cc: Willem de Brujin Signed-off-by: David S. Miller --- net/ipv4/udp.c | 6 ++---- net/ipv6/udp.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 966c3e1..2f00d32 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1249,10 +1249,8 @@ csum_copy_err: UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_INERRORS, is_udplite); unlock_sock_fast(sk, slow); - if (noblock) - return -EAGAIN; - - /* starting over for a new packet */ + /* starting over for a new packet, but check if we need to yield */ + cond_resched(); msg->msg_flags &= ~MSG_TRUNC; goto try_again; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5c363cd..823d1ef 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -453,10 +453,8 @@ csum_copy_err: } unlock_sock_fast(sk, slow); - if (noblock) - return -EAGAIN; - - /* starting over for a new packet */ + /* starting over for a new packet, but check if we need to yield */ + cond_resched(); msg->msg_flags &= ~MSG_TRUNC; goto try_again; } -- cgit v1.1 From 13696bfd759e22d99eab7a81b18341b05772aad4 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Fri, 23 Jan 2015 12:01:26 +0100 Subject: ipv4: try to cache dst_entries which would cause a redirect Not caching dst_entries which cause redirects could be exploited by hosts on the same subnet, causing a severe DoS attack. This effect aggravated since commit f88649721268999 ("ipv4: fix dst race in sk_dst_get()"). Lookups causing redirects will be allocated with DST_NOCACHE set which will force dst_release to free them via RCU. Unfortunately waiting for RCU grace period just takes too long, we can end up with >1M dst_entries waiting to be released and the system will run OOM. rcuos threads cannot catch up under high softirq load. Attaching the flag to emit a redirect later on to the specific skb allows us to cache those dst_entries thus reducing the pressure on allocation and deallocation. This issue was discovered by Marcelo Leitner. Cc: Julian Anastasov Signed-off-by: Marcelo Leitner Signed-off-by: Florian Westphal Signed-off-by: Hannes Frederic Sowa Signed-off-by: Julian Anastasov Signed-off-by: David S. Miller Conflicts: include/net/ip.h net/ipv4/route.c Change-Id: I53e4b500a4db2f5fece937a42a3bd810b2640c44 --- include/net/ip.h | 11 ++++++----- net/ipv4/ip_forward.c | 3 ++- net/ipv4/route.c | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index 29e31ae..8729846 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -37,11 +37,12 @@ struct inet_skb_parm { struct ip_options opt; /* Compiled IP options */ unsigned char flags; -#define IPSKB_FORWARDED 1 -#define IPSKB_XFRM_TUNNEL_SIZE 2 -#define IPSKB_XFRM_TRANSFORMED 4 -#define IPSKB_FRAG_COMPLETE 8 -#define IPSKB_REROUTED 16 +#define IPSKB_FORWARDED BIT(0) +#define IPSKB_XFRM_TUNNEL_SIZE BIT(1) +#define IPSKB_XFRM_TRANSFORMED BIT(2) +#define IPSKB_FRAG_COMPLETE BIT(3) +#define IPSKB_REROUTED BIT(4) +#define IPSKB_DOREDIRECT BIT(5) }; static inline unsigned int ip_hdrlen(const struct sk_buff *skb) diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 29a07b6..0ee2d50 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -107,7 +107,8 @@ int ip_forward(struct sk_buff *skb) * We now generate an ICMP HOST REDIRECT giving the route * we calculated. */ - if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb)) + if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr && + !skb_sec_path(skb)) ip_rt_send_redirect(skb); skb->priority = rt_tos2priority(iph->tos); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index cd40415..666b486 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2123,9 +2123,10 @@ static int __mkroute_input(struct sk_buff *skb, flags |= RTCF_DIRECTSRC; if (out_dev == in_dev && err && + skb->protocol == htons(ETH_P_IP) && (IN_DEV_SHARED_MEDIA(out_dev) || inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) - flags |= RTCF_DOREDIRECT; + IPCB(skb)->flags |= IPSKB_DOREDIRECT; if (skb->protocol != htons(ETH_P_IP)) { /* Not IP (i.e. ARP). Do not create route, if it is @@ -2949,6 +2950,8 @@ static int rt_fill_info(struct net *net, r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED; if (rt->rt_flags & RTCF_NOTIFY) r->rtm_flags |= RTM_F_NOTIFY; + if (IPCB(skb)->flags & IPSKB_DOREDIRECT) + r->rtm_flags |= RTCF_DOREDIRECT; NLA_PUT_BE32(skb, RTA_DST, rt->rt_dst); -- cgit v1.1 From f7a711c992bb3272bc07d66c60ec79a276638817 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 Sep 2015 21:55:07 -0700 Subject: tcp_cubic: better follow cubic curve after idle period Jana Iyengar found an interesting issue on CUBIC : The epoch is only updated/reset initially and when experiencing losses. The delta "t" of now - epoch_start can be arbitrary large after app idle as well as the bic_target. Consequentially the slope (inverse of ca->cnt) would be really large, and eventually ca->cnt would be lower-bounded in the end to 2 to have delayed-ACK slow-start behavior. This particularly shows up when slow_start_after_idle is disabled as a dangerous cwnd inflation (1.5 x RTT) after few seconds of idle time. Jana initial fix was to reset epoch_start if app limited, but Neal pointed out it would ask the CUBIC algorithm to recalculate the curve so that we again start growing steeply upward from where cwnd is now (as CUBIC does just after a loss). Ideally we'd want the cwnd growth curve to be the same shape, just shifted later in time by the amount of the idle period. Change-Id: Ia99e2743517729b1c0589fbb8db9965ec806043b Reported-by: Jana Iyengar Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Cc: Stephen Hemminger Cc: Sangtae Ha Cc: Lawrence Brakmo Signed-off-by: David S. Miller Signed-off-by: Andrea Arcangeli --- net/ipv4/tcp_cubic.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index b78eac2..ceb6362 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -151,6 +151,21 @@ static void bictcp_init(struct sock *sk) tcp_sk(sk)->snd_ssthresh = initial_ssthresh; } +static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_TX_START) { + s32 delta = tcp_time_stamp - tcp_sk(sk)->lsndtime; + struct bictcp *ca = inet_csk_ca(sk); + + /* We were application limited (idle) for a while. + * Shift epoch_start to keep cwnd growth to cubic curve. + */ + if (ca->epoch_start && delta > 0) + ca->epoch_start += delta; + return; + } +} + /* calculate the cubic root of x using a table lookup followed by one * Newton-Raphson iteration. * Avg err ~= 0.195% @@ -437,6 +452,7 @@ static struct tcp_congestion_ops cubictcp __read_mostly = { .cong_avoid = bictcp_cong_avoid, .set_state = bictcp_state, .undo_cwnd = bictcp_undo_cwnd, + .cwnd_event = bictcp_cwnd_event, .pkts_acked = bictcp_acked, .owner = THIS_MODULE, .name = "cubic", -- cgit v1.1 From 13d1b294893bb995fa38660585745e6a23631075 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 17 Sep 2015 08:38:00 -0700 Subject: tcp_cubic: do not set epoch_start in the future Tracking idle time in bictcp_cwnd_event() is imprecise, as epoch_start is normally set at ACK processing time, not at send time. Doing a proper fix would need to add an additional state variable, and does not seem worth the trouble, given CUBIC bug has been there forever before Jana noticed it. Let's simply not set epoch_start in the future, otherwise bictcp_update() could overflow and CUBIC would again grow cwnd too fast. This was detected thanks to a packetdrill test Neal wrote that was flaky before applying this fix. Change-Id: I600d3a8ac0ed1d70f3ff5cc6b7341ac13178f4f3 Fixes: 30927520dbae ("tcp_cubic: better follow cubic curve after idle period") Signed-off-by: Eric Dumazet Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Cc: Jana Iyengar Signed-off-by: David S. Miller Signed-off-by: Andrea Arcangeli --- net/ipv4/tcp_cubic.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index ceb6362..2cee558 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -154,14 +154,20 @@ static void bictcp_init(struct sock *sk) static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) { if (event == CA_EVENT_TX_START) { - s32 delta = tcp_time_stamp - tcp_sk(sk)->lsndtime; struct bictcp *ca = inet_csk_ca(sk); + u32 now = tcp_time_stamp; + s32 delta; + + delta = now - tcp_sk(sk)->lsndtime; /* We were application limited (idle) for a while. * Shift epoch_start to keep cwnd growth to cubic curve. */ - if (ca->epoch_start && delta > 0) + if (ca->epoch_start && delta > 0) { ca->epoch_start += delta; + if (after(ca->epoch_start, now)) + ca->epoch_start = now; + } return; } } -- cgit v1.1 From d4f6e1b2a0e5d4d430f51343c3c3e901900ea315 Mon Sep 17 00:00:00 2001 From: Caio Schnepper Date: Mon, 8 Feb 2016 14:36:30 -0200 Subject: i9100: Regenerate defconfig Change-Id: Id74edd83db7c9196546f2e38f40370bc33409b3b --- arch/arm/configs/cyanogenmod_i9100_defconfig | 58 ++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index 1fe48b3..729a465 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -86,12 +86,14 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set # CONFIG_SCHED_AUTOGROUP is not set +CONFIG_MM_OWNER=y # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -144,6 +146,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set CONFIG_PROFILING=y +# CONFIG_OPROFILE is not set CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -452,12 +455,15 @@ CONFIG_TARGET_LOCALE_EUR=y # CONFIG_GC1_00_BD is not set # CONFIG_T0_00_BD is not set # CONFIG_T0_04_BD is not set +# CONFIG_KONA_00_BD is not set +# CONFIG_KONA_01_BD is not set # CONFIG_IRON_BD is not set # CONFIG_GRANDE_BD is not set # CONFIG_WRITEBACK_ENABLED is not set # CONFIG_EXYNOS_SOUND_PLATFORM_DATA is not set # CONFIG_JACK_FET is not set # CONFIG_JACK_GROUND_DET is not set +# CONFIG_USE_ADC_DET is not set CONFIG_SAMSUNG_ANALOG_UART_SWITCH=1 # CONFIG_EXYNOS5_DEV_BTS is not set @@ -508,6 +514,7 @@ CONFIG_SEC_LOG=y CONFIG_SEC_LOG_NONCACHED=y CONFIG_SEC_LOG_LAST_KMSG=y CONFIG_EHCI_IRQ_DISTRIBUTION=y +CONFIG_EHCI_MODEM_PORTNUM=2 # # Samsung Modem Feature @@ -531,6 +538,11 @@ CONFIG_SEC_MODEM_U1=y # CONFIG_SEC_MODEM_P8LTE is not set # CONFIG_SEC_MODEM_T0_TD_DUAL is not set # CONFIG_SEC_MODEM_U1_SPR is not set + +# +# Connectivity Feature +# +# CONFIG_GPS_BRCM_475X is not set # CONFIG_BT_CSR8811 is not set CONFIG_BT_BCM4330=y # CONFIG_BT_BCM4334 is not set @@ -549,6 +561,7 @@ CONFIG_BT_MGMT=y CONFIG_USB_CDFS_SUPPORT=y CONFIG_SAMSUNG_PRODUCT_SHIP=y # CONFIG_CORESIGHT_ETM is not set +# CONFIG_MACH_KONA_SENSOR is not set # # Processor Type @@ -763,6 +776,7 @@ CONFIG_EARLYSUSPEND=y # CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set # CONFIG_CONSOLE_EARLYSUSPEND is not set CONFIG_FB_EARLYSUSPEND=y +# CONFIG_HIBERNATION is not set CONFIG_PM_SLEEP=y CONFIG_PM_SLEEP_SMP=y CONFIG_PM_RUNTIME=y @@ -773,6 +787,7 @@ CONFIG_ARCH_HAS_OPP=y CONFIG_PM_OPP=y CONFIG_PM_RUNTIME_CLK=y # CONFIG_SUSPEND_TIME is not set +CONFIG_CPU_PM=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -1173,6 +1188,14 @@ CONFIG_RFKILL_PM=y # # Device Drivers # +CONFIG_MALI400=y +CONFIG_MALI_VER_R3P2=y +# CONFIG_MALI400_DEBUG is not set +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_DVFS=y +CONFIG_MALI400_UMP=y +# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_UMP_DEBUG is not set # # Generic Driver Options @@ -1253,6 +1276,7 @@ CONFIG_PN544=y # CONFIG_STMPE811_ADC is not set # CONFIG_MPU_SENSORS_MPU3050 is not set # CONFIG_MPU_SENSORS_MPU6050 is not set +CONFIG_UID_CPUTIME=y # CONFIG_C2PORT is not set # @@ -1375,6 +1399,8 @@ CONFIG_WIFI_CONTROL_FUNC=y CONFIG_BCM4330=m # CONFIG_BCM4334 is not set # CONFIG_BCM4335 is not set +# CONFIG_BCM4339 is not set +# CONFIG_BCM4354 is not set # CONFIG_BCM43241 is not set CONFIG_BROADCOM_WIFI=y CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" @@ -1465,6 +1491,7 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_SENSORS_HALL is not set CONFIG_KEYBOARD_CYPRESS_TOUCH=y # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set @@ -1521,8 +1548,12 @@ CONFIG_TOUCHSCREEN_ATMEL_MXT224_U1=y # CONFIG_TOUCHSCREEN_MXT1386 is not set # CONFIG_TOUCHSCREEN_MXT768E is not set # CONFIG_TOUCHSCREEN_SYNAPTICS_S7301 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYS is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_WORKAROUND is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYLED is not set # CONFIG_TOUCHSCREEN_CYTTSP4 is not set # CONFIG_SEC_TOUCHSCREEN_DVFS_LOCK is not set +# CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH is not set # CONFIG_KEYPAD_MELFAS_TOUCH is not set # CONFIG_TOUCHSCREEN_ATMEL_MXT540S is not set # CONFIG_INPUT_WACOM is not set @@ -1760,6 +1791,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_MAX17043_FUELGAUGE is not set # CONFIG_BATTERY_MAX17042_FUELGAUGE is not set # CONFIG_BATTERY_MAX17047_FUELGAUGE is not set +# CONFIG_BATTERY_MAX17047_C_FUELGAUGE is not set # CONFIG_BATTERY_SMB136_CHARGER is not set # CONFIG_BATTERY_SAMSUNG_P1X is not set # CONFIG_CHARGER_MAX8903 is not set @@ -1983,6 +2015,7 @@ CONFIG_VIDEO_S5K5BAFX=y # CONFIG_VIDEO_S5K4EA is not set # CONFIG_VIDEO_S5C73M3 is not set # CONFIG_VIDEO_SLP_S5C73M3 is not set +# CONFIG_VIDEO_SR130PC20 is not set CONFIG_VIDEO_IMPROVE_STREAMOFF=y CONFIG_CSI_C=y # CONFIG_CSI_D is not set @@ -2049,7 +2082,6 @@ CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y # CONFIG_USB_ZR364XX is not set # CONFIG_USB_STKWEBCAM is not set # CONFIG_USB_S2255 is not set -CONFIG_MALI_400MP_UMP=y CONFIG_VIDEO_SAMSUNG=y CONFIG_VIDEO_SAMSUNG_V4L2=y CONFIG_VIDEO_FIMC=y @@ -2078,6 +2110,8 @@ CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 CONFIG_VIDEO_MFC_MEM_PORT_COUNT=2 # CONFIG_VIDEO_MFC5X_DEBUG is not set +# CONFIG_VIDEO_MALI400MP is not set +# CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y # CONFIG_VIDEO_FIMG2D_DEBUG is not set CONFIG_VIDEO_FIMG2D3X=y @@ -2114,19 +2148,12 @@ CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE=y # # Graphics support # -# CONFIG_MALI_VER_BEFORE_R3P2 is not set # CONFIG_DRM is not set CONFIG_ION=y CONFIG_ION_EXYNOS=y CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=71680 # CONFIG_ION_EXYNOS_CONTIGHEAP_DEBUG is not set -CONFIG_MALI400=y -CONFIG_MALI_VER_R3P2=y -# CONFIG_MALI400_DEBUG is not set -# CONFIG_MALI400_PROFILING is not set -CONFIG_MALI_DVFS=y -CONFIG_MALI400_UMP=y -# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set @@ -2201,6 +2228,7 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_BACKLIGHT_PWM is not set # CONFIG_BACKLIGHT_ADP8860 is not set # CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set # # Display device support @@ -2662,8 +2690,10 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_LINE6_USB is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -# CONFIG_XVMALLOC is not set +CONFIG_XVMALLOC=y CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_ZRAM_FOR_ANDROID is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -2705,10 +2735,15 @@ CONFIG_SENSORS_CM3663=y # CONFIG_SENSORS_CM36651 is not set # CONFIG_SENSORS_BH1721 is not set # CONFIG_SENSORS_AL3201 is not set +# CONFIG_SENSORS_K2DH is not set CONFIG_SENSORS_K3DH=y +# CONFIG_SENSOR_K3DH_INPUTDEV is not set CONFIG_SENSORS_K3G=y # CONFIG_SENSORS_LSM330DLC is not set # CONFIG_SENSORS_LPS331 is not set +# CONFIG_SENSORS_YAS532 is not set +# CONFIG_SENSORS_YAS_ORI is not set +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=0 # CONFIG_SENSORS_SYSFS is not set # CONFIG_SENSORS_SSP is not set # CONFIG_SENSORS_SSP_LSM330 is not set @@ -2921,7 +2956,6 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y -CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y @@ -3164,6 +3198,8 @@ CONFIG_LIBCRC32C=y CONFIG_AUDIT_GENERIC=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y # CONFIG_XZ_DEC is not set # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y -- cgit v1.1 From 6c10f87559a0a6c42debf2f0c11298ca66703bb5 Mon Sep 17 00:00:00 2001 From: Caio Schnepper Date: Mon, 22 Feb 2016 15:58:57 -0300 Subject: i9100: Enable F2FS Change-Id: Ic8eb87571644fbe8ca0429e35932eae704b740b4 --- arch/arm/configs/cyanogenmod_i9100_defconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index 729a465..56c3eff 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -2858,6 +2858,12 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +CONFIG_F2FS_FS=y +# CONFIG_F2FS_STAT_FS is not set +CONFIG_F2FS_FS_XATTR=y +# CONFIG_F2FS_FS_POSIX_ACL is not set +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_CHECK_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y -- cgit v1.1 From 63489bea04df98a191f4a528ceb646fbbffa36c4 Mon Sep 17 00:00:00 2001 From: Ziyan Date: Sun, 20 Dec 2015 04:31:00 +1100 Subject: staging: iio: use monotonic time since boot for event timestamps Recent Android versions are expecting a monotonic timestamp that includes the time since boot, not the time elapsed since boot while the device was awake. Change-Id: I68681be011162fe102287243f8e200b08d8c31e2 --- drivers/staging/iio/iio.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h index 38f1425..705dc66 100644 --- a/drivers/staging/iio/iio.h +++ b/drivers/staging/iio/iio.h @@ -181,14 +181,7 @@ int __iio_add_chan_devattr(const char *postfix, **/ static inline s64 iio_get_time_ns(void) { - struct timespec ts; - /* - * calls getnstimeofday. - * If hrtimers then up to ns accurate, if not microsecond. - */ - ktime_get_real_ts(&ts); - - return timespec_to_ns(&ts); + return ktime_to_ns(ktime_get_boottime()); } /* Device operating modes */ -- cgit v1.1 From 873c3ec472cea798469b2bed2df4730f62d07022 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Fri, 19 Feb 2016 22:37:57 +1100 Subject: cpufreq: pegasusq: add support for setting a boost freq/cpulock Currently, the pegasusq cpufreq driver has no way of allowing a userspace component (such as a powerhal) to indicate that the cpu should be brought to a higher clockspeed in anticipation of user input (such as on POWER_HINT_INTERACTION). This commit adds three interfaces to pegasusq, boost_freq and boost_mincpus, and boost_lock_time. boost_freq sets the minimum frequency for the cpus held online, and boost_mincpus specifies the minimum number of cpus to hold online. boost_lock_time is measured in nanoseconds, and is the amount of time to wait before restoring normal control. boost_mincpus and boost_freq will not have an effect until boost_lock_time != 0. Reading from boost_lock_time will return 1 if there is currently a boost, and 0 if there is not a boost. Writing 0 will cancel any current boost, and writing a non-zero number will cancel any current boost and begin boosting for that amount of time. Writing -1 will indefinitely boost until another value is written. This feature is hidden behind the CPU_FREQ_GOV_PEGASUSQ_BOOST Kconfig flag. Change-Id: I6e81dd3ec3af5d1408b99e136e5a63789b79dfbe --- drivers/cpufreq/Kconfig | 5 ++ drivers/cpufreq/cpufreq_pegasusq.c | 168 ++++++++++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 8112af3..578f807 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -266,6 +266,11 @@ config CPU_FREQ_GOV_ADAPTIVE config CPU_FREQ_GOV_PEGASUSQ tristate "'pegasusq' cpufreq policy governor" +config CPU_FREQ_GOV_PEGASUSQ_BOOST + bool "pegasusq - enable suport for userspace-controlled cpu boosts" + depends on CPU_FREQ_GOV_PEGASUSQ + default n + config CPU_FREQ_GOV_SLP tristate "'slp' cpufreq policy governor" diff --git a/drivers/cpufreq/cpufreq_pegasusq.c b/drivers/cpufreq/cpufreq_pegasusq.c index c44af54..107b4e5 100644 --- a/drivers/cpufreq/cpufreq_pegasusq.c +++ b/drivers/cpufreq/cpufreq_pegasusq.c @@ -194,6 +194,27 @@ static int hotplug_freq[4][2] = { }; #endif +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST +static unsigned int is_boosting = 0; +static struct hrtimer boost_timer; +static DEFINE_MUTEX(boost_mutex); + +static void finish_boost_do_work(struct work_struct *work) { + mutex_lock(&boost_mutex); + is_boosting = 0; + boost_timer.function = NULL; + mutex_unlock(&boost_mutex); + printk(KERN_DEBUG "[boost] ended boost\n"); +} + +static DECLARE_WORK(finish_boost_work, finish_boost_do_work); + +static enum hrtimer_restart end_boost(struct hrtimer *timer) { + schedule_work(&finish_boost_work); + return HRTIMER_NORESTART; +} +#endif + static unsigned int min_sampling_rate; static void do_dbs_timer(struct work_struct *work); @@ -262,6 +283,10 @@ static struct dbs_tuners { unsigned int dvfs_debug; unsigned int max_freq; unsigned int min_freq; +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + unsigned int boost_freq; + unsigned int boost_mincpus; +#endif #ifdef CONFIG_HAS_EARLYSUSPEND int early_suspend; #endif @@ -485,6 +510,17 @@ show_one(up_nr_cpus, up_nr_cpus); show_one(max_cpu_lock, max_cpu_lock); show_one(min_cpu_lock, min_cpu_lock); show_one(dvfs_debug, dvfs_debug); +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST +show_one(boost_freq, boost_freq); +show_one(boost_mincpus, boost_mincpus); + +static ssize_t show_boost_lock_time(struct kobject *kobj, + struct attribute *attr, char *buf) { + return sprintf(buf, "%d\n", is_boosting); +} + +#endif + static ssize_t show_hotplug_lock(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -496,7 +532,6 @@ static ssize_t show_cpucore_table(struct kobject *kobj, { ssize_t count = 0; int i; - for (i = CONFIG_NR_CPUS; i > 0; i--) { count += sprintf(&buf[count], "%d ", i); } @@ -819,6 +854,72 @@ static ssize_t store_dvfs_debug(struct kobject *a, struct attribute *b, return count; } +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST +static ssize_t store_boost_freq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.boost_freq = input; + return count; +} + +static ssize_t store_boost_mincpus(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.boost_mincpus = min(input, 4u); + return count; +} +static ssize_t store_boost_lock_time(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + long input; + int ret; + ktime_t time; + ret = sscanf(buf, "%ld", &input); + if (ret != 1 || input < -1) + return -EINVAL; + mutex_lock(&boost_mutex); + if (input != 0) { + if (boost_timer.function != NULL) { + // ensure last boost is cancelled + hrtimer_cancel(&boost_timer); + } + is_boosting = 1; + if (input != -1) { + time = ktime_set(0, (unsigned long)input); + hrtimer_init(&boost_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + boost_timer.function = &end_boost; + printk(KERN_DEBUG "[boost] starting boost lock: %lu\n", input); + hrtimer_start(&boost_timer, time, HRTIMER_MODE_REL); + } + } else { + printk(KERN_DEBUG "[boost] cancelling boost lock"); + if (boost_timer.function != NULL) { + hrtimer_cancel(&boost_timer); + } + is_boosting = 0; + } + mutex_unlock(&boost_mutex); + + + return count; +} +#endif + + define_one_global_rw(sampling_rate); define_one_global_rw(io_is_busy); define_one_global_rw(up_threshold); @@ -836,6 +937,11 @@ define_one_global_rw(min_cpu_lock); define_one_global_rw(hotplug_lock); define_one_global_rw(dvfs_debug); define_one_global_ro(cpucore_table); +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST +define_one_global_rw(boost_freq); +define_one_global_rw(boost_mincpus); +define_one_global_rw(boost_lock_time); +#endif static struct attribute *dbs_attributes[] = { &sampling_rate_min.attr, @@ -870,6 +976,11 @@ static struct attribute *dbs_attributes[] = { &hotplug_rq_3_1.attr, &hotplug_rq_4_0.attr, &cpucore_table.attr, +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + &boost_freq.attr, + &boost_mincpus.attr, + &boost_lock_time.attr, +#endif NULL }; @@ -886,6 +997,9 @@ static void cpu_up_work(struct work_struct *work) int online = num_online_cpus(); int nr_up = dbs_tuners_ins.up_nr_cpus; int min_cpu_lock = dbs_tuners_ins.min_cpu_lock; +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + int boost_mincpus = dbs_tuners_ins.boost_mincpus; +#endif int hotplug_lock = atomic_read(&g_hotplug_lock); if (hotplug_lock && min_cpu_lock) @@ -894,6 +1008,12 @@ static void cpu_up_work(struct work_struct *work) nr_up = hotplug_lock - online; else if (min_cpu_lock) nr_up = max(nr_up, min_cpu_lock - online); +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + if (is_boosting && boost_mincpus) { + printk(KERN_DEBUG "[PEGASUSQ_BOOST] boost mincpus to %d", boost_mincpus); + nr_up = max(nr_up, boost_mincpus - online); + } +#endif if (online == 1) { printk(KERN_ERR "CPU_UP 3\n"); @@ -921,6 +1041,11 @@ static void cpu_down_work(struct work_struct *work) if (hotplug_lock) nr_down = online - hotplug_lock; +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + if (is_boosting && dbs_tuners_ins.boost_mincpus) + nr_down = min(nr_down, online - (int)dbs_tuners_ins.boost_mincpus); +#endif + for_each_online_cpu(cpu) { if (cpu == 0) continue; @@ -988,6 +1113,12 @@ static int check_up(void) && online < dbs_tuners_ins.min_cpu_lock) return 1; +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + if (is_boosting && dbs_tuners_ins.boost_mincpus != 0 + && online < dbs_tuners_ins.boost_mincpus) + return 1; +#endif + if (num_hist == 0 || num_hist % up_rate) return 0; @@ -1033,6 +1164,14 @@ static int check_down(void) down_freq = hotplug_freq[online - 1][HOTPLUG_DOWN_INDEX]; down_rq = hotplug_rq[online - 1][HOTPLUG_DOWN_INDEX]; +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + /* don't bother trying to turn off cpu if we're not done boosting yet, + * but allow turning off cpus above minimum */ + if (is_boosting && dbs_tuners_ins.boost_mincpus != 0 + && online <= dbs_tuners_ins.boost_mincpus) + return 0; +#endif + if (online == 1) return 0; @@ -1169,6 +1308,21 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) up_threshold = UP_THRESHOLD_AT_MIN_FREQ; } +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + if (is_boosting && policy->cur < dbs_tuners_ins.boost_freq) { + printk(KERN_DEBUG "[PEGASUSQ_BOOST] boosting to %d\n", dbs_tuners_ins.boost_freq); + /* disallow boosting beyond max freq */ + int target = min(policy->max, dbs_tuners_ins.boost_freq); + dbs_tuners_ins.boost_freq = target; + /* If switching to max speed, apply sampling_down_factor */ + if (policy->cur < policy->max && target == policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + dbs_freq_increase(policy, target); + return; + } +#endif + if (max_load_freq > up_threshold * policy->cur) { int inc = (policy->max * dbs_tuners_ins.freq_step) / 100; int target = min(policy->max, policy->cur + inc); @@ -1187,6 +1341,12 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) return; #endif +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + /* don't bother trying to downclock below boost_freq */ + if (is_boosting && policy->cur <= dbs_tuners_ins.boost_freq) + return; +#endif + /* * The optimal frequency is the frequency that is the lowest that * can support the current CPU usage without triggering the up @@ -1217,6 +1377,12 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) && (max_load_freq / freq_next) > down_thres) freq_next = FREQ_FOR_RESPONSIVENESS; +#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST + if (is_boosting) + freq_next = max(freq_next, dbs_tuners_ins.boost_freq); + +#endif + if (policy->cur == freq_next) return; -- cgit v1.1 From ba3777098efb278e72b0dc8155a70f2dd625c9fa Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Sat, 27 Feb 2016 20:32:56 +1100 Subject: cpufreq: pegasusq: boost: tone down the logspam Change-Id: I945ec4bb014c9488b14b5a73fd86a22eb4a3a8c2 --- drivers/cpufreq/cpufreq_pegasusq.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_pegasusq.c b/drivers/cpufreq/cpufreq_pegasusq.c index 107b4e5..716745a 100644 --- a/drivers/cpufreq/cpufreq_pegasusq.c +++ b/drivers/cpufreq/cpufreq_pegasusq.c @@ -204,7 +204,6 @@ static void finish_boost_do_work(struct work_struct *work) { is_boosting = 0; boost_timer.function = NULL; mutex_unlock(&boost_mutex); - printk(KERN_DEBUG "[boost] ended boost\n"); } static DECLARE_WORK(finish_boost_work, finish_boost_do_work); @@ -906,7 +905,6 @@ static ssize_t store_boost_lock_time(struct kobject *a, struct attribute *b, hrtimer_start(&boost_timer, time, HRTIMER_MODE_REL); } } else { - printk(KERN_DEBUG "[boost] cancelling boost lock"); if (boost_timer.function != NULL) { hrtimer_cancel(&boost_timer); } @@ -1010,7 +1008,6 @@ static void cpu_up_work(struct work_struct *work) nr_up = max(nr_up, min_cpu_lock - online); #ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST if (is_boosting && boost_mincpus) { - printk(KERN_DEBUG "[PEGASUSQ_BOOST] boost mincpus to %d", boost_mincpus); nr_up = max(nr_up, boost_mincpus - online); } #endif @@ -1310,7 +1307,6 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) #ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST if (is_boosting && policy->cur < dbs_tuners_ins.boost_freq) { - printk(KERN_DEBUG "[PEGASUSQ_BOOST] boosting to %d\n", dbs_tuners_ins.boost_freq); /* disallow boosting beyond max freq */ int target = min(policy->max, dbs_tuners_ins.boost_freq); dbs_tuners_ins.boost_freq = target; -- cgit v1.1 From 1a4ef9f546b960f440b3c45ce4e2809586780a08 Mon Sep 17 00:00:00 2001 From: Lanchon Date: Tue, 19 Jan 2016 05:05:07 -0300 Subject: Enable TRIM on the Galaxy S2 family WARNING!!! DO NOT MERGE THIS COMMIT WITHOUT VERIFYING THAT ALL THE REQUIRED PATCHES ARE APPLIED TO YOUR KERNEL. FAILURE TO HEED THIS WARNING MAY RESULT IN UNRECOVERABLE DEVICE BRICKS. For more information, please see: http://forum.xda-developers.com/galaxy-s2/development-derivatives/rom-brickbug-aftermath-speeding-t2843238 Change-Id: I193d5117ca63106265d6aac64c265b5c214612c1 (cherry picked from commit b784e843520149f55316bfc012338563dab3bbff) --- drivers/mmc/host/mshci.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mshci.c b/drivers/mmc/host/mshci.c index f93a7eb..6ce9987 100644 --- a/drivers/mmc/host/mshci.c +++ b/drivers/mmc/host/mshci.c @@ -2046,7 +2046,17 @@ int mshci_add_host(struct mshci_host *host) mmc->f_min = 400000; mmc->f_max = host->max_clk; #ifdef CONFIG_MACH_U1 - mmc->caps |= MMC_CAP_SDIO_IRQ; + /* + * BrickbugAftermath: + * Revert suppression of ERASE/TRIM/DISCARD eMMC commands. + * + * Current kernel bugfix status: + * -Brickbug: has fix + * -MAG2GA TRIM bug: has fix + * -Wear Leveling bug: HAS NO FIX + */ + /* mmc->caps |= MMC_CAP_SDIO_IRQ; */ + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; #else mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE; #endif -- cgit v1.1 From d41fe22d5df6bbb9645ff519bfadcd56305af7a1 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Wed, 2 Mar 2016 22:13:20 +1100 Subject: cpufreq: pegasusq: boost: don't NULL timer function Change-Id: Ibe8f0327f933ddf25a1ad0522293def0cdaaf515 --- drivers/cpufreq/cpufreq_pegasusq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_pegasusq.c b/drivers/cpufreq/cpufreq_pegasusq.c index 716745a..db79738 100644 --- a/drivers/cpufreq/cpufreq_pegasusq.c +++ b/drivers/cpufreq/cpufreq_pegasusq.c @@ -202,7 +202,6 @@ static DEFINE_MUTEX(boost_mutex); static void finish_boost_do_work(struct work_struct *work) { mutex_lock(&boost_mutex); is_boosting = 0; - boost_timer.function = NULL; mutex_unlock(&boost_mutex); } -- cgit v1.1 From 8cbabd4e53fcdf49076bf938fa7052a78fd8342b Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Wed, 2 Mar 2016 22:14:55 +1100 Subject: cpufreq: pegasusq: boost: drop more debugging Change-Id: I607c3a8a70857bad2d3f13d666f987ae2c22329f --- drivers/cpufreq/cpufreq_pegasusq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_pegasusq.c b/drivers/cpufreq/cpufreq_pegasusq.c index db79738..a5439d8 100644 --- a/drivers/cpufreq/cpufreq_pegasusq.c +++ b/drivers/cpufreq/cpufreq_pegasusq.c @@ -900,7 +900,6 @@ static ssize_t store_boost_lock_time(struct kobject *a, struct attribute *b, time = ktime_set(0, (unsigned long)input); hrtimer_init(&boost_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); boost_timer.function = &end_boost; - printk(KERN_DEBUG "[boost] starting boost lock: %lu\n", input); hrtimer_start(&boost_timer, time, HRTIMER_MODE_REL); } } else { -- cgit v1.1 From b3d06ad4cc7b328552ebe028f6eba535b9c0ab15 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Thu, 3 Mar 2016 19:44:58 +1100 Subject: sensor: lsm330dlc: allow exposing in /dev/input don't make /dev/accelerometer mutually exclusive with /dev/input, though Change-Id: I576bb751bb1c590af97b1c60b6a1ca94a9539b81 --- drivers/sensor/Kconfig | 10 ++++++++++ drivers/sensor/lsm330dlc_accel.c | 15 ++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 36464eb..fddd251 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -155,6 +155,16 @@ config SENSORS_LSM330DLC help Driver for STMicro LSM330DLC +config SENSORS_LSM330DLC_USE_INPUT_DEV + depends on SENSORS_LSM330DLC + bool "LSM330DLC - expose device in /dev/input" + default n + help + Samsung blob sensor HALs expect LSM330DLC to be exposed via /dev/accelerometer + Writing a custom sensor HAL is probably easier if the accelerometer is exposed + in the same place as the rest of the sensors. Note that enabling this will not + disable /dev/accelerometer + config SENSORS_LPS331 tristate "STMicro LPS331 driver" default n diff --git a/drivers/sensor/lsm330dlc_accel.c b/drivers/sensor/lsm330dlc_accel.c index 430ef85..f25a883 100644 --- a/drivers/sensor/lsm330dlc_accel.c +++ b/drivers/sensor/lsm330dlc_accel.c @@ -42,7 +42,11 @@ #undef DEBUG_ODR #undef DEBUG_REACTIVE_ALERT /* It will be used, when google fusion is enabled. */ +#ifdef CONFIG_SENSORS_LSM330DLC_USE_INPUT_DEV +#define USES_INPUT_DEV 1 +#else #undef USES_INPUT_DEV +#endif #define VENDOR "STM" #define CHIP_ID "LSM330" @@ -1130,7 +1134,7 @@ probe_retry: pr_err("%s: could not create sysfs group\n", __func__); goto err_create_sysfs; } -#else +#endif /* sensor HAL expects to find /dev/accelerometer */ data->lsm330dlc_accel_device.minor = MISC_DYNAMIC_MINOR; data->lsm330dlc_accel_device.name = ACC_DEV_NAME; @@ -1141,7 +1145,6 @@ probe_retry: pr_err("%s: misc_register failed\n", __FILE__); goto err_misc_register; } -#endif #ifdef USES_MOVEMENT_RECOGNITION data->movement_recog_flag = OFF; @@ -1315,11 +1318,10 @@ err_create_sysfs: input_unregister_device(data->input_dev); err_input_allocate: destroy_workqueue(data->work_queue); -err_create_workqueue: -#else +#endif misc_deregister(&data->lsm330dlc_accel_device); err_misc_register: -#endif +err_create_workqueue: mutex_destroy(&data->read_lock); mutex_destroy(&data->write_lock); err_read_reg: @@ -1373,9 +1375,8 @@ static int lsm330dlc_accel_remove(struct i2c_client *client) sysfs_remove_group(&data->input_dev->dev.kobj, &lsm330dlc_attribute_group); input_unregister_device(data->input_dev); -#else - misc_deregister(&data->lsm330dlc_accel_device); #endif + misc_deregister(&data->lsm330dlc_accel_device); mutex_destroy(&data->read_lock); mutex_destroy(&data->write_lock); kfree(data); -- cgit v1.1 From 763470dfb6eea0015338b556807e5cb3c2b346c7 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Thu, 3 Mar 2016 19:47:19 +1100 Subject: i9300: enable lsm330dlc input interface Change-Id: I17cb6159c241772e4da50f233da34eb48a241c96 --- arch/arm/configs/cyanogenmod_i9300_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 69204ce..480c5d7 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2923,6 +2923,7 @@ CONFIG_SENSORS_CM36651=y # CONFIG_SENSORS_K3DH is not set # CONFIG_SENSORS_K3G is not set CONFIG_SENSORS_LSM330DLC=y +CONFIG_SENSORS_LSM330DLC_USE_INPUT_DEV=y CONFIG_SENSORS_LPS331=y # CONFIG_SENSORS_YAS532 is not set # CONFIG_SENSORS_YAS_ORI is not set -- cgit v1.1 From 9cc07d95d9d1d6f3593b33481ac5d803938515e6 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Sat, 27 Feb 2016 18:13:15 +1100 Subject: i9300: enable pegasusq boosting Change-Id: Ie8ad0c5aa13d8af306c4d77a49f85c786b5454e5 --- arch/arm/configs/cyanogenmod_i9300_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 480c5d7..197d6d9 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -748,6 +748,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST=y # CONFIG_CPU_FREQ_GOV_SLP is not set # CONFIG_CPU_FREQ_DVFS_MONITOR is not set CONFIG_CPU_IDLE=y -- cgit v1.1 From 1765c98fb7b0e24527baa3dbaf1fa4cdf5905232 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Sun, 6 Mar 2016 21:45:21 +1100 Subject: i9300: disable CMA for MFC Change-Id: Ic5e9ac846259583f5e6be540b760d97b8d214709 --- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 197d6d9..118ba8c 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2172,7 +2172,7 @@ CONFIG_LSI_HDMI_AUDIO_CH_EVENT=y CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 # CONFIG_VIDEO_MFC5X_DEBUG is not set -CONFIG_USE_MFC_CMA=y +# CONFIG_USE_MFC_CMA is not set # CONFIG_VIDEO_MALI400MP is not set # CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y -- cgit v1.1 From ee1933dac461167c4673b054b5ab0199c616abb5 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 5 Mar 2016 04:43:20 +0530 Subject: crypto: testmgr - add empty test vectors for null ciphers Without these, kernel log shows: [ 5.984881] alg: No test for cipher_null (cipher_null-generic) [ 5.985096] alg: No test for ecb(cipher_null) (ecb-cipher_null) [ 5.985170] alg: No test for compress_null (compress_null-generic) [ 5.985297] alg: No test for digest_null (digest_null-generic) Change-Id: I78448a5a39617212489aa14227569a2254811e14 Signed-off-by: Jussi Kivilinna Signed-off-by: Herbert Xu --- crypto/testmgr.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 3a914c7..5087af9 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1735,6 +1735,9 @@ static const struct alg_test_desc alg_test_descs[] = { } } }, { + .alg = "compress_null", + .test = alg_test_null, + }, { .alg = "crc32c", .test = alg_test_crc32c, .fips_allowed = 1, @@ -1815,6 +1818,9 @@ static const struct alg_test_desc alg_test_descs[] = { } } }, { + .alg = "digest_null", + .test = alg_test_null, + }, { .alg = "ecb(__aes-aesni)", .test = alg_test_null, .suite = { @@ -1936,6 +1942,9 @@ static const struct alg_test_desc alg_test_descs[] = { } } }, { + .alg = "ecb(cipher_null)", + .test = alg_test_null, + }, { .alg = "ecb(des)", .test = alg_test_skcipher, .fips_allowed = 1, -- cgit v1.1 From 0ec591e5aa7c6b2339bcbc5580ab1b5a4c8fc493 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 7 Mar 2016 04:28:55 -0800 Subject: Revert "i9300: disable CMA for MFC" * this actually makes everything worse This reverts commit 1765c98fb7b0e24527baa3dbaf1fa4cdf5905232. Change-Id: Idc4d0d888cf29da191bf4b8423a45c1fda81f7b0 --- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 118ba8c..197d6d9 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2172,7 +2172,7 @@ CONFIG_LSI_HDMI_AUDIO_CH_EVENT=y CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 # CONFIG_VIDEO_MFC5X_DEBUG is not set -# CONFIG_USE_MFC_CMA is not set +CONFIG_USE_MFC_CMA=y # CONFIG_VIDEO_MALI400MP is not set # CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y -- cgit v1.1 From cfdcd7ff3e3acc77288c0573640085e9af708078 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Dec 2015 12:34:00 +1100 Subject: KEYS: Fix race between read and revoke This fixes CVE-2015-7550. There's a race between keyctl_read() and keyctl_revoke(). If the revoke happens between keyctl_read() checking the validity of a key and the key's semaphore being taken, then the key type read method will see a revoked key. This causes a problem for the user-defined key type because it assumes in its read method that there will always be a payload in a non-revoked key and doesn't check for a NULL pointer. Fix this by making keyctl_read() check the validity of a key after taking semaphore instead of before. I think the bug was introduced with the original keyrings code. This was discovered by a multithreaded test program generated by syzkaller (http://github.com/google/syzkaller). Here's a cleaned up version: #include #include #include void *thr0(void *arg) { key_serial_t key = (unsigned long)arg; keyctl_revoke(key); return 0; } void *thr1(void *arg) { key_serial_t key = (unsigned long)arg; char buffer[16]; keyctl_read(key, buffer, 16); return 0; } int main() { key_serial_t key = add_key("user", "%", "foo", 3, KEY_SPEC_USER_KEYRING); pthread_t th[5]; pthread_create(&th[0], 0, thr0, (void *)(unsigned long)key); pthread_create(&th[1], 0, thr1, (void *)(unsigned long)key); pthread_create(&th[2], 0, thr0, (void *)(unsigned long)key); pthread_create(&th[3], 0, thr1, (void *)(unsigned long)key); pthread_join(th[0], 0); pthread_join(th[1], 0); pthread_join(th[2], 0); pthread_join(th[3], 0); return 0; } Build as: cc -o keyctl-race keyctl-race.c -lkeyutils -lpthread Run as: while keyctl-race; do :; done as it may need several iterations to crash the kernel. The crash can be summarised as: BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 IP: [] user_read+0x56/0xa3 ... Call Trace: [] keyctl_read_key+0xb6/0xd7 [] SyS_keyctl+0x83/0xe0 [] entry_SYSCALL_64_fastpath+0x12/0x6f Change-Id: Id6e3b200377ca71c1dd4c0679de33b7165eb2c56 Reported-by: Dmitry Vyukov Signed-off-by: David Howells Tested-by: Dmitry Vyukov Cc: stable@vger.kernel.org Signed-off-by: James Morris --- security/keys/keyctl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 9f9cc3a..36c5578 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -688,16 +688,16 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) /* the key is probably readable - now try to read it */ can_read_key: - ret = key_validate(key); - if (ret == 0) { - ret = -EOPNOTSUPP; - if (key->type->read) { - /* read the data with the semaphore held (since we - * might sleep) */ - down_read(&key->sem); + ret = -EOPNOTSUPP; + if (key->type->read) { + /* Read the data with the semaphore held (since we might sleep) + * to protect against the key being updated or revoked. + */ + down_read(&key->sem); + ret = key_validate(key); + if (ret == 0) ret = key->type->read(key, buffer, buflen); - up_read(&key->sem); - } + up_read(&key->sem); } error2: -- cgit v1.1 From 3c5c3034e3057270ef59a895ceba0a6c10c08fa2 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Thu, 10 Mar 2016 21:24:37 +1100 Subject: i9300: ZRAM_FOR_ANDROID=y derp Change-Id: I565a4467ae321359e5b1ba38741cf109a45f0f60 --- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 197d6d9..e1ace9c 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2878,7 +2878,7 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_XVMALLOC=y CONFIG_ZRAM=y # CONFIG_ZRAM_DEBUG is not set -# CONFIG_ZRAM_FOR_ANDROID is not set +CONFIG_ZRAM_FOR_ANDROID=y # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set -- cgit v1.1 From 0280906bb6e2548b41cb6788e3ec62feb1749db8 Mon Sep 17 00:00:00 2001 From: Vasily Kulikov Date: Fri, 8 Jan 2016 11:19:14 -0500 Subject: include/linux/poison.h: fix LIST_POISON{1,2} offset Poison pointer values should be small enough to find a room in non-mmap'able/hardly-mmap'able space. E.g. on x86 "poison pointer space" is located starting from 0x0. Given unprivileged users cannot mmap anything below mmap_min_addr, it should be safe to use poison pointers lower than mmap_min_addr. The current poison pointer values of LIST_POISON{1,2} might be too big for mmap_min_addr values equal or less than 1 MB (common case, e.g. Ubuntu uses only 0x10000). There is little point to use such a big value given the "poison pointer space" below 1 MB is not yet exhausted. Changing it to a smaller value solves the problem for small mmap_min_addr setups. The values are suggested by Solar Designer: http://www.openwall.com/lists/oss-security/2015/05/02/6 Bug: 26186802 Change-Id: I2663f4e4d8725547c90ea14e082f10ae0cf80679 Signed-off-by: Yuan Lin --- include/linux/poison.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/poison.h b/include/linux/poison.h index 2110a81..253c9b4 100644 --- a/include/linux/poison.h +++ b/include/linux/poison.h @@ -19,8 +19,8 @@ * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ -#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) -#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) +#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) +#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA) /********** include/linux/timer.h **********/ /* -- cgit v1.1 From b152bb944d40c369236e0d642f8296fdbacabfa2 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Wed, 13 Jan 2016 16:28:49 -0500 Subject: BACKPORT: pagemap: do not leak physical addresses to non-privileged userspace (cherry pick from commit ab676b7d6fbf4b294bf198fb27ade5b0e865c7ce) As pointed by recent post[1] on exploiting DRAM physical imperfection, /proc/PID/pagemap exposes sensitive information which can be used to do attacks. This disallows anybody without CAP_SYS_ADMIN to read the pagemap. [1] http://googleprojectzero.blogspot.com/2015/03/exploiting-dram-rowhammer-bug-to-gain.html [ Eventually we might want to do anything more finegrained, but for now this is the simple model. - Linus ] Signed-off-by: Kirill A. Shutemov Acked-by: Konstantin Khlebnikov Acked-by: Andy Lutomirski Cc: Pavel Emelyanov Cc: Andrew Morton Cc: Mark Seaborn Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds Signed-off-by: Mark Salyzyn Bug: 26038811 Change-Id: Icd68075a32ef6c9be1ae00ae9cf5a68bbe7f4e4f --- fs/proc/task_mmu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 55a1f49..00e7ac4 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -862,9 +862,18 @@ out: return ret; } +static int pagemap_open(struct inode *inode, struct file *file) +{ + /* do not disclose physical addresses: attack vector */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return 0; +} + const struct file_operations proc_pagemap_operations = { .llseek = mem_lseek, /* borrow this */ .read = pagemap_read, + .open = pagemap_open, }; #endif /* CONFIG_PROC_PAGE_MONITOR */ -- cgit v1.1 From c3f6d1154c2af856d66c6367f91bcb6efceced8a Mon Sep 17 00:00:00 2001 From: dataanddreams Date: Tue, 1 Dec 2015 10:57:28 -0500 Subject: bcmdhd: Add checks for stack buffer overflows These two checks prevent exploitable buffer overflows in two scenarios. 1. Long WPS_ID_DEVICE_NAME in WPS info elements 2. Invalid SSID determined in certain scan results Bug: 25661991 Change-Id: Ie2f99897df2e4ce9fabcc03bb6091796777f95fa --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index e9dfcd0..5073913 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -1128,8 +1128,9 @@ wl_validate_wps_ie(char *wps_ie, s32 wps_ie_len, bool *pbc) 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'; + size_t namelen = MIN(subelt_len, sizeof(devname)); + memcpy(devname, subel, namelen); + devname[namelen-1] = '\0'; WL_DBG((" attr WPS_ID_DEVICE_NAME: %s (len %u)\n", devname, subelt_len)); } else if (subelt_id == WPS_ID_DEVICE_PWD_ID) { @@ -9090,6 +9091,10 @@ wl_notify_sched_scan_results(struct bcm_cfg80211 *cfg, struct net_device *ndev, * scan request in the form of cfg80211_scan_request. For timebeing, create * cfg80211_scan_request one out of the received PNO event. */ + ssid[i].ssid_len = MIN(DOT11_MAX_SSID_LEN, netinfo->pfnsubnet.SSID_len); + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, ssid[i].ssid_len); + request->n_ssids++; + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.SSID_len); ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; -- cgit v1.1 From d75110451e7c79cbe3daf0a0088331b7021e8627 Mon Sep 17 00:00:00 2001 From: dataanddreams Date: Fri, 4 Dec 2015 10:28:53 -0500 Subject: net: wireless: bcmdhd: Add checks for stack buffer overflows These two checks prevent exploitable buffer overflows in two scenarios. 1. Long WPS_ID_DEVICE_NAME in WPS info elements 2. Invalid SSID determined in certain scan results Bug: 25661991 Change-Id: I356c71b3ccda765b03a1a380c39e199c3c3e3261 Signed-off-by: Yuan Lin --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 5073913..ba000ef 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -9095,11 +9095,6 @@ wl_notify_sched_scan_results(struct bcm_cfg80211 *cfg, struct net_device *ndev, memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, ssid[i].ssid_len); request->n_ssids++; - 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; -- cgit v1.1 From 0a7a34f29e714624c5de103e82d4bb18be88b7dc Mon Sep 17 00:00:00 2001 From: Patrick Tjin Date: Wed, 9 Dec 2015 21:17:56 -0800 Subject: net: wireless: bcmdhd: check packet length for event messages Check the datalen field is less than the size of packet received from the network. BUG=25306181 BUG=25668859 Signed-off-by: Patrick Tjin Change-Id: I3b021d88a95bd7d4e6e0d745d2527d73487bcadc --- drivers/net/wireless/bcmdhd/dhd.h | 2 +- drivers/net/wireless/bcmdhd/dhd_common.c | 11 ++++++++++- drivers/net/wireless/bcmdhd/dhd_linux.c | 7 ++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index b0b2b29..c5074ab 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -651,7 +651,7 @@ 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 net_os_send_hang_message(struct net_device *dev); -extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, +extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, size_t pktlen, wl_event_msg_t *, void **data_ptr); extern void wl_event_to_host_order(wl_event_msg_t * evt); diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index ea3433e..dd6ace7 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -1190,7 +1190,7 @@ wl_show_host_event(wl_event_msg_t *event, void *event_data) #endif /* SHOW_EVENTS */ int -wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, +wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen, wl_event_msg_t *event, void **data_ptr) { /* check whether packet is a BRCM event pkt */ @@ -1211,6 +1211,9 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, return (BCME_ERROR); } + if (pktlen < sizeof(bcm_event_t)) + return (BCME_ERROR); + *data_ptr = &pvt_data[1]; event_data = *data_ptr; @@ -1220,8 +1223,14 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, 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); + if (datalen > pktlen) + return (BCME_ERROR); + evlen = datalen + sizeof(bcm_event_t); + if (evlen > pktlen) + return (BCME_ERROR); switch (type) { #ifdef PROP_TXSTATUS diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index da6b2a2..876ae18 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -679,7 +679,7 @@ 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, +static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, size_t pktlen, wl_event_msg_t *event_ptr, void **data_ptr); #if defined(SUPPORT_P2P_GO_PS) @@ -2213,6 +2213,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #else skb->mac.raw, #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */ + len - 2, &event, &data); @@ -6154,13 +6155,13 @@ dhd_get_wireless_stats(struct net_device *dev) #endif /* defined(WL_WIRELESS_EXT) */ static int -dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, +dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, size_t pktlen, wl_event_msg_t *event, void **data) { int bcmerror = 0; ASSERT(dhd != NULL); - bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data); + bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, pktlen, event, data); if (bcmerror != BCME_OK) return (bcmerror); -- cgit v1.1 From d3819b76e827ba7e4095657397e5aad2382b8724 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 10 Jan 2016 22:40:55 -0800 Subject: tty: Fix unsafe ldisc reference via ioctl(TIOCGETD) ioctl(TIOCGETD) retrieves the line discipline id directly from the ldisc because the line discipline id (c_line) in termios is untrustworthy; userspace may have set termios via ioctl(TCSETS*) without actually changing the line discipline via ioctl(TIOCSETD). However, directly accessing the current ldisc via tty->ldisc is unsafe; the ldisc ptr dereferenced may be stale if the line discipline is changing via ioctl(TIOCSETD) or hangup. Wait for the line discipline reference (just like read() or write()) to retrieve the "current" line discipline id. Change-Id: I7dae813721d4631d84a7d48b7b908e3bc40617cb Cc: Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index cf32a5c..cff688b 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2447,6 +2447,28 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) } /** + * tiocgetd - get line discipline + * @tty: tty device + * @p: pointer to user data + * + * Retrieves the line discipline id directly from the ldisc. + * + * Locking: waits for ldisc reference (in case the line discipline + * is changing or the tty is being hungup) + */ + +static int tiocgetd(struct tty_struct *tty, int __user *p) +{ + struct tty_ldisc *ld; + int ret; + + ld = tty_ldisc_ref_wait(tty); + ret = put_user(ld->ops->num, p); + tty_ldisc_deref(ld); + return ret; +} + +/** * send_break - performed time break * @tty: device to break on * @duration: timeout in mS @@ -2656,7 +2678,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: - return put_user(tty->ldisc->ops->num, (int __user *)p); + return tiocgetd(tty, p); case TIOCSETD: return tiocsetd(tty, p); case TIOCVHANGUP: -- cgit v1.1 From 9227d076f1b8d82f78410b1ecc40b77a6a75eee7 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Wed, 11 Mar 2015 14:32:24 -0700 Subject: mm: reorder can_do_mlock to fix audit denial A userspace call to mmap(MAP_LOCKED) may result in the successful locking of memory while also producing a confusing audit log denial. can_do_mlock checks capable and rlimit. If either of these return positive can_do_mlock returns true. The capable check leads to an LSM hook used by apparmour and selinux which produce the audit denial. Reordering so rlimit is checked first eliminates the denial on success, only recording a denial when the lock is unsuccessful as a result of the denial. Change-Id: I8d300365c8f85b002d6c5375a22abfb1b7579d20 Signed-off-by: Jeff Vander Stoep Acked-by: Nick Kralevich Cc: Jeff Vander Stoep Cc: Sasha Levin Cc: "Paul E. McKenney" Cc: Rik van Riel Cc: Vlastimil Babka Cc: Paul Cassella Signed-off-by: Andrew Morton --- mm/mlock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/mlock.c b/mm/mlock.c index 048260c..b41b3b7 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -23,10 +23,10 @@ int can_do_mlock(void) { - if (capable(CAP_IPC_LOCK)) - return 1; if (rlimit(RLIMIT_MEMLOCK) != 0) return 1; + if (capable(CAP_IPC_LOCK)) + return 1; return 0; } EXPORT_SYMBOL(can_do_mlock); -- cgit v1.1 From a92474e6463fcca623b5a8ebcd11a71c3ee8c445 Mon Sep 17 00:00:00 2001 From: Mark Grondona Date: Wed, 11 Sep 2013 14:24:31 -0700 Subject: __ptrace_may_access() should not deny sub-threads commit 73af963f9f3036dffed55c3a2898598186db1045 upstream. __ptrace_may_access() checks get_dumpable/ptrace_has_cap/etc if task != current, this can can lead to surprising results. For example, a sub-thread can't readlink("/proc/self/exe") if the executable is not readable. setup_new_exec()->would_dump() notices that inode_permission(MAY_READ) fails and then it does set_dumpable(suid_dumpable). After that get_dumpable() fails. (It is not clear why proc_pid_readlink() checks get_dumpable(), perhaps we could add PTRACE_MODE_NODUMPABLE) Change __ptrace_may_access() to use same_thread_group() instead of "task == current". Any security check is pointless when the tasks share the same ->mm. Change-Id: Ib6ca927a1eb0637df8030aabcb3129d5be343512 Signed-off-by: Mark Grondona Signed-off-by: Ben Woodard Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/ptrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 40581ee..8381d15 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -182,7 +182,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode) */ int dumpable = 0; /* Don't let security modules deny introspection */ - if (task == current) + if (same_thread_group(task, current)) return 0; rcu_read_lock(); tcred = __task_cred(task); -- cgit v1.1 From 07c64da538554085466e9338e19f033438a0a5bf Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Tue, 18 Sep 2012 13:38:59 -0400 Subject: ext4: make orphan functions be no-op in no-journal mode Instead of checking whether the handle is valid, we check if journal is enabled. This avoids taking the s_orphan_lock mutex in all cases when there is no journal in use, including the error paths where ext4_orphan_del() is called with a handle set to NULL. Signed-off-by: Anatol Pomozov Signed-off-by: "Theodore Ts'o" Conflicts: fs/ext4/namei.c Change-Id: I734ccb8069fceb12b864e7b9dceb37e27ab94c61 --- fs/ext4/namei.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4098064..c70e08b 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1986,7 +1986,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) struct ext4_iloc iloc; int err = 0, rc; - if (!ext4_handle_valid(handle)) + if (!EXT4_SB(sb)->s_journal) return 0; mutex_lock(&EXT4_SB(sb)->s_orphan_lock); @@ -2067,8 +2067,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) struct ext4_iloc iloc; int err = 0; - /* ext4_handle_valid() assumes a valid handle_t pointer */ - if (handle && !ext4_handle_valid(handle) && + if (!EXT4_SB(inode->i_sb)->s_journal && !(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ORPHAN_FS)) return 0; @@ -2088,7 +2087,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) * transaction handle with which to update the orphan list on * disk, but we still need to remove the inode from the linked * list in memory. */ - if (sbi->s_journal && !handle) + if (!handle) goto out; err = ext4_reserve_inode_write(handle, inode, &iloc); -- cgit v1.1 From c2710a162474f0fba1018167499c3c5b4a5e4ce7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 16 Aug 2012 18:14:14 +0100 Subject: tracing/syscalls: Fix perf syscall tracing when syscall_nr == -1 syscall_get_nr can return -1 in the case that the task is not executing a system call. This patch fixes perf_syscall_{enter,exit} to check that the syscall number is valid before using it as an index into a bitmap. Link: http://lkml.kernel.org/r/1345137254-7377-1-git-send-email-will.deacon@arm.com Change-Id: Iedc719957e184c6572b3ad94e241ae2a97a0b533 Cc: Jason Baron Cc: Wade Farnsworth Cc: Frederic Weisbecker Signed-off-by: Will Deacon Signed-off-by: Steven Rostedt --- kernel/trace/trace_syscalls.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 5819cd5..d6803a4 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -518,6 +518,8 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) int size; syscall_nr = syscall_get_nr(current, regs); + if (syscall_nr < 0) + return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) return; @@ -592,6 +594,8 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int size; syscall_nr = syscall_get_nr(current, regs); + if (syscall_nr < 0) + return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) return; -- cgit v1.1 From f14deb73188f99142fb1ffcaf564613674349989 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 29 Oct 2014 23:06:58 +0100 Subject: tracing/syscalls: Ignore numbers outside NR_syscalls' range ARM has some private syscalls (for example, set_tls(2)) which lie outside the range of NR_syscalls. If any of these are called while syscall tracing is being performed, out-of-bounds array access will occur in the ftrace and perf sys_{enter,exit} handlers. # trace-cmd record -e raw_syscalls:* true && trace-cmd report ... true-653 [000] 384.675777: sys_enter: NR 192 (0, 1000, 3, 4000022, ffffffff, 0) true-653 [000] 384.675812: sys_exit: NR 192 = 1995915264 true-653 [000] 384.675971: sys_enter: NR 983045 (76f74480, 76f74000, 76f74b28, 76f74480, 76f76f74, 1) true-653 [000] 384.675988: sys_exit: NR 983045 = 0 ... # trace-cmd record -e syscalls:* true [ 17.289329] Unable to handle kernel paging request at virtual address aaaaaace [ 17.289590] pgd = 9e71c000 [ 17.289696] [aaaaaace] *pgd=00000000 [ 17.289985] Internal error: Oops: 5 [#1] PREEMPT SMP ARM [ 17.290169] Modules linked in: [ 17.290391] CPU: 0 PID: 704 Comm: true Not tainted 3.18.0-rc2+ #21 [ 17.290585] task: 9f4dab00 ti: 9e710000 task.ti: 9e710000 [ 17.290747] PC is at ftrace_syscall_enter+0x48/0x1f8 [ 17.290866] LR is at syscall_trace_enter+0x124/0x184 Fix this by ignoring out-of-NR_syscalls-bounds syscall numbers. Commit cd0980fc8add "tracing: Check invalid syscall nr while tracing syscalls" added the check for less than zero, but it should have also checked for greater than NR_syscalls. Link: http://lkml.kernel.org/p/1414620418-29472-1-git-send-email-rabin@rab.in Fixes: cd0980fc8add "tracing: Check invalid syscall nr while tracing syscalls" Cc: stable@vger.kernel.org # 2.6.33+ Signed-off-by: Rabin Vincent Signed-off-by: Steven Rostedt Conflicts: kernel/trace/trace_syscalls.c Change-Id: I512142f8f1e1b2a8dc063209666dbce9737377e7 --- kernel/trace/trace_syscalls.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index d6803a4..2486bae 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -308,7 +308,7 @@ void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id) int pc; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_enter_syscalls)) return; @@ -348,7 +348,7 @@ void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int pc; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_exit_syscalls)) return; @@ -518,7 +518,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) int size; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) return; @@ -594,7 +594,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int size; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) return; -- cgit v1.1 From 28c6602e20af957244ebc2ce4d7dbab42ffb3f93 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 14 Jul 2014 17:02:31 -0700 Subject: net/l2tp: don't fall back on UDP [get|set]sockopt The l2tp [get|set]sockopt() code has fallen back to the UDP functions for socket option levels != SOL_PPPOL2TP since day one, but that has never actually worked, since the l2tp socket isn't an inet socket. As David Miller points out: "If we wanted this to work, it'd have to look up the tunnel and then use tunnel->sk, but I wonder how useful that would be" Since this can never have worked so nobody could possibly have depended on that functionality, just remove the broken code and return -EINVAL. Change-Id: I5fc9d8033eb368e72436ec7682adaa1ff387615a Reported-by: Sasha Levin Acked-by: James Chapman Acked-by: David Miller Cc: Phil Turnbull Cc: Vegard Nossum Cc: Willy Tarreau Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds --- net/l2tp/l2tp_ppp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 2366914..af62439 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1342,7 +1342,7 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, int err; if (level != SOL_PPPOL2TP) - return udp_prot.setsockopt(sk, level, optname, optval, optlen); + return -EINVAL; if (optlen < sizeof(int)) return -EINVAL; @@ -1468,7 +1468,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, struct pppol2tp_session *ps; if (level != SOL_PPPOL2TP) - return udp_prot.getsockopt(sk, level, optname, optval, optlen); + return -EINVAL; if (get_user(len, (int __user *) optlen)) return -EFAULT; -- cgit v1.1 From f35d99ecd31b7cea5d64434c41c6f6a52bd867f0 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 29 Dec 2014 14:39:01 -0600 Subject: KEYS: close race between key lookup and freeing When a key is being garbage collected, it's key->user would get put before the ->destroy() callback is called, where the key is removed from it's respective tracking structures. This leaves a key hanging in a semi-invalid state which leaves a window open for a different task to try an access key->user. An example is find_keyring_by_name() which would dereference key->user for a key that is in the process of being garbage collected (where key->user was freed but ->destroy() wasn't called yet - so it's still present in the linked list). This would cause either a panic, or corrupt memory. Change-Id: I7856b7012d1bb668b15977ed16a4ee5f9c2882de Signed-off-by: Sasha Levin --- security/keys/key.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/keys/key.c b/security/keys/key.c index f7f9d93..e1704f6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -639,12 +639,12 @@ found_dead_key: if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) atomic_dec(&key->user->nikeys); - key_user_put(key->user); - /* now throw away the key memory */ if (key->type->destroy) key->type->destroy(key); + key_user_put(key->user); + kfree(key->description); #ifdef KEY_DEBUGGING -- cgit v1.1 From 74d8781c8987f23bb289d7b4eceb0c2bcf47686d Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Wed, 26 Nov 2014 09:09:16 -0800 Subject: eCryptfs: Remove buggy and unnecessary write in file name decode routine Dmitry Chernenkov used KASAN to discover that eCryptfs writes past the end of the allocated buffer during encrypted filename decoding. This fix corrects the issue by getting rid of the unnecessary 0 write when the current bit offset is 2. Change-Id: Id8e04a580e550495c46cd36fec430a1ec4342940 Signed-off-by: Michael Halcrow Reported-by: Dmitry Chernenkov Suggested-by: Kees Cook Cc: stable@vger.kernel.org # v2.6.29+: 51ca58d eCryptfs: Filename Encryption: Encoding and encryption functions Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index c6602d2..dcf1fb5 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -2036,7 +2036,6 @@ ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size, break; case 2: dst[dst_byte_offset++] |= (src_byte); - dst[dst_byte_offset] = 0; current_bit_offset = 0; break; } -- cgit v1.1 From 8132aca1c841f92e8a3f1398f724251e3d68d757 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 24 Apr 2014 15:50:33 -0700 Subject: net: ipv4: current group_info should be put after using. Plug a group_info refcount leak in ping_init. group_info is only needed during initialization and the code failed to release the reference on exit. While here move grabbing the reference to a place where it is actually needed. Change-Id: I423dff6dda7ab91cdfdacb332330745739e1936d Signed-off-by: Chuansheng Liu Signed-off-by: Zhang Dongxing Signed-off-by: xiaoming wang Signed-off-by: David S. Miller --- net/ipv4/ping.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 140d420..93fbd72 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -248,26 +248,33 @@ int ping_init_sock(struct sock *sk) struct net *net = sock_net(sk); gid_t group = current_egid(); gid_t range[2]; - struct group_info *group_info = get_current_groups(); - int i, j, count = group_info->ngroups; + struct group_info *group_info; + int i, j, count; + int ret = 0; inet_get_ping_group_range_net(net, range, range+1); if (range[0] <= group && group <= range[1]) return 0; + group_info = get_current_groups(); + count = group_info->ngroups; for (i = 0; i < group_info->nblocks; i++) { int cp_count = min_t(int, NGROUPS_PER_BLOCK, count); for (j = 0; j < cp_count; j++) { group = group_info->blocks[i][j]; if (range[0] <= group && group <= range[1]) - return 0; + goto out_release_group; } count -= cp_count; } - return -EACCES; + ret = -EACCES; + +out_release_group: + put_group_info(group_info); + return ret; } EXPORT_SYMBOL_GPL(ping_init_sock); -- cgit v1.1 From 260325caf43c98bd8ca8146ffe4730017ebdab44 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 28 Jul 2014 16:26:53 -0700 Subject: mnt: Only change user settable mount flags in remount commit a6138db815df5ee542d848318e5dae681590fccd upstream. Kenton Varda discovered that by remounting a read-only bind mount read-only in a user namespace the MNT_LOCK_READONLY bit would be cleared, allowing an unprivileged user to the remount a read-only mount read-write. Correct this by replacing the mask of mount flags to preserve with a mask of mount flags that may be changed, and preserve all others. This ensures that any future bugs with this mask and remount will fail in an easy to detect way where new mount flags simply won't change. Change-Id: I8ab8bda03a14b9b43e78f1dc6c818bbec048e986 Acked-by: Serge E. Hallyn Signed-off-by: "Eric W. Biederman" Cc: Francis Moreau Signed-off-by: Zefan Li --- fs/namespace.c | 2 +- include/linux/mount.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index b3d8f51..912d273 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1842,7 +1842,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, err = do_remount_sb(sb, flags, data, 0); if (!err) { br_write_lock(vfsmount_lock); - mnt_flags |= path->mnt->mnt_flags & MNT_PROPAGATION_MASK; + mnt_flags |= path->mnt->mnt_flags & ~MNT_USER_SETTABLE_MASK; path->mnt->mnt_flags = mnt_flags; br_write_unlock(vfsmount_lock); } diff --git a/include/linux/mount.h b/include/linux/mount.h index 604f122..dacabd6 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -42,7 +42,9 @@ struct mnt_namespace; * flag, consider how it interacts with shared mounts. */ #define MNT_SHARED_MASK (MNT_UNBINDABLE) -#define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE) +#define MNT_USER_SETTABLE_MASK (MNT_NOSUID | MNT_NODEV | MNT_NOEXEC \ + | MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME \ + | MNT_READONLY) #define MNT_INTERNAL 0x4000 -- cgit v1.1 From c88f7bbd8026761a615c9969d186ffa2a1a3da3c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 15 Jan 2015 17:49:27 +0000 Subject: mnt: Prevent pivot_root from creating a loop in the mount tree Andy Lutomirski recently demonstrated that when chroot is used to set the root path below the path for the new ``root'' passed to pivot_root the pivot_root system call succeeds and leaks mounts. In examining the code I see that starting with a new root that is below the current root in the mount tree will result in a loop in the mount tree after the mounts are detached and then reattached to one another. Resulting in all kinds of ugliness including a leak of that mounts involved in the leak of the mount loop. Prevent this problem by ensuring that the new mount is reachable from the current root of the mount tree. [Added stable cc. Fixes CVE-2014-7970. --Andy] Cc: stable@vger.kernel.org Reported-by: Andy Lutomirski Reviewed-by: Andy Lutomirski Link: http://lkml.kernel.org/r/87bnpmihks.fsf@x220.int.ebiederm.org Signed-off-by: "Eric W. Biederman" Signed-off-by: Andy Lutomirski (backported from commit 0d0826019e529f21c84687521d03f60cd241ca7d) CVE-2014-7970 BugLink: http://bugs.launchpad.net/bugs/1383356 Signed-off-by: Luis Henriques Acked-by: Stefan Bader Acked-by: Andy Whitcroft Signed-off-by: Andy Whitcroft Change-Id: I0fe1d090eeb4765cc49401784e44a430f9585498 --- fs/namespace.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/namespace.c b/fs/namespace.c index 912d273..4f47629 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2618,6 +2618,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out4; } else if (!is_subdir(old.dentry, new.dentry)) goto out4; + /* make certain new is below the root */ + if (!is_path_reachable(new.mnt, new.dentry, &root)) + goto out4; br_write_lock(vfsmount_lock); detach_mnt(new.mnt, &parent_path); detach_mnt(root.mnt, &root_parent); -- cgit v1.1 From 9c6b633d3414bf49bb75a641a4f028bf0acf2254 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 28 Jan 2015 20:30:43 -0600 Subject: vfs: read file_handle only once in handle_to_path We used to read file_handle twice. Once to get the amount of extra bytes, and once to fetch the entire structure. This may be problematic since we do size verifications only after the first read, so if the number of extra bytes changes in userspace between the first and second calls, we'll have an incoherent view of file_handle. Instead, read the constant size once, and copy that over to the final structure without having to re-read it again. Change-Id: Ib05e5129629e27d5a05953098c5bc470fae40d2a Signed-off-by: Sasha Levin --- fs/fhandle.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index 6b08864..c9e18f3 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -196,8 +196,9 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, goto out_err; } /* copy the full handle */ - if (copy_from_user(handle, ufh, - sizeof(struct file_handle) + + *handle = f_handle; + if (copy_from_user(&handle->f_handle, + &ufh->f_handle, f_handle.handle_bytes)) { retval = -EFAULT; goto out_handle; -- cgit v1.1 From b6e29030f9325ccc841ad02e4687dedf6d9bba08 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Fri, 20 Dec 2013 15:10:03 +0200 Subject: mm: Fix NULL pointer dereference in madvise(MADV_WILLNEED) support Sasha Levin found a NULL pointer dereference that is due to a missing page table lock, which in turn is due to the pmd entry in question being a transparent huge-table entry. The code - introduced in commit 1998cc048901 ("mm: make madvise(MADV_WILLNEED) support swap file prefetch") - correctly checks for this situation using pmd_none_or_trans_huge_or_clear_bad(), but it turns out that that function doesn't work correctly. pmd_none_or_trans_huge_or_clear_bad() expected that pmd_bad() would trigger if the transparent hugepage bit was set, but it doesn't do that if pmd_numa() is also set. Note that the NUMA bit only gets set on real NUMA machines, so people trying to reproduce this on most normal development systems would never actually trigger this. Fix it by removing the very subtle (and subtly incorrect) expectation, and instead just checking pmd_trans_huge() explicitly. Reported-by: Sasha Levin Acked-by: Andrea Arcangeli [ Additionally remove the now stale test for pmd_trans_huge() inside the pmd_bad() case - Linus ] Signed-off-by: Linus Torvalds Change-Id: I3f3763f236ef102de735297cd175cf514d40d28f --- include/asm-generic/pgtable.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 831924a..2fda790 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -488,11 +488,10 @@ static inline int pmd_none_or_trans_huge_or_clear_bad(pmd_t *pmd) #ifdef CONFIG_TRANSPARENT_HUGEPAGE barrier(); #endif - if (pmd_none(pmdval)) + if (pmd_none(pmdval) || pmd_trans_huge(pmdval)) return 1; if (unlikely(pmd_bad(pmdval))) { - if (!pmd_trans_huge(pmdval)) - pmd_clear_bad(pmd); + pmd_clear_bad(pmd); return 1; } return 0; -- cgit v1.1 From ceb67cdf816dafbd03a31821eaaf718cb180c414 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Sun, 19 Apr 2015 02:48:39 +0200 Subject: fs: take i_mutex during prepare_binprm for set[ug]id executables This prevents a race between chown() and execve(), where chowning a setuid-user binary to root would momentarily make the binary setuid root. This patch was mostly written by Linus Torvalds. Signed-off-by: Jann Horn Signed-off-by: Linus Torvalds Conflicts: fs/exec.c Change-Id: Iecebf23d07e299689e4ba4fd74ea8821ef96e72b --- fs/exec.c | 65 +++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 9ab31ca..807400f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1267,6 +1267,45 @@ int check_unsafe_exec(struct linux_binprm *bprm) return res; } +static void bprm_fill_uid(struct linux_binprm *bprm) +{ + struct inode *inode; + unsigned int mode; + uid_t uid; + gid_t gid; + + /* clear any previous set[ug]id data from a previous binary */ + bprm->cred->euid = current_euid(); + bprm->cred->egid = current_egid(); + + if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + return; + + inode = bprm->file->f_path.dentry->d_inode; + mode = ACCESS_ONCE(inode->i_mode); + if (!(mode & (S_ISUID|S_ISGID))) + return; + + /* Be careful if suid/sgid is set */ + mutex_lock(&inode->i_mutex); + + /* reload atomically mode/uid/gid now that lock held */ + mode = inode->i_mode; + uid = inode->i_uid; + gid = inode->i_gid; + mutex_unlock(&inode->i_mutex); + + if (mode & S_ISUID) { + bprm->per_clear |= PER_CLEAR_ON_SETID; + bprm->cred->euid = uid; + } + + if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { + bprm->per_clear |= PER_CLEAR_ON_SETID; + bprm->cred->egid = gid; + } +} + /* * Fill the binprm structure from the inode. * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes @@ -1275,36 +1314,12 @@ int check_unsafe_exec(struct linux_binprm *bprm) */ int prepare_binprm(struct linux_binprm *bprm) { - umode_t mode; - struct inode * inode = bprm->file->f_path.dentry->d_inode; int retval; - mode = inode->i_mode; if (bprm->file->f_op == NULL) return -EACCES; - /* clear any previous set[ug]id data from a previous binary */ - bprm->cred->euid = current_euid(); - bprm->cred->egid = current_egid(); - - if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) { - /* Set-uid? */ - if (mode & S_ISUID) { - bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->euid = inode->i_uid; - } - - /* Set-gid? */ - /* - * If setgid is set but no group execute bit then this - * is a candidate for mandatory locking, not a setgid - * executable. - */ - if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { - bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->egid = inode->i_gid; - } - } + bprm_fill_uid(bprm); /* fill in binprm security blob */ retval = security_bprm_set_creds(bprm); -- cgit v1.1 From 14f55d438ceb4fb380151f601a78a937e1bb7e5e Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Thu, 28 May 2015 15:10:14 -0700 Subject: android: drivers: workaround debugfs race in binder If a /d/binder/proc/[pid] entry is kept open after linux has torn down the associated process, binder_proc_show can deference an invalid binder_proc that has been stashed in the debugfs inode. Validate that the binder_proc ptr passed into binder_proc_show has not been freed by looking for it within the global process list whilst the global lock is held. If the ptr is not valid, print nothing. Bug 19587483 Change-Id: I4abc6443d96cca6500608976cded5ff3d1697d33 Signed-off-by: Riley Andrews --- drivers/staging/android/binder.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index aad750b..42b23f6 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -3545,13 +3545,25 @@ static int binder_transactions_show(struct seq_file *m, void *unused) static int binder_proc_show(struct seq_file *m, void *unused) { + struct binder_proc *itr; struct binder_proc *proc = m->private; + struct hlist_node *pos; int do_lock = !binder_debug_no_lock; + bool valid_proc = false; if (do_lock) - mutex_lock(&binder_lock); - seq_puts(m, "binder proc state:\n"); - print_binder_proc(m, proc, 1); + binder_lock(__func__); + + hlist_for_each_entry(itr, pos, &binder_procs, proc_node) { + if (itr == proc) { + valid_proc = true; + break; + } + } + if (valid_proc) { + seq_puts(m, "binder proc state:\n"); + print_binder_proc(m, proc, 1); + } if (do_lock) mutex_unlock(&binder_lock); return 0; -- cgit v1.1 From 60d05551c60819bea1c8cfe1e5427a47a21bccd1 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Thu, 15 Oct 2015 12:25:00 -0500 Subject: net: add length argument to skb_copy_and_csum_datagram_iovec Without this length argument, we can read past the end of the iovec in memcpy_toiovec because we have no way of knowing the total length of the iovec's buffers. This is needed for stable kernels where 89c22d8c3b27 ("net: Fix skb csum races when peeking") has been backported but that don't have the ioviter conversion, which is almost all the stable trees <= 3.18. This also fixes a kernel crash for NFS servers when the client uses -onfsvers=3,proto=udp to mount the export. Change-Id: I1865e3d7a1faee42a5008a9ad58c4d3323ea4bab Signed-off-by: Sabrina Dubroca Reviewed-by: Hannes Frederic Sowa (cherry picked from commit c91234366e4cfd4f70c73e7d79ede92a6e462a88) --- include/linux/skbuff.h | 3 ++- net/core/datagram.c | 6 +++++- net/ipv4/tcp_input.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/raw.c | 2 +- net/ipv6/udp.c | 3 ++- net/rxrpc/ar-recvmsg.c | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f430316..cfcbb17 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1863,7 +1863,8 @@ extern int skb_copy_datagram_iovec(const struct sk_buff *from, int size); extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen, - struct iovec *iov); + struct iovec *iov, + int len); extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, const struct iovec *from, diff --git a/net/core/datagram.c b/net/core/datagram.c index 18ac112..aaf4559 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -672,6 +672,7 @@ EXPORT_SYMBOL(__skb_checksum_complete); * @skb: skbuff * @hlen: hardware length * @iov: io vector + * @len: amount of data to copy from skb to iov * * Caller _must_ check that skb will fit to this iovec. * @@ -681,11 +682,14 @@ EXPORT_SYMBOL(__skb_checksum_complete); * can be modified! */ int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, - int hlen, struct iovec *iov) + int hlen, struct iovec *iov, int len) { __wsum csum; int chunk = skb->len - hlen; + if (chunk > len) + chunk = len; + if (!chunk) return 0; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b1172b4..0343af4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5120,7 +5120,7 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen) err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk); else err = skb_copy_and_csum_datagram_iovec(skb, hlen, - tp->ucopy.iov); + tp->ucopy.iov, chunk); if (!err) { tp->ucopy.len -= chunk; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 2f00d32..0e8d4a7 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1209,7 +1209,7 @@ try_again: else { err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov); + msg->msg_iov, copied); if (err == -EINVAL) goto csum_copy_err; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 0a7eae0..40147c9 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -481,7 +481,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, goto csum_copy_err; err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); } else { - err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov); + err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov, copied); if (err == -EINVAL) goto csum_copy_err; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 823d1ef..d786b9c 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -385,7 +385,8 @@ try_again: err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,len); else { - err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov); + err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), + msg->msg_iov, copied); if (err == -EINVAL) goto csum_copy_err; } diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c index 0c65013..280b9ea 100644 --- a/net/rxrpc/ar-recvmsg.c +++ b/net/rxrpc/ar-recvmsg.c @@ -181,7 +181,8 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_iov, copy); } else { ret = skb_copy_and_csum_datagram_iovec(skb, offset, - msg->msg_iov); + msg->msg_iov, + copy); if (ret == -EINVAL) goto csum_copy_error; } -- cgit v1.1 From 18bac65c1273d0e059f9174c4e8e9c7e45a12911 Mon Sep 17 00:00:00 2001 From: Benjamin Randazzo Date: Sat, 25 Jul 2015 16:36:50 +0200 Subject: md: use kzalloc() when bitmap is disabled commit b6878d9e03043695dbf3fa1caa6dfc09db225b16 upstream. In drivers/md/md.c get_bitmap_file() uses kmalloc() for creating a mdu_bitmap_file_t called "file". 5769 file = kmalloc(sizeof(*file), GFP_NOIO); 5770 if (!file) 5771 return -ENOMEM; This structure is copied to user space at the end of the function. 5786 if (err == 0 && 5787 copy_to_user(arg, file, sizeof(*file))) 5788 err = -EFAULT But if bitmap is disabled only the first byte of "file" is initialized with zero, so it's possible to read some bytes (up to 4095) of kernel space memory from user space. This is an information leak. 5775 /* bitmap disabled, zero the first byte and copy out */ 5776 if (!mddev->bitmap_info.file) 5777 file->pathname[0] = '\0'; Change-Id: I7cd2a3c7fad2e2cb9edb8b4eff2af8a3a8f40149 Signed-off-by: Benjamin Randazzo Signed-off-by: NeilBrown [lizf: Backported to 3.4: fix both branches] Signed-off-by: Zefan Li --- drivers/md/md.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 4ef75e9..84ba7af 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5117,9 +5117,9 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg) int err = -ENOMEM; if (md_allow_write(mddev)) - file = kmalloc(sizeof(*file), GFP_NOIO); + file = kzalloc(sizeof(*file), GFP_NOIO); else - file = kmalloc(sizeof(*file), GFP_KERNEL); + file = kzalloc(sizeof(*file), GFP_KERNEL); if (!file) goto out; -- cgit v1.1 From b2332d884f24e8b74bf9d7e425e11ef5d02813ae Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Nov 2015 21:36:31 +0000 Subject: KEYS: Fix handling of stored error in a negatively instantiated user key If a user key gets negatively instantiated, an error code is cached in the payload area. A negatively instantiated key may be then be positively instantiated by updating it with valid data. However, the ->update key type method must be aware that the error code may be there. The following may be used to trigger the bug in the user key type: keyctl request2 user user "" @u keyctl add user user "a" @u which manifests itself as: BUG: unable to handle kernel paging request at 00000000ffffff8a IP: [] __call_rcu.constprop.76+0x1f/0x280 kernel/rcu/tree.c:3046 PGD 7cc30067 PUD 0 Oops: 0002 [#1] SMP Modules linked in: CPU: 3 PID: 2644 Comm: a.out Not tainted 4.3.0+ #49 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff88003ddea700 ti: ffff88003dd88000 task.ti: ffff88003dd88000 RIP: 0010:[] [] __call_rcu.constprop.76+0x1f/0x280 [] __call_rcu.constprop.76+0x1f/0x280 kernel/rcu/tree.c:3046 RSP: 0018:ffff88003dd8bdb0 EFLAGS: 00010246 RAX: 00000000ffffff82 RBX: 0000000000000000 RCX: 0000000000000001 RDX: ffffffff81e3fe40 RSI: 0000000000000000 RDI: 00000000ffffff82 RBP: ffff88003dd8bde0 R08: ffff88007d2d2da0 R09: 0000000000000000 R10: 0000000000000000 R11: ffff88003e8073c0 R12: 00000000ffffff82 R13: ffff88003dd8be68 R14: ffff88007d027600 R15: ffff88003ddea700 FS: 0000000000b92880(0063) GS:ffff88007fd00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 00000000ffffff8a CR3: 000000007cc5f000 CR4: 00000000000006e0 Stack: ffff88003dd8bdf0 ffffffff81160a8a 0000000000000000 00000000ffffff82 ffff88003dd8be68 ffff88007d027600 ffff88003dd8bdf0 ffffffff810a39e5 ffff88003dd8be20 ffffffff812a31ab ffff88007d027600 ffff88007d027620 Call Trace: [] kfree_call_rcu+0x15/0x20 kernel/rcu/tree.c:3136 [] user_update+0x8b/0xb0 security/keys/user_defined.c:129 [< inline >] __key_update security/keys/key.c:730 [] key_create_or_update+0x291/0x440 security/keys/key.c:908 [< inline >] SYSC_add_key security/keys/keyctl.c:125 [] SyS_add_key+0x101/0x1e0 security/keys/keyctl.c:60 [] entry_SYSCALL_64_fastpath+0x12/0x6a arch/x86/entry/entry_64.S:185 Note the error code (-ENOKEY) in EDX. A similar bug can be tripped by: keyctl request2 trusted user "" @u keyctl add trusted user "a" @u This should also affect encrypted keys - but that has to be correctly parameterised or it will fail with EINVAL before getting to the bit that will crashes. Change-Id: I171d566f431c56208e1fe279f466d2d399a9ac7c Reported-by: Dmitry Vyukov Signed-off-by: David Howells Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/keys/encrypted.c | 2 ++ security/keys/trusted.c | 5 ++++- security/keys/user_defined.c | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c index b1cba5b..e14c4be 100644 --- a/security/keys/encrypted.c +++ b/security/keys/encrypted.c @@ -708,6 +708,8 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) char *new_master_desc = NULL; int ret = 0; + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + return -ENOKEY; if (datalen <= 0 || datalen > 32767 || !data) return -EINVAL; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 0c33e2e..7611f70 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1002,12 +1002,15 @@ static void trusted_rcu_free(struct rcu_head *rcu) */ static int trusted_update(struct key *key, const void *data, size_t datalen) { - struct trusted_key_payload *p = key->payload.data; + struct trusted_key_payload *p; struct trusted_key_payload *new_p; struct trusted_key_options *new_o; char *datablob; int ret = 0; + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + return -ENOKEY; + p = key->payload.data; if (!p->migratable) return -EPERM; if (datalen <= 0 || datalen > 32767 || !data) diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 69ff52c..49ec881 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -97,7 +97,10 @@ int user_update(struct key *key, const void *data, size_t datalen) if (ret == 0) { /* attach the new data, displacing the old */ - zap = key->payload.data; + if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + zap = key->payload.data; + else + zap = NULL; rcu_assign_pointer(key->payload.data, upayload); key->expiry = 0; } -- cgit v1.1 From b9ff9ff44ca2b2b348a27081c8e0bb686dd094fa Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 15 Jan 2015 17:49:25 +0000 Subject: vfs: new internal helper: mnt_has_parent(mnt) vfsmounts have ->mnt_parent pointing either to a different vfsmount or to itself; it's never NULL and termination condition in loops traversing the tree towards root is mnt == mnt->mnt_parent. At least one place (see the next patch) is confused about what's going on; let's add an explicit helper checking it right way and use it in all places where we need it. Not that there had been too many, but... Signed-off-by: Al Viro (cherry picked from commit b2dba1af3c4157040303a76d25216b1713d333d0) CVE-2014-7970 BugLink: http://bugs.launchpad.net/bugs/1383356 Signed-off-by: Luis Henriques Acked-by: Stefan Bader Acked-by: Andy Whitcroft Signed-off-by: Andy Whitcroft Change-Id: Iaa5ab510804f3b17fe71197b8919d663a416bf05 --- fs/dcache.c | 6 +++--- fs/mount.h | 6 ++++++ fs/namespace.c | 14 +++++++------- fs/pnode.c | 2 +- fs/pnode.h | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 fs/mount.h diff --git a/fs/dcache.c b/fs/dcache.c index ecc0742..5d34f80 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -37,6 +37,7 @@ #include #include #include "internal.h" +#include "mount.h" /* * Usage: @@ -2519,9 +2520,8 @@ static int prepend_path(const struct path *path, if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { /* Global root? */ - if (vfsmnt->mnt_parent == vfsmnt) { + if (!mnt_has_parent(vfsmnt)) goto global_root; - } dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; continue; @@ -2921,7 +2921,7 @@ int path_is_under(struct path *path1, struct path *path2) br_read_lock(vfsmount_lock); if (mnt != path2->mnt) { for (;;) { - if (mnt->mnt_parent == mnt) { + if (!mnt_has_parent(mnt)) { br_read_unlock(vfsmount_lock); return 0; } diff --git a/fs/mount.h b/fs/mount.h new file mode 100644 index 0000000..7890e49 --- /dev/null +++ b/fs/mount.h @@ -0,0 +1,6 @@ +#include + +static inline int mnt_has_parent(struct vfsmount *mnt) +{ + return mnt != mnt->mnt_parent; +} diff --git a/fs/namespace.c b/fs/namespace.c index 4f47629..3d86080 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1207,7 +1207,7 @@ void release_mounts(struct list_head *head) while (!list_empty(head)) { mnt = list_first_entry(head, struct vfsmount, mnt_hash); list_del_init(&mnt->mnt_hash); - if (mnt->mnt_parent != mnt) { + if (mnt_has_parent(mnt)) { struct dentry *dentry; struct vfsmount *m; @@ -1248,7 +1248,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) __mnt_make_shortterm(p); p->mnt_ns = NULL; list_del_init(&p->mnt_child); - if (p->mnt_parent != p) { + if (mnt_has_parent(p)) { p->mnt_parent->mnt_ghosts++; dentry_reset_mounted(p->mnt_parent, p->mnt_mountpoint); } @@ -1893,7 +1893,7 @@ static int do_move_mount(struct path *path, char *old_name) if (old_path.dentry != old_path.mnt->mnt_root) goto out1; - if (old_path.mnt == old_path.mnt->mnt_parent) + if (!mnt_has_parent(old_path.mnt)) goto out1; if (S_ISDIR(path->dentry->d_inode->i_mode) != @@ -1913,7 +1913,7 @@ static int do_move_mount(struct path *path, char *old_name) tree_contains_unbindable(old_path.mnt)) goto out1; err = -ELOOP; - for (p = path->mnt; p->mnt_parent != p; p = p->mnt_parent) + for (p = path->mnt; mnt_has_parent(p); p = p->mnt_parent) if (p == old_path.mnt) goto out1; @@ -2598,17 +2598,17 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, error = -EINVAL; if (root.mnt->mnt_root != root.dentry) goto out4; /* not a mountpoint */ - if (root.mnt->mnt_parent == root.mnt) + if (!mnt_has_parent(root.mnt)) goto out4; /* not attached */ if (new.mnt->mnt_root != new.dentry) goto out4; /* not a mountpoint */ - if (new.mnt->mnt_parent == new.mnt) + if (!mnt_has_parent(new.mnt)) goto out4; /* not attached */ /* make sure we can reach put_old from new_root */ tmp = old.mnt; if (tmp != new.mnt) { for (;;) { - if (tmp->mnt_parent == tmp) + if (!mnt_has_parent(tmp)) goto out4; /* already mounted on put_old */ if (tmp->mnt_parent == new.mnt) break; diff --git a/fs/pnode.c b/fs/pnode.c index d42514e..f1cd958 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -36,7 +36,7 @@ static inline struct vfsmount *next_slave(struct vfsmount *p) static bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, const struct path *root) { - while (mnt != root->mnt && mnt->mnt_parent != mnt) { + while (mnt != root->mnt && mnt_has_parent(mnt)) { dentry = mnt->mnt_mountpoint; mnt = mnt->mnt_parent; } diff --git a/fs/pnode.h b/fs/pnode.h index 1ea4ae1..e4d24fa 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -9,7 +9,7 @@ #define _LINUX_PNODE_H #include -#include +#include "mount.h" #define IS_MNT_SHARED(mnt) (mnt->mnt_flags & MNT_SHARED) #define IS_MNT_SLAVE(mnt) (mnt->mnt_master) -- cgit v1.1 From 1ea0519741949d2d1030a42383665fa6353f047e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 15 Jan 2015 17:49:26 +0000 Subject: vfs: more mnt_parent cleanups a) mount --move is checking that ->mnt_parent is non-NULL before looking if that parent happens to be shared; ->mnt_parent is never NULL and it's not even an misspelled !mnt_has_parent() b) pivot_root open-codes is_path_reachable(), poorly. c) so does path_is_under(), while we are at it. Signed-off-by: Al Viro (backported from commit afac7cba7ed31968a95e181dc25e204e45009ea8) CVE-2014-7970 BugLink: http://bugs.launchpad.net/bugs/1383356 Signed-off-by: Luis Henriques Acked-by: Stefan Bader Acked-by: Andy Whitcroft Signed-off-by: Andy Whitcroft Change-Id: I6b2297f46388f135c1b760a37d45efc0e33542db --- fs/dcache.c | 25 ------------------------- fs/namespace.c | 42 +++++++++++++++++++++++++++--------------- fs/pnode.c | 15 --------------- fs/pnode.h | 2 ++ 4 files changed, 29 insertions(+), 55 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 5d34f80..996064f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2912,31 +2912,6 @@ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry) return result; } -int path_is_under(struct path *path1, struct path *path2) -{ - struct vfsmount *mnt = path1->mnt; - struct dentry *dentry = path1->dentry; - int res; - - br_read_lock(vfsmount_lock); - if (mnt != path2->mnt) { - for (;;) { - if (!mnt_has_parent(mnt)) { - br_read_unlock(vfsmount_lock); - return 0; - } - if (mnt->mnt_parent == path2->mnt) - break; - mnt = mnt->mnt_parent; - } - dentry = mnt->mnt_mountpoint; - } - res = is_subdir(dentry, path2->dentry); - br_read_unlock(vfsmount_lock); - return res; -} -EXPORT_SYMBOL(path_is_under); - void d_genocide(struct dentry *root) { struct dentry *this_parent; diff --git a/fs/namespace.c b/fs/namespace.c index 3d86080..900812f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1902,8 +1902,7 @@ static int do_move_mount(struct path *path, char *old_name) /* * Don't move a mount residing in a shared parent. */ - if (old_path.mnt->mnt_parent && - IS_MNT_SHARED(old_path.mnt->mnt_parent)) + if (IS_MNT_SHARED(old_path.mnt->mnt_parent)) goto out1; /* * Don't move a mount tree containing unbindable mounts to a destination @@ -2528,6 +2527,31 @@ out_type: } /* + * Return true if path is reachable from root + * + * namespace_sem or vfsmount_lock is held + */ +bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, + const struct path *root) +{ + while (mnt != root->mnt && mnt_has_parent(mnt)) { + dentry = mnt->mnt_mountpoint; + mnt = mnt->mnt_parent; + } + return mnt == root->mnt && is_subdir(dentry, root->dentry); +} + +int path_is_under(struct path *path1, struct path *path2) +{ + int res; + br_read_lock(vfsmount_lock); + res = is_path_reachable(path1->mnt, path1->dentry, path2); + br_read_unlock(vfsmount_lock); + return res; +} +EXPORT_SYMBOL(path_is_under); + +/* * pivot_root Semantics: * Moves the root file system of the current process to the directory put_old, * makes new_root as the new root file system of the current process, and sets @@ -2555,7 +2579,6 @@ out_type: SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, const char __user *, put_old) { - struct vfsmount *tmp; struct path new, old, parent_path, root_parent, root; int error; @@ -2605,18 +2628,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, if (!mnt_has_parent(new.mnt)) goto out4; /* not attached */ /* make sure we can reach put_old from new_root */ - tmp = old.mnt; - if (tmp != new.mnt) { - for (;;) { - if (!mnt_has_parent(tmp)) - goto out4; /* already mounted on put_old */ - if (tmp->mnt_parent == new.mnt) - break; - tmp = tmp->mnt_parent; - } - if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) - goto out4; - } else if (!is_subdir(old.dentry, new.dentry)) + if (!is_path_reachable(old.mnt, old.dentry, &new)) goto out4; /* make certain new is below the root */ if (!is_path_reachable(new.mnt, new.dentry, &root)) diff --git a/fs/pnode.c b/fs/pnode.c index f1cd958..4d5a06e 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -28,21 +28,6 @@ static inline struct vfsmount *next_slave(struct vfsmount *p) return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); } -/* - * Return true if path is reachable from root - * - * namespace_sem is held, and mnt is attached - */ -static bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, - const struct path *root) -{ - while (mnt != root->mnt && mnt_has_parent(mnt)) { - dentry = mnt->mnt_mountpoint; - mnt = mnt->mnt_parent; - } - return mnt == root->mnt && is_subdir(dentry, root->dentry); -} - static struct vfsmount *get_peer_under_root(struct vfsmount *mnt, struct mnt_namespace *ns, const struct path *root) diff --git a/fs/pnode.h b/fs/pnode.h index e4d24fa..a602ec4 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -36,4 +36,6 @@ int propagate_umount(struct list_head *); int propagate_mount_busy(struct vfsmount *, int); void mnt_release_group_id(struct vfsmount *); int get_dominating_id(struct vfsmount *mnt, const struct path *root); +bool is_path_reachable(struct vfsmount *, struct dentry *, + const struct path *root); #endif /* _LINUX_PNODE_H */ -- cgit v1.1 From 6dd3b4e53f10b8c06c4067a3585e0f82ba6d31c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 24 May 2012 15:10:08 -0700 Subject: Staging: android: binder: Add some tracepoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tracepoints: - ioctl entry and exit - Main binder lock: lock, locked and unlock - Command and return buffer opcodes - Transaction: create and receive - Transaction buffer: create and free - Object and file descriptor transfer - binder_update_page_range Change-Id: Ib09ae78b0b8b75062325318e2307afd71b7c4458 Signed-off-by: Arve Hjønnevåg --- drivers/staging/android/Makefile | 2 + drivers/staging/android/binder.c | 89 +++++++-- drivers/staging/android/binder_trace.h | 327 +++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+), 19 deletions(-) create mode 100644 drivers/staging/android/binder_trace.h diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 8e057e6..c16e2bf 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_ANDROID_LOGGER) += logger.o obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 42b23f6..e2b69f2 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -36,8 +36,9 @@ #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); @@ -500,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; @@ -626,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 @@ -1564,6 +1580,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) { @@ -1574,6 +1593,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); @@ -1650,6 +1670,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, @@ -1678,6 +1699,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, @@ -1691,6 +1713,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, @@ -1735,6 +1759,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? */ @@ -1783,6 +1808,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); @@ -1828,6 +1854,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)]++; @@ -1997,6 +2024,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; @@ -2205,6 +2233,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)]++; @@ -2266,7 +2295,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))) { @@ -2290,7 +2324,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; @@ -2480,6 +2516,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" @@ -2632,12 +2669,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)) @@ -2665,11 +2704,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; @@ -2694,6 +2735,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))) @@ -2703,6 +2745,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) { @@ -2781,10 +2824,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; } @@ -2926,13 +2971,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]; @@ -3122,7 +3170,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, @@ -3149,7 +3197,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); @@ -3490,7 +3538,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"); @@ -3502,7 +3550,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; } @@ -3513,7 +3561,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"); @@ -3522,7 +3570,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; } @@ -3533,13 +3581,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; } @@ -3565,7 +3613,7 @@ static int binder_proc_show(struct seq_file *m, void *unused) print_binder_proc(m, proc, 1); } if (do_lock) - mutex_unlock(&binder_lock); + binder_unlock(__func__); return 0; } @@ -3660,4 +3708,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 0000000..82a567c --- /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 -- cgit v1.1 From c306a0e6a789795851f862701fb9ca606ffa3fa2 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 1 Dec 2011 14:12:55 -0500 Subject: Revert "udp: remove redundant variable" This reverts commit 81d54ec8479a2c695760da81f05b5a9fb2dbe40a. If we take the "try_again" goto, due to a checksum error, the 'len' has already been truncated. So we won't compute the same values as the original code did. Change-Id: I0503e45682377965571c4544385811765ef2e2bb Reported-by: paul bilke Signed-off-by: David S. Miller (cherry picked from commit 7c3b1de9c0ba32bd33ac15c62e8b8a0548641c6b) --- net/ipv4/udp.c | 15 ++++++++------- net/ipv6/udp.c | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0e8d4a7..f8d2119 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1165,7 +1165,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct inet_sock *inet = inet_sk(sk); struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; struct sk_buff *skb; - unsigned int ulen; + unsigned int ulen, copied; int peeked; int err; int is_udplite = IS_UDPLITE(sk); @@ -1187,9 +1187,10 @@ try_again: goto out; ulen = skb->len - sizeof(struct udphdr); - if (len > ulen) - len = ulen; - else if (len < ulen) + copied = len; + if (copied > ulen) + copied = ulen; + else if (copied < ulen) msg->msg_flags |= MSG_TRUNC; /* @@ -1198,14 +1199,14 @@ try_again: * coverage checksum (UDP-Lite), do it before the copy. */ - if (len < ulen || UDP_SKB_CB(skb)->partial_cov) { + if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { if (udp_lib_checksum_complete(skb)) goto csum_copy_err; } if (skb_csum_unnecessary(skb)) err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov, len); + msg->msg_iov, copied); else { err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), @@ -1234,7 +1235,7 @@ try_again: if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); - err = len; + err = copied; if (flags & MSG_TRUNC) err = ulen; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index d786b9c..72660aa 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -340,7 +340,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct ipv6_pinfo *np = inet6_sk(sk); struct inet_sock *inet = inet_sk(sk); struct sk_buff *skb; - unsigned int ulen; + unsigned int ulen, copied; int peeked; int err; int is_udplite = IS_UDPLITE(sk); @@ -363,9 +363,10 @@ try_again: goto out; ulen = skb->len - sizeof(struct udphdr); - if (len > ulen) - len = ulen; - else if (len < ulen) + copied = len; + if (copied > ulen) + copied = ulen; + else if (copied < ulen) msg->msg_flags |= MSG_TRUNC; is_udp4 = (skb->protocol == htons(ETH_P_IP)); @@ -376,14 +377,14 @@ try_again: * coverage checksum (UDP-Lite), do it before the copy. */ - if (len < ulen || UDP_SKB_CB(skb)->partial_cov) { + if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { if (udp_lib_checksum_complete(skb)) goto csum_copy_err; } if (skb_csum_unnecessary(skb)) err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov,len); + msg->msg_iov, copied ); else { err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); @@ -433,7 +434,7 @@ try_again: datagram_recv_ctl(sk, msg, skb); } - err = len; + err = copied; if (flags & MSG_TRUNC) err = ulen; -- cgit v1.1 From dbb3b088e8c6c2e8b00bf065a1e76920c378a56f Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Mon, 12 Oct 2015 16:33:44 +0300 Subject: fuse: break infinite loop in fuse_fill_write_pages() I got a report about unkillable task eating CPU. Further investigation shows, that the problem is in the fuse_fill_write_pages() function. If iov's first segment has zero length, we get an infinite loop, because we never reach iov_iter_advance() call. Fix this by calling iov_iter_advance() before repeating an attempt to copy data from userspace. A similar problem is described in 124d3b7041f ("fix writev regression: pan hanging unkillable and un-straceable"). If zero-length segmend is followed by segment with invalid address, iov_iter_fault_in_readable() checks only first segment (zero-length), iov_iter_copy_from_user_atomic() skips it, fails at second and returns zero -> goto again without skipping zero-length segment. Patch calls iov_iter_advance() before goto again: we'll skip zero-length segment at second iteraction and iov_iter_fault_in_readable() will detect invalid address. Special thanks to Konstantin Khlebnikov, who helped a lot with the commit description. Cc: Andrew Morton Cc: Maxim Patlasov Cc: Konstantin Khlebnikov Signed-off-by: Roman Gushchin Signed-off-by: Miklos Szeredi Fixes: ea9b9907b82a ("fuse: implement perform_write") Cc: Conflicts: fs/fuse/file.c Change-Id: Id37193373294dd43191469389cfe68ca1736a54b --- fs/fuse/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e5c0b5d..c404e6f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -932,6 +932,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req, pagefault_enable(); flush_dcache_page(page); + iov_iter_advance(ii, tmp); if (!tmp) { unlock_page(page); page_cache_release(page); @@ -943,7 +944,6 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req, req->pages[req->num_pages] = page; req->num_pages++; - iov_iter_advance(ii, tmp); count += tmp; pos += tmp; offset += tmp; -- cgit v1.1 From fd54af341177a37606ee3179c6e79c83868fe61c Mon Sep 17 00:00:00 2001 From: Ziyan Date: Fri, 26 Feb 2016 17:55:12 +0100 Subject: input: Don't use monotonic time for event time stamps. Change-Id: Id0a5c96eb8a7d1d8e08bd3b7a4ac50cdc9c73f39 --- drivers/input/evdev.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 67dd99a..97046dd 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -107,11 +107,8 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; - struct timespec ts; - ktime_get_ts(&ts); - event.time.tv_sec = ts.tv_sec; - event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; -- cgit v1.1 From f0f6cbaf4d48a251a2a37078f82ae96c4f6079fe Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 3 Feb 2012 00:19:07 -0800 Subject: Input: add infrastructure for selecting clockid for event time stamps As noted by Arve and others, since wall time can jump backwards, it is difficult to use for input because one cannot determine if one event occurred before another or for how long a key was pressed. However, the timestamp field is part of the kernel ABI, and cannot be changed without possibly breaking existing users. This patch adds a new IOCTL that allows a clockid to be set in the evdev_client struct that will specify which time base to use for event timestamps (ie: CLOCK_MONOTONIC instead of CLOCK_REALTIME). For now we only support CLOCK_MONOTONIC and CLOCK_REALTIME, but in the future we could support other clockids if appropriate. The default remains CLOCK_REALTIME, so we don't change the ABI. Signed-off-by: John Stultz Reviewed-by: Daniel Kurtz Signed-off-by: Dmitry Torokhov Conflicts: include/linux/input.h Change-Id: I7b9b442dcd7930a1e72c688327e6fb7275107128 --- drivers/input/evdev.c | 25 +++++++++++++++++++++---- include/linux/input.h | 2 ++ kernel/time/timekeeping.c | 2 ++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 97046dd..dd719fd 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -55,6 +55,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + int clkid; unsigned int bufsize; struct input_event buffer[]; }; @@ -63,8 +64,12 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, - struct input_event *event) + struct input_event *event, + ktime_t mono, ktime_t real) { + event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? + mono : real); + /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); @@ -107,8 +112,11 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; + ktime_t time_mono, time_real; + + time_mono = ktime_get(); + time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); - do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; @@ -116,11 +124,12 @@ static void evdev_event(struct input_handle *handle, rcu_read_lock(); client = rcu_dereference(evdev->grab); + if (client) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); rcu_read_unlock(); @@ -732,6 +741,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, else return evdev_ungrab(evdev, client); + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) + return -EINVAL; + client->clkid = i; + return 0; + case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); diff --git a/include/linux/input.h b/include/linux/input.h index 5694583..af73bfd 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -132,6 +132,8 @@ struct input_keymap_entry { #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 */ + /* * Device properties and quirks */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 7aa8c3d..91e88d2 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1214,6 +1214,8 @@ ktime_t ktime_get_monotonic_offset(void) } while (read_seqretry(&xtime_lock, seq)); return timespec_to_ktime(wtom); } +EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset); + /** * xtime_update() - advances the timekeeping infrastructure -- cgit v1.1 From 1bbc15bdb1b3ee0cf511baace1b4e6042d6b22ab Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Sat, 19 Mar 2016 00:18:09 +1100 Subject: i9300: enable KSM Change-Id: I2bf903671f625e616d51a2b6a40635fc2f543b6f --- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index e1ace9c..5961c38 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -688,7 +688,7 @@ CONFIG_MIGRATION=y CONFIG_ZONE_DMA_FLAG=0 CONFIG_BOUNCE=y CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set CONFIG_CMA=y -- cgit v1.1 From 9393c919cefaca29c13f5725492b8181d2358a2e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 16 Jun 2015 22:11:06 +0100 Subject: pipe: iovec: Fix memory corruption when retrying atomic copy as non-atomic pipe_iov_copy_{from,to}_user() may be tried twice with the same iovec, the first time atomically and the second time not. The second attempt needs to continue from the iovec position, pipe buffer offset and remaining length where the first attempt failed, but currently the pipe buffer offset and remaining length are reset. This will corrupt the piped data (possibly also leading to an information leak between processes) and may also corrupt kernel memory. This was fixed upstream by commits f0d1bec9d58d ("new helper: copy_page_from_iter()") and 637b58c2887e ("switch pipe_read() to copy_page_to_iter()"), but those aren't suitable for stable. This fix for older kernel versions was made by Seth Jennings for RHEL and I have extracted it from their update. CVE-2015-1805 Bug: 27275324 Change-Id: I459adb9076fcd50ff1f1c557089c4e421b036ec4 References: https://bugzilla.redhat.com/show_bug.cgi?id=1202855 Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 85c34d007116f8a8aafb173966a605fb03532f45) --- fs/pipe.c | 55 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 342aa86..6060d2f 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -103,25 +103,27 @@ void pipe_wait(struct pipe_inode_info *pipe) } static int -pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, - int atomic) +pipe_iov_copy_from_user(void *addr, int *offset, struct iovec *iov, + size_t *remaining, int atomic) { unsigned long copy; - while (len > 0) { + while (*remaining > 0) { while (!iov->iov_len) iov++; - copy = min_t(unsigned long, len, iov->iov_len); + copy = min_t(unsigned long, *remaining, iov->iov_len); if (atomic) { - if (__copy_from_user_inatomic(to, iov->iov_base, copy)) + if (__copy_from_user_inatomic(addr + *offset, + iov->iov_base, copy)) return -EFAULT; } else { - if (copy_from_user(to, iov->iov_base, copy)) + if (copy_from_user(addr + *offset, + iov->iov_base, copy)) return -EFAULT; } - to += copy; - len -= copy; + *offset += copy; + *remaining -= copy; iov->iov_base += copy; iov->iov_len -= copy; } @@ -129,25 +131,27 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, } static int -pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len, - int atomic) +pipe_iov_copy_to_user(struct iovec *iov, void *addr, int *offset, + size_t *remaining, int atomic) { unsigned long copy; - while (len > 0) { + while (*remaining > 0) { while (!iov->iov_len) iov++; - copy = min_t(unsigned long, len, iov->iov_len); + copy = min_t(unsigned long, *remaining, iov->iov_len); if (atomic) { - if (__copy_to_user_inatomic(iov->iov_base, from, copy)) + if (__copy_to_user_inatomic(iov->iov_base, + addr + *offset, copy)) return -EFAULT; } else { - if (copy_to_user(iov->iov_base, from, copy)) + if (copy_to_user(iov->iov_base, + addr + *offset, copy)) return -EFAULT; } - from += copy; - len -= copy; + *offset += copy; + *remaining -= copy; iov->iov_base += copy; iov->iov_len -= copy; } @@ -383,7 +387,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, struct pipe_buffer *buf = pipe->bufs + curbuf; const struct pipe_buf_operations *ops = buf->ops; void *addr; - size_t chars = buf->len; + size_t chars = buf->len, remaining; int error, atomic; if (chars > total_len) @@ -397,9 +401,11 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, } atomic = !iov_fault_in_pages_write(iov, chars); + remaining = chars; redo: addr = ops->map(pipe, buf, atomic); - error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic); + error = pipe_iov_copy_to_user(iov, addr, &buf->offset, + &remaining, atomic); ops->unmap(pipe, buf, addr); if (unlikely(error)) { /* @@ -414,7 +420,6 @@ redo: break; } ret += chars; - buf->offset += chars; buf->len -= chars; /* Was it a packet buffer? Clean up and exit */ @@ -521,6 +526,7 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov, if (ops->can_merge && offset + chars <= PAGE_SIZE) { int error, atomic = 1; void *addr; + size_t remaining = chars; error = ops->confirm(pipe, buf); if (error) @@ -529,8 +535,8 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov, iov_fault_in_pages_read(iov, chars); redo1: addr = ops->map(pipe, buf, atomic); - error = pipe_iov_copy_from_user(offset + addr, iov, - chars, atomic); + error = pipe_iov_copy_from_user(addr, &offset, iov, + &remaining, atomic); ops->unmap(pipe, buf, addr); ret = error; do_wakeup = 1; @@ -565,6 +571,8 @@ redo1: struct page *page = pipe->tmp_page; char *src; int error, atomic = 1; + int offset = 0; + size_t remaining; if (!page) { page = alloc_page(GFP_HIGHUSER); @@ -585,14 +593,15 @@ redo1: chars = total_len; iov_fault_in_pages_read(iov, chars); + remaining = chars; redo2: if (atomic) src = kmap_atomic(page, KM_USER0); else src = kmap(page); - error = pipe_iov_copy_from_user(src, iov, chars, - atomic); + error = pipe_iov_copy_from_user(src, &offset, iov, + &remaining, atomic); if (atomic) kunmap_atomic(src, KM_USER0); else -- cgit v1.1 From 0fc0548f98b6a525087caae453835a9ace22f447 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 21 Mar 2016 20:52:31 +1100 Subject: mfc5x: update from n7100 source drop * include a few fixes for weird samsung mistakes in mfc_open Change-Id: Icad82feac58c07f6af4c014e7dc967c70c6d1405 --- .../arm/mach-exynos/include/mach/busfreq_exynos4.h | 5 + drivers/media/video/samsung/mfc5x/SsbSipMfcApi.h | 4 +- drivers/media/video/samsung/mfc5x/mfc_buf.h | 5 + drivers/media/video/samsung/mfc5x/mfc_cmd.c | 2 +- drivers/media/video/samsung/mfc5x/mfc_dec.c | 129 ++++++--------------- drivers/media/video/samsung/mfc5x/mfc_dev.c | 79 +++++++++++-- drivers/media/video/samsung/mfc5x/mfc_dev.h | 4 + drivers/media/video/samsung/mfc5x/mfc_enc.c | 32 +++-- drivers/media/video/samsung/mfc5x/mfc_inst.c | 59 +++++----- drivers/media/video/samsung/mfc5x/mfc_inst.h | 4 + drivers/media/video/samsung/mfc5x/mfc_mem.c | 56 ++++++++- 11 files changed, 232 insertions(+), 147 deletions(-) diff --git a/arch/arm/mach-exynos/include/mach/busfreq_exynos4.h b/arch/arm/mach-exynos/include/mach/busfreq_exynos4.h index f8f377d..ab75f55 100644 --- a/arch/arm/mach-exynos/include/mach/busfreq_exynos4.h +++ b/arch/arm/mach-exynos/include/mach/busfreq_exynos4.h @@ -27,6 +27,11 @@ #define PRIME_DMC_MAX_THRESHOLD 30 #define EXYNOS4412_DMC_MAX_THRESHOLD 30 #define EXYNOS4212_DMC_MAX_THRESHOLD 30 +#if defined(CONFIG_MACH_P4NOTE) || defined(CONFIG_MACH_SP7160LTE) || defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_C1) || defined(CONFIG_MACH_T0) +#define DECODING_LOAD 5 +#else +#define DECODING_LOAD 10 +#endif extern unsigned int up_threshold; extern unsigned int ppmu_threshold; diff --git a/drivers/media/video/samsung/mfc5x/SsbSipMfcApi.h b/drivers/media/video/samsung/mfc5x/SsbSipMfcApi.h index cbf6cab..c68980d 100644 --- a/drivers/media/video/samsung/mfc5x/SsbSipMfcApi.h +++ b/drivers/media/video/samsung/mfc5x/SsbSipMfcApi.h @@ -41,7 +41,7 @@ #define SAMSUNG_MFC_DEV_NAME "/dev/s3c-mfc" #if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) -#define SUPPORT_SLICE_ENCODING 0 // originaly 1, but we're missing matching userspace +#define SUPPORT_SLICE_ENCODING 0 // originally 1, but we're missing matching userspace #else #define SUPPORT_SLICE_ENCODING 0 #endif @@ -136,7 +136,7 @@ typedef enum { /* C210 specific feature */ MFC_ENC_SETCONF_VUI_INFO, MFC_ENC_SETCONF_I_PERIOD, - MFC_ENC_SETCONF_SPS_PPS_GEN, + MFC_ENC_SETCONF_SPS_PPS_GEN, MFC_ENC_SETCONF_HIER_P, MFC_ENC_SETCONF_SEI_GEN, diff --git a/drivers/media/video/samsung/mfc5x/mfc_buf.h b/drivers/media/video/samsung/mfc5x/mfc_buf.h index 28ef0d6..f93ed26 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_buf.h +++ b/drivers/media/video/samsung/mfc5x/mfc_buf.h @@ -34,8 +34,13 @@ #define ALIGN_H_L_L 16 /* Linear, Vertical, Luma */ #define ALIGN_H_L_C 8 /* Linear, Vertical, Chroma */ +#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_GC1) +/* System */ /* Size, Port, Align */ +#define MFC_FW_SYSTEM_SIZE (0x100000) /* 1MB, A, N(4KB for VMEM) */ +#else /* System */ /* Size, Port, Align */ #define MFC_FW_SYSTEM_SIZE (0x80000) /* 512KB, A, N(4KB for VMEM) */ +#endif /* Instance */ #define MFC_CTX_SIZE_L (0x96000) /* 600KB, N, 2KB, H.264 Decoding only */ diff --git a/drivers/media/video/samsung/mfc5x/mfc_cmd.c b/drivers/media/video/samsung/mfc5x/mfc_cmd.c index 38b4757..ba4faf9 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_cmd.c +++ b/drivers/media/video/samsung/mfc5x/mfc_cmd.c @@ -310,7 +310,7 @@ int mfc_cmd_inst_open(struct mfc_inst_ctx *ctx) } memset(&h2r_args, 0, sizeof(struct mfc_cmd_args)); - h2r_args.arg[0] = ctx->codecid; + h2r_args.arg[0] = (1 << 29) | ctx->codecid; h2r_args.arg[1] = crc << 31 | pixelcache; h2r_args.arg[2] = ctx->ctxbufofs; h2r_args.arg[3] = ctx->ctxbufsize; diff --git a/drivers/media/video/samsung/mfc5x/mfc_dec.c b/drivers/media/video/samsung/mfc5x/mfc_dec.c index d3d336a..f0a7c4d 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_dec.c +++ b/drivers/media/video/samsung/mfc5x/mfc_dec.c @@ -155,10 +155,6 @@ static void dump_stream(unsigned long address, unsigned int size) ctx->ctxbufofs = mfc_mem_base_ofs(alloc->real) >> 11; ctx->ctxbufsize = alloc->size; - memset((void *)alloc->addr, 0, alloc->size); - - mfc_mem_cache_clean((void *)alloc->addr, alloc->size); - return 0; } @@ -176,10 +172,6 @@ static int h264_alloc_ctx_buf(struct mfc_inst_ctx *ctx) ctx->ctxbufofs = mfc_mem_base_ofs(alloc->real) >> 11; ctx->ctxbufsize = alloc->size; - memset((void *)alloc->addr, 0, alloc->size); - - mfc_mem_cache_clean((void *)alloc->addr, alloc->size); - return 0; } @@ -1591,56 +1583,6 @@ static int CheckMPEG4StartCode(unsigned char *src_mem, unsigned int remainSize) return -1; } -static int CheckDecStartCode(unsigned char *src_mem, - unsigned int nstreamSize, - SSBSIP_MFC_CODEC_TYPE nCodecType) -{ - unsigned int index = 0; - /* Check Start Code within "isearchSize" bytes */ - unsigned int isearchSize = 20; - unsigned int nShift = 0; - unsigned char nFlag = 0xFF; - - if (nCodecType == H263_DEC) { - nFlag = 0x08; - nShift = 4; - } else if (nCodecType == MPEG4_DEC) { - nFlag = 0x01; - nShift = 0; - } else if (nCodecType == H264_DEC) { - nFlag = 0x01; - nShift = 0; - } else - nFlag = 0xFF; - - /* Last frame detection from user */ - if (nstreamSize == 0) - nFlag = 0xFF; - - if (nFlag == 0xFF) - return 0; - - if (nstreamSize > 3) { - if (nstreamSize > isearchSize) { - for (index = 0; index < isearchSize-3; index++) { - if ((src_mem[index] == 0x00) && - (src_mem[index+1] == 0x00) && - ((src_mem[index+2] >> nShift) == nFlag)) - return index; - } - } else { - for (index = 0; index < nstreamSize - 3; index++) { - if ((src_mem[index] == 0x00) && - (src_mem[index+1] == 0x00) && - ((src_mem[index+2] >> nShift) == nFlag)) - return index; - } - } - } - - return -1; -} - void mfc_init_decoders(void) { list_add_tail(&unknown_dec.list, &mfc_decoders); @@ -1904,6 +1846,20 @@ int mfc_init_decoding(struct mfc_inst_ctx *ctx, union mfc_args *args) } #endif +#if defined(CONFIG_MACH_GC1) && defined(CONFIG_EXYNOS4_CPUFREQ) + if ((ctx->width >= 1280 && ctx->height >= 720) + || (ctx->width >= 720 && ctx->height >= 1280)) { + if (atomic_read(&ctx->dev->cpufreq_lock_cnt) == 0) { + if (0 == ctx->dev->cpufreq_level) /* 800MHz */ + exynos_cpufreq_get_level(800000, &ctx->dev->cpufreq_level); + exynos_cpufreq_lock(DVFS_LOCK_ID_MFC, ctx->dev->cpufreq_level); + mfc_dbg("[%s] CPU Freq Locked 800MHz!\n", __func__); + } + atomic_inc(&ctx->dev->cpufreq_lock_cnt); + ctx->cpufreq_flag = true; + } +#endif + #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_EXYNOS4_CPUFREQ) if ((ctx->width >= 1280 && ctx->height >= 720) || (ctx->width >= 720 && ctx->height >= 1280)) { @@ -1919,27 +1875,27 @@ int mfc_init_decoding(struct mfc_inst_ctx *ctx, union mfc_args *args) #endif #ifdef CONFIG_BUSFREQ_OPP - if (HD_MOVIE_SIZE_MULTIPLY_WIDTH_HEIGHT > (ctx->width * ctx->height)) { - if (atomic_read(&ctx->dev->dmcthreshold_lock_cnt) == 0) { - mfc_info("Implement set dmc_max_threshold\n"); - if (soc_is_exynos4212()) { +if (HD_MOVIE_SIZE_MULTIPLY_WIDTH_HEIGHT > (ctx->width * ctx->height)) { + if (atomic_read(&ctx->dev->dmcthreshold_lock_cnt) == 0) { + mfc_info("Implement set dmc_max_threshold\n"); + if (soc_is_exynos4212()) { + dmc_max_threshold = + EXYNOS4212_DMC_MAX_THRESHOLD + DECODING_LOAD; + } else if (soc_is_exynos4412()) { + if (samsung_rev() >= EXYNOS4412_REV_2_0) dmc_max_threshold = - EXYNOS4212_DMC_MAX_THRESHOLD + 5; - } else if (soc_is_exynos4412()) { - if (samsung_rev() >= EXYNOS4412_REV_2_0) - dmc_max_threshold = - PRIME_DMC_MAX_THRESHOLD + 5; - else - dmc_max_threshold = - EXYNOS4412_DMC_MAX_THRESHOLD + 5; - } else { - pr_err("Unsupported model.\n"); - return -EINVAL; - } + PRIME_DMC_MAX_THRESHOLD + DECODING_LOAD; + else + dmc_max_threshold = + EXYNOS4412_DMC_MAX_THRESHOLD + DECODING_LOAD; + } else { + pr_err("Unsupported model.\n"); + return -EINVAL; } - atomic_inc(&ctx->dev->dmcthreshold_lock_cnt); - ctx->dmcthreshold_flag = true; } + atomic_inc(&ctx->dev->dmcthreshold_lock_cnt); + ctx->dmcthreshold_flag = true; +} #endif /* * allocate & set codec buffers @@ -2146,7 +2102,6 @@ static int mfc_decoding_frame(struct mfc_inst_ctx *ctx, struct mfc_dec_exe_arg * int display_chroma_addr; int display_frame_type; int display_frame_tag; - unsigned char *stream_vir; int ret; struct mfc_dec_ctx *dec_ctx = (struct mfc_dec_ctx *)ctx->c_priv; long mem_ofs; @@ -2154,22 +2109,6 @@ static int mfc_decoding_frame(struct mfc_inst_ctx *ctx, struct mfc_dec_exe_arg * void *ump_handle; #endif -#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION - if (!ctx->drm_flag) { -#endif - /* Check Frame Start code */ - stream_vir = phys_to_virt(exe_arg->in_strm_buf + start_ofs); - ret = CheckDecStartCode(stream_vir, exe_arg->in_strm_size, - exe_arg->in_codec_type); - if (ret < 0) { - mfc_err("Frame Check start Code Failed\n"); - /* FIXME: Need to define proper error */ - return MFC_FRM_BUF_SIZE_FAIL; - } -#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION - } -#endif - /* Set Frame Tag */ write_shm(ctx, dec_ctx->frametag, SET_FRAME_TAG); @@ -2405,7 +2344,9 @@ int mfc_exec_decoding(struct mfc_inst_ctx *ctx, union mfc_args *args) if (ctx->resolution_status == RES_SET_CHANGE) { ret = mfc_decoding_frame(ctx, exe_arg, &consumed); #ifndef CONFIG_SLP - } else if ((ctx->resolution_status == RES_WAIT_FRAME_DONE) && + } + + if ((ctx->resolution_status == RES_WAIT_FRAME_DONE) && (exe_arg->out_display_status == DISP_S_FINISH)) { exe_arg->out_display_status = DISP_S_RES_CHANGE; ret = mfc_change_resolution(ctx, exe_arg); diff --git a/drivers/media/video/samsung/mfc5x/mfc_dev.c b/drivers/media/video/samsung/mfc5x/mfc_dev.c index 23bc10d..43ea79d 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_dev.c +++ b/drivers/media/video/samsung/mfc5x/mfc_dev.c @@ -213,7 +213,7 @@ static int mfc_open(struct inode *inode, struct file *file) mutex_lock(&mfcdev->lock); -#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_M0) +#ifdef CONFIG_USE_MFC_CMA if (atomic_read(&mfcdev->inst_cnt) == 0) { size_t size = 0x02800000; mfcdev->cma_vaddr = dma_alloc_coherent(mfcdev->device, size, @@ -368,8 +368,6 @@ static int mfc_open(struct inode *inode, struct file *file) mfc_ctx->ctxbufofs = mfc_mem_base_ofs(alloc->real) >> 11; mfc_ctx->ctxbufsize = alloc->size; - memset((void *)alloc->addr, 0, alloc->size); - mfc_mem_cache_clean((void *)alloc->addr, alloc->size); } #endif @@ -392,7 +390,7 @@ static int mfc_open(struct inode *inode, struct file *file) ret = mfc_queue_alloc(mfc_ctx); if (ret < 0) { mfc_err("mfc_queue_alloc failed\n"); - goto err_inst_ctx; + goto err_queue_alloc; } #endif @@ -403,9 +401,17 @@ static int mfc_open(struct inode *inode, struct file *file) return 0; +#ifdef CONFIG_SLP_DMABUF +err_queue_alloc: +#endif #ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION err_drm_ctx: #endif + mfcdev->inst_ctx[inst_id] = NULL; + atomic_dec(&mfcdev->inst_cnt); + + mfc_destroy_inst(mfc_ctx); + err_inst_ctx: err_inst_id: err_inst_cnt: @@ -423,7 +429,16 @@ err_pwr_enable: #endif err_fw_state: -#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION +#ifdef CONFIG_USE_MFC_CMA + if (atomic_read(&mfcdev->inst_cnt) == 0) { + size_t size = 0x02800000; + dma_free_coherent(mfcdev->device, size, mfcdev->cma_vaddr, + mfcdev->cma_dma_addr); + printk(KERN_INFO "%s[%d] size 0x%x, vaddr 0x%x, base 0x0%x\n", + __func__, __LINE__, (int)size, + (int) mfcdev->cma_vaddr, + (int)mfcdev->cma_dma_addr); + } #endif mutex_unlock(&mfcdev->lock); @@ -476,6 +491,19 @@ static int mfc_release(struct inode *inode, struct file *file) } #endif +#if defined(CONFIG_MACH_GC1) && defined(CONFIG_EXYNOS4_CPUFREQ) + /* Release MFC & CPU Frequency lock for High resolution */ + if (mfc_ctx->cpufreq_flag == true) { + atomic_dec(&dev->cpufreq_lock_cnt); + mfc_ctx->cpufreq_flag = false; + if (atomic_read(&dev->cpufreq_lock_cnt) == 0) { + /* release Freq lock back to normal */ + exynos_cpufreq_lock_free(DVFS_LOCK_ID_MFC); + mfc_dbg("[%s] CPU Freq lock Released Normal!\n", __func__); + } + } +#endif + #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_EXYNOS4_CPUFREQ) /* Release MFC & CPU Frequency lock for High resolution */ if (mfc_ctx->cpufreq_flag == true) { @@ -565,7 +593,7 @@ static int mfc_release(struct inode *inode, struct file *file) err_pwr_disable: -#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_M0) +#ifdef CONFIG_USE_MFC_CMA if (atomic_read(&mfcdev->inst_cnt) == 0) { size_t size = 0x02800000; dma_free_coherent(mfcdev->device, size, mfcdev->cma_vaddr, @@ -977,6 +1005,16 @@ static long mfc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* RMVME: need locking ? */ mutex_lock(&dev->lock); + if (mfc_ctx->state < INST_STATE_SETUP) { + mfc_err("IOCTL_MFC_GET_CONFIG invalid state: 0x%08x\n", + mfc_ctx->state); + in_param.ret_code = MFC_STATE_INVALID; + ret = -EINVAL; + + mutex_unlock(&dev->lock); + break; + } + cfg_arg = (struct mfc_config_arg *)&in_param.args; in_param.ret_code = mfc_get_inst_cfg(mfc_ctx, cfg_arg->type, @@ -1083,9 +1121,13 @@ static int mfc_mmap(struct file *file, struct vm_area_struct *vma) unsigned long start, size; #endif #endif + mfc_info("%s line : %d IN\n", __func__, __LINE__); mfc_ctx = (struct mfc_inst_ctx *)file->private_data; - if (!mfc_ctx) + if (!mfc_ctx) { + mfc_err("%s line : %d mfc_ctx is NULL\n", + __func__, __LINE__); return -EINVAL; + } #if !(defined(CONFIG_VIDEO_MFC_VCM_UMP) || defined(CONFIG_S5P_VMEM)) dev = mfc_ctx->dev; @@ -1325,9 +1367,30 @@ static int mfc_mmap(struct file *file, struct vm_area_struct *vma) return 0; } +#ifdef CONFIG_USE_MFC_CMA +/* FIXME: workaround for CMA migration fail due to page lock */ +static int mfc_open_with_retry(struct inode *inode, struct file *file) +{ + int ret; + int i = 0; + + ret = mfc_open(inode, file); + + while (ret == -ENOMEM && i++ < 10) { + msleep(1000); + ret = mfc_open(inode, file); + } + + return ret; +} +#define MFC_OPEN mfc_open_with_retry +#else +#define MFC_OPEN mfc_open +#endif + static const struct file_operations mfc_fops = { .owner = THIS_MODULE, - .open = mfc_open, + .open = MFC_OPEN, .release = mfc_release, .unlocked_ioctl = mfc_ioctl, .mmap = mfc_mmap, diff --git a/drivers/media/video/samsung/mfc5x/mfc_dev.h b/drivers/media/video/samsung/mfc5x/mfc_dev.h index bb2095c..16bd027 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_dev.h +++ b/drivers/media/video/samsung/mfc5x/mfc_dev.h @@ -114,6 +114,10 @@ struct mfc_dev { #if defined(CONFIG_BUSFREQ) atomic_t busfreq_lock_cnt; /* Bus frequency Lock count */ #endif +#if defined(CONFIG_MACH_GC1) && defined(CONFIG_EXYNOS4_CPUFREQ) + atomic_t cpufreq_lock_cnt; /* CPU frequency Lock count */ + int cpufreq_level; /* CPU frequency leve */ +#endif #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_EXYNOS4_CPUFREQ) atomic_t cpufreq_lock_cnt; /* CPU frequency Lock count */ int cpufreq_level; /* CPU frequency leve */ diff --git a/drivers/media/video/samsung/mfc5x/mfc_enc.c b/drivers/media/video/samsung/mfc5x/mfc_enc.c index 65d7b6b..f2dc039 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_enc.c +++ b/drivers/media/video/samsung/mfc5x/mfc_enc.c @@ -55,10 +55,6 @@ static LIST_HEAD(mfc_encoders); ctx->ctxbufofs = mfc_mem_base_ofs(alloc->real) >> 11; ctx->ctxbufsize = alloc->size; - memset((void *)alloc->addr, 0, alloc->size); - - mfc_mem_cache_clean((void *)alloc->addr, alloc->size); - return 0; } @@ -101,7 +97,7 @@ int get_init_arg(struct mfc_inst_ctx *ctx, void *arg) write_reg((1 << 1) | 0x1, MFC_ENC_MSLICE_CTRL); if (init_arg->cmn.in_ms_arg < 1900) init_arg->cmn.in_ms_arg = 1900; - write_reg(init_arg->cmn.in_ms_arg, MFC_ENC_MSLICE_BIT); + write_reg(init_arg->cmn.in_ms_arg * 8, MFC_ENC_MSLICE_BIT); } else { write_reg(0, MFC_ENC_MSLICE_CTRL); write_reg(0, MFC_ENC_MSLICE_MB); @@ -586,8 +582,7 @@ static int h264_pre_seq_start(struct mfc_inst_ctx *ctx) if (h264->sps_pps_gen == 1) { write_shm(ctx, - ((h264->sps_pps_gen << 8) | - read_shm(ctx, EXT_ENC_CONTROL)), + ((h264->sps_pps_gen << 8) | read_shm(ctx, EXT_ENC_CONTROL)), EXT_ENC_CONTROL); } @@ -1066,10 +1061,8 @@ static int h264_set_codec_cfg(struct mfc_inst_ctx *ctx, int type, void *arg) case MFC_ENC_SETCONF_SPS_PPS_GEN: mfc_dbg("MFC_ENC_SETCONF_SPS_PPS_GEN : %d\n", ctx->state); - if ((ctx->state < INST_STATE_CREATE) || - (ctx->state > INST_STATE_EXE)) { - mfc_err("MFC_ENC_SETCONF_SPS_PPS_GEN : " - " state is invalid\n"); + if ((ctx->state < INST_STATE_CREATE) || (ctx->state > INST_STATE_EXE)) { + mfc_err("MFC_ENC_SETCONF_SPS_PPS_GEN : state is invalid\n"); return MFC_STATE_INVALID; } @@ -1079,6 +1072,7 @@ static int h264_set_codec_cfg(struct mfc_inst_ctx *ctx, int type, void *arg) h264->sps_pps_gen = 0; break; + default: mfc_dbg("invalid set cfg type: 0x%08x\n", type); ret = -2; @@ -1548,6 +1542,22 @@ int mfc_init_encoding(struct mfc_inst_ctx *ctx, union mfc_args *args) } #endif +#if defined(CONFIG_MACH_GC1) && defined(CONFIG_EXYNOS4_CPUFREQ) + if ((ctx->width >= 1280 && ctx->height >= 720) + || (ctx->width >= 720 && ctx->height >= 1280)) { + if (atomic_read(&ctx->dev->cpufreq_lock_cnt) == 0) { + if (0 == ctx->dev->cpufreq_level) /* 800MHz */ + exynos_cpufreq_get_level(800000, + &ctx->dev->cpufreq_level); + exynos_cpufreq_lock(DVFS_LOCK_ID_MFC, + ctx->dev->cpufreq_level); + mfc_dbg("[%s] CPU Freq Locked 800MHz!\n", __func__); + } + atomic_inc(&ctx->dev->cpufreq_lock_cnt); + ctx->cpufreq_flag = true; + } +#endif + #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_EXYNOS4_CPUFREQ) if ((ctx->width >= 320 && ctx->height >= 240) || (ctx->width >= 240 && ctx->height >= 320)) { diff --git a/drivers/media/video/samsung/mfc5x/mfc_inst.c b/drivers/media/video/samsung/mfc5x/mfc_inst.c index ef0d0e0..33736c9 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_inst.c +++ b/drivers/media/video/samsung/mfc5x/mfc_inst.c @@ -55,6 +55,9 @@ struct mfc_inst_ctx *mfc_create_inst(void) #ifdef CONFIG_BUSFREQ ctx->busfreq_flag = false; #endif +#if defined(CONFIG_MACH_GC1) && defined(CONFIG_EXYNOS4_CPUFREQ) + ctx->cpufreq_flag = false; +#endif #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_EXYNOS4_CPUFREQ) ctx->cpufreq_flag = false; #endif @@ -193,34 +196,34 @@ int mfc_set_inst_cfg(struct mfc_inst_ctx *ctx, int type, void *arg) } switch (type) { - case MFC_DEC_SETCONF_POST_ENABLE: - case MFC_DEC_SETCONF_EXTRA_BUFFER_NUM: - case MFC_DEC_SETCONF_DISPLAY_DELAY: - case MFC_DEC_SETCONF_IS_LAST_FRAME: - case MFC_DEC_SETCONF_SLICE_ENABLE: - case MFC_DEC_SETCONF_CRC_ENABLE: - case MFC_DEC_SETCONF_FIMV1_WIDTH_HEIGHT: - case MFC_DEC_SETCONF_FRAME_TAG: - case MFC_DEC_SETCONF_IMMEDIATELY_DISPLAY: - case MFC_DEC_SETCONF_DPB_FLUSH: - case MFC_DEC_SETCONF_SEI_PARSE: - case MFC_DEC_SETCONF_PIXEL_CACHE: - case MFC_ENC_SETCONF_FRAME_TYPE: - case MFC_ENC_SETCONF_CHANGE_FRAME_RATE: - case MFC_ENC_SETCONF_CHANGE_BIT_RATE: - case MFC_ENC_SETCONF_FRAME_TAG: - case MFC_ENC_SETCONF_ALLOW_FRAME_SKIP: - case MFC_ENC_SETCONF_VUI_INFO: - case MFC_ENC_SETCONF_I_PERIOD: - case MFC_ENC_SETCONF_HIER_P: - case MFC_ENC_SETCONF_SEI_GEN: - case MFC_ENC_SETCONF_FRAME_PACKING: - case MFC_ENC_SETCONF_SPS_PPS_GEN: - if (ctx->c_ops->set_codec_cfg) { - if ((ctx->c_ops->set_codec_cfg(ctx, type, arg)) < 0) - return MFC_SET_CONF_FAIL; - } - break; + case MFC_DEC_SETCONF_POST_ENABLE: + case MFC_DEC_SETCONF_EXTRA_BUFFER_NUM: + case MFC_DEC_SETCONF_DISPLAY_DELAY: + case MFC_DEC_SETCONF_IS_LAST_FRAME: + case MFC_DEC_SETCONF_SLICE_ENABLE: + case MFC_DEC_SETCONF_CRC_ENABLE: + case MFC_DEC_SETCONF_FIMV1_WIDTH_HEIGHT: + case MFC_DEC_SETCONF_FRAME_TAG: + case MFC_DEC_SETCONF_IMMEDIATELY_DISPLAY: + case MFC_DEC_SETCONF_DPB_FLUSH: + case MFC_DEC_SETCONF_SEI_PARSE: + case MFC_DEC_SETCONF_PIXEL_CACHE: + case MFC_ENC_SETCONF_FRAME_TYPE: + case MFC_ENC_SETCONF_CHANGE_FRAME_RATE: + case MFC_ENC_SETCONF_CHANGE_BIT_RATE: + case MFC_ENC_SETCONF_FRAME_TAG: + case MFC_ENC_SETCONF_ALLOW_FRAME_SKIP: + case MFC_ENC_SETCONF_VUI_INFO: + case MFC_ENC_SETCONF_I_PERIOD: + case MFC_ENC_SETCONF_HIER_P: + case MFC_ENC_SETCONF_SEI_GEN: + case MFC_ENC_SETCONF_FRAME_PACKING: + case MFC_ENC_SETCONF_SPS_PPS_GEN: + if (ctx->c_ops->set_codec_cfg) { + if ((ctx->c_ops->set_codec_cfg(ctx, type, arg)) < 0) + return MFC_SET_CONF_FAIL; + } + break; default: mfc_err("invalid set config type: 0x%08x\n", type); diff --git a/drivers/media/video/samsung/mfc5x/mfc_inst.h b/drivers/media/video/samsung/mfc5x/mfc_inst.h index b78dd91..eedd6f1 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_inst.h +++ b/drivers/media/video/samsung/mfc5x/mfc_inst.h @@ -158,6 +158,10 @@ struct mfc_inst_ctx { int busfreq_flag; /* context bus frequency flag */ #endif +#if defined(CONFIG_MACH_GC1) && defined(CONFIG_EXYNOS4_CPUFREQ) + int cpufreq_flag; /* context CPU frequency flag*/ +#endif + #if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_EXYNOS4_CPUFREQ) int cpufreq_flag; /* context CPU frequency flag*/ #endif diff --git a/drivers/media/video/samsung/mfc5x/mfc_mem.c b/drivers/media/video/samsung/mfc5x/mfc_mem.c index 051e4c0..a4f5345 100644 --- a/drivers/media/video/samsung/mfc5x/mfc_mem.c +++ b/drivers/media/video/samsung/mfc5x/mfc_mem.c @@ -551,12 +551,26 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) /* early allocator */ #if defined(CONFIG_S5P_MEM_CMA) #ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION -#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_M0) +#ifdef CONFIG_USE_MFC_CMA +#if defined(CONFIG_MACH_M0) cma_infos[0].lower_bound = 0x5C100000; cma_infos[0].upper_bound = 0x5F200000; cma_infos[0].total_size = 0x03100000; cma_infos[0].free_size = 0x03100000; cma_infos[0].count = 1; +#elif defined(CONFIG_MACH_GC1) || defined(CONFIG_MACH_GC2PD) + cma_infos[0].lower_bound = 0x50200000; + cma_infos[0].upper_bound = 0x53300000; + cma_infos[0].total_size = 0x03100000; + cma_infos[0].free_size = 0x03100000; + cma_infos[0].count = 1; +#elif defined(CONFIG_MACH_TAB3) || defined(CONFIG_MACH_ZEST) + cma_infos[0].lower_bound = 0x58100000; + cma_infos[0].upper_bound = 0x5B200000; + cma_infos[0].total_size = 0x03100000; + cma_infos[0].free_size = 0x03100000; + cma_infos[0].count = 1; +#endif #else if (cma_info(&cma_infos[0], dev->device, "A")) { mfc_info("failed to get CMA info of 'mfc-secure'\n"); @@ -611,8 +625,14 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) return -ENOMEM; } -#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_M0) +#ifdef CONFIG_USE_MFC_CMA +#if defined(CONFIG_MACH_GC1) || defined(CONFIG_MACH_GC2PD) + base[0] = 0x50200000; +#elif defined(CONFIG_MACH_TAB3) || defined(CONFIG_MACH_ZEST) + base[0] = 0x58100000; +#else base[0] = 0x5c100000; +#endif dev->mem_infos[0].base = base[0]; dev->mem_infos[0].size = size; dev->mem_infos[0].addr = phys_to_virt(base[0]); @@ -707,6 +727,20 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) dev->mem_infos[0].size = size; dev->mem_infos[0].addr = cma_get_virt(base[0], size, 0); } else if (dev->mem_ports == 2) { +#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_Q1_BD) + /* for MFC0:A */ + cma_infos[0].lower_bound = 0x67200000; + cma_infos[0].upper_bound = 0x68400000; + cma_infos[0].total_size = 0x01200000; + cma_infos[0].free_size = 0x01200000; + cma_infos[0].count = 1; + /* for MFC1:B */ + cma_infos[1].lower_bound = 0x68400000; + cma_infos[1].upper_bound = 0x6A000000; + cma_infos[1].total_size = 0x01C00000; + cma_infos[1].free_size = 0x01C00000; + cma_infos[1].count = 1; +#else if (cma_info(&cma_infos[0], dev->device, "A")) { mfc_info("failed to get CMA info of 'mfc0'\n"); return -ENOMEM; @@ -716,7 +750,7 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) mfc_info("failed to get CMA info of 'mfc1'\n"); return -ENOMEM; } - +#endif if (cma_infos[0].lower_bound > cma_infos[1].lower_bound) cma_index = 1; @@ -733,8 +767,12 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) base[0] = cma_alloc(dev->device, cma_index ? "B" : "A", MFC_FW_SYSTEM_SIZE, ALIGN_128KB); #else +#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_Q1_BD) + base[0] = cma_index ? 0x68400000 : 0x67200000; +#else base[0] = cma_alloc(dev->device, cma_index ? "B" : "A", size, ALIGN_128KB); #endif +#endif if (IS_ERR_VALUE(base[0])) { mfc_err("failed to get rsv. memory from CMA on port #0"); return -ENOMEM; @@ -742,7 +780,11 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) dev->mem_infos[0].base = base[0]; dev->mem_infos[0].size = size; +#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_Q1_BD) + dev->mem_infos[0].addr = phys_to_virt(base[0]); +#else dev->mem_infos[0].addr = cma_get_virt(base[0], size, 0); +#endif /* swap CMA index */ cma_index = !cma_index; @@ -760,8 +802,12 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) base[1] = cma_index ? cma_infos[1].lower_bound : cma_infos[0].lower_bound; #else +#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_Q1_BD) + base[1] = cma_index ? 0x68400000 : 0x67200000; +#else base[1] = cma_alloc(dev->device, cma_index ? "B" : "A", size, ALIGN_128KB); #endif +#endif if (IS_ERR_VALUE(base[1])) { mfc_err("failed to get rsv. memory from CMA on port #1"); cma_free(base[0]); @@ -770,7 +816,11 @@ int mfc_init_mem_mgr(struct mfc_dev *dev) dev->mem_infos[1].base = base[1]; dev->mem_infos[1].size = size; +#if defined(CONFIG_USE_MFC_CMA) && defined(CONFIG_MACH_Q1_BD) + dev->mem_infos[1].addr = phys_to_virt(base[1]); +#else dev->mem_infos[1].addr = cma_get_virt(base[1], size, 0); +#endif } else { mfc_err("failed to get reserved memory from CMA"); return -EPERM; -- cgit v1.1 From 3419d278c82828870f8f039ac429df389d0cb738 Mon Sep 17 00:00:00 2001 From: Wolfgang Wiedmeyer Date: Fri, 25 Mar 2016 22:27:31 +0100 Subject: i9300: disable samsung_extdisp driver and regenerate defconfig The samsung_extdisp driver for GT-I9500 was affected by CVE-2015-1800 and CVE-2015-1801. The Galaxy S3 uses an older version of this driver and is at least affected by CVE-2015-1801. Newer kernel versions for the GT-I9500 had the driver completely removed. I also found no indication that the driver is actually needed for the S3, so let's disable it. vulnerability disclosure and further information: http://blog.quarkslab.com/kernel-vulnerabilities-in-the-samsung-s4.html Change-Id: I7c2bfb4c2469a06302d92da3e224702942eae709 Signed-off-by: Wolfgang Wiedmeyer --- arch/arm/configs/cyanogenmod_i9300_defconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 5961c38..9d6f8b8 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2290,9 +2290,7 @@ CONFIG_FB_S5P_S6E8AA0=y CONFIG_S6E8AA0_AMS480GYXX=y CONFIG_AID_DIMMING=y CONFIG_LCD_FREQ_SWITCH=y -CONFIG_FB_S5P_EXTDSP=y -# CONFIG_FB_S5P_EXTDSP_DEBUG is not set -CONFIG_FB_S5P_EXTDSP_NR_BUFFERS=3 +# CONFIG_FB_S5P_EXTDSP is not set # CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD is not set # CONFIG_FB_S1D13XXX is not set # CONFIG_FB_TMIO is not set -- cgit v1.1 From 632dc60cfc82c6b7b5ba129875c5a4cd20564ddb Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 13 Feb 2016 02:34:52 +0000 Subject: pipe: Fix buffer offset after partially failed read Quoting the RHEL advisory: > It was found that the fix for CVE-2015-1805 incorrectly kept buffer > offset and buffer length in sync on a failed atomic read, potentially > resulting in a pipe buffer state corruption. A local, unprivileged user > could use this flaw to crash the system or leak kernel memory to user > space. (CVE-2016-0774, Moderate) The same flawed fix was applied to stable branches from 2.6.32.y to 3.14.y inclusive, and I was able to reproduce the issue on 3.2.y. We need to give pipe_iov_copy_to_user() a separate offset variable and only update the buffer offset if it succeeds. Change-Id: I988802f38acf40c7671fa0978880928b02d29b56 References: https://rhn.redhat.com/errata/RHSA-2016-0103.html Signed-off-by: Ben Hutchings (cherry picked from commit feae3ca2e5e1a8f44aa6290255d3d9709985d0b2) --- fs/pipe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/pipe.c b/fs/pipe.c index 6060d2f..a36a4d3 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -389,6 +389,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, void *addr; size_t chars = buf->len, remaining; int error, atomic; + int offset; if (chars > total_len) chars = total_len; @@ -402,9 +403,10 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, atomic = !iov_fault_in_pages_write(iov, chars); remaining = chars; + offset = buf->offset; redo: addr = ops->map(pipe, buf, atomic); - error = pipe_iov_copy_to_user(iov, addr, &buf->offset, + error = pipe_iov_copy_to_user(iov, addr, &offset, &remaining, atomic); ops->unmap(pipe, buf, addr); if (unlikely(error)) { @@ -420,6 +422,7 @@ redo: break; } ret += chars; + buf->offset += chars; buf->len -= chars; /* Was it a packet buffer? Clean up and exit */ -- cgit v1.1 From d074a87602640643f766fdcc2990b47dd97a8413 Mon Sep 17 00:00:00 2001 From: Arnab Chaudhuri Date: Mon, 18 Apr 2016 01:10:58 +0530 Subject: i9100: Enable Zcache Zcache doubles RAM efficiency while providing a significant performance boosts on many workloads. Zcache uses compression and an in-kernel implementation of transcendent memory to store clean page cache pages and swap in RAM, providing a noticeable reduction in disk I/O. Change-Id: If62338ff5c3f37ef2dfdcd5f7f5f180cd870225a --- arch/arm/configs/cyanogenmod_i9100_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index 56c3eff..c56b778 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -2692,8 +2692,9 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_IIO is not set CONFIG_XVMALLOC=y CONFIG_ZRAM=y +CONFIG_ZCACHE=y # CONFIG_ZRAM_DEBUG is not set -# CONFIG_ZRAM_FOR_ANDROID is not set +CONFIG_ZRAM_FOR_ANDROID=y # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set -- cgit v1.1 From 466285b156cbcb88adba11d15b16c63d4bc0e395 Mon Sep 17 00:00:00 2001 From: rogersb11 Date: Sun, 1 May 2016 15:39:22 +0200 Subject: n7100: Enable UID_CPUTIME and profiling Change-Id: I365695fdfb02ba131fdf6b8c5ff177a5c0e9a5f4 --- arch/arm/configs/cyanogenmod_n7100_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_n7100_defconfig b/arch/arm/configs/cyanogenmod_n7100_defconfig index 9b0dd73..2172e19 100644 --- a/arch/arm/configs/cyanogenmod_n7100_defconfig +++ b/arch/arm/configs/cyanogenmod_n7100_defconfig @@ -145,7 +145,7 @@ CONFIG_COMPAT_BRK=y # CONFIG_SLAB is not set CONFIG_SLUB=y # CONFIG_SLOB is not set -# CONFIG_PROFILING is not set +CONFIG_PROFILING=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -3107,6 +3107,7 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y +CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y -- cgit v1.1 From 52e6d89350a3723bd516046303d101aebc36c798 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Sun, 1 May 2016 15:48:15 +0200 Subject: n7100: enable pegasusq boosting Change-Id: I12179c8ff2d4b9e7195ddf0a69fffeb3d2b7bf9d --- arch/arm/configs/cyanogenmod_n7100_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/cyanogenmod_n7100_defconfig b/arch/arm/configs/cyanogenmod_n7100_defconfig index 2172e19..eb68010 100644 --- a/arch/arm/configs/cyanogenmod_n7100_defconfig +++ b/arch/arm/configs/cyanogenmod_n7100_defconfig @@ -734,6 +734,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST=y # CONFIG_CPU_FREQ_GOV_SLP is not set # CONFIG_CPU_FREQ_DVFS_MONITOR is not set CONFIG_CPU_IDLE=y -- cgit v1.1 From 33d997e867fb6b5f27bbab34daa2759d9e560201 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Sat, 13 Feb 2016 11:08:06 +0300 Subject: ALSA: usb-audio: avoid freeing umidi object twice The 'umidi' object will be free'd on the error path by snd_usbmidi_free() when tearing down the rawmidi interface. So we shouldn't try to free it in snd_usbmidi_create() after having registered the rawmidi interface. Found by KASAN. Change-Id: I628889be11651c1e6b90d5c61d041328124deae3 Signed-off-by: Andrey Konovalov Acked-by: Clemens Ladisch Cc: Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index c635a2d..9e864e9 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2213,7 +2213,6 @@ int snd_usbmidi_create(struct snd_card *card, else err = snd_usbmidi_create_endpoints(umidi, endpoints); if (err < 0) { - snd_usbmidi_free(umidi); return err; } -- cgit v1.1 From 30decc8a8e13959e2db6bd58628e38de1733c5fb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 13 Jan 2016 21:35:06 +0100 Subject: ALSA: timer: Fix double unlink of active_list ALSA timer instance object has a couple of linked lists and they are unlinked unconditionally at snd_timer_stop(). Meanwhile snd_timer_interrupt() unlinks it, but it calls list_del() which leaves the element list itself unchanged. This ends up with unlinking twice, and it was caught by syzkaller fuzzer. The fix is to use list_del_init() variant properly there, too. Change-Id: I95e2ab06180dfe43fb6b7c2875a866b53ca245ce Reported-by: Dmitry Vyukov Tested-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 950eed0..e8a12f3 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -689,7 +689,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) } else { ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; if (--timer->running) - list_del(&ti->active_list); + list_del_init(&ti->active_list); } if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || (ti->flags & SNDRV_TIMER_IFLG_FAST)) -- cgit v1.1 From fcc7980348b57d7d18f4ca486ed8d36a9ada3665 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 13 Jan 2016 17:48:01 +0100 Subject: ALSA: timer: Fix race among timer ioctls ALSA timer ioctls have an open race and this may lead to a use-after-free of timer instance object. A simplistic fix is to make each ioctl exclusive. We have already tread_sem for controlling the tread, and extend this as a global mutex to be applied to each ioctl. The downside is, of course, the worse concurrency. But these ioctls aren't to be parallel accessible, in anyway, so it should be fine to serialize there. Change-Id: Iaa21b00f62e02cc58e346a29846e0fce6536e860 Reported-by: Dmitry Vyukov Tested-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/timer.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index e8a12f3..eb2da96 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -72,7 +72,7 @@ struct snd_timer_user { struct timespec tstamp; /* trigger tstamp */ wait_queue_head_t qchange_sleep; struct fasync_struct *fasync; - struct mutex tread_sem; + struct mutex ioctl_lock; }; /* list of timers */ @@ -1252,7 +1252,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file) return -ENOMEM; spin_lock_init(&tu->qlock); init_waitqueue_head(&tu->qchange_sleep); - mutex_init(&tu->tread_sem); + mutex_init(&tu->ioctl_lock); tu->ticks = 1; tu->queue_size = 128; tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), @@ -1272,8 +1272,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) if (file->private_data) { tu = file->private_data; file->private_data = NULL; + mutex_lock(&tu->ioctl_lock); if (tu->timeri) snd_timer_close(tu->timeri); + mutex_unlock(&tu->ioctl_lock); kfree(tu->queue); kfree(tu->tqueue); kfree(tu); @@ -1511,7 +1513,6 @@ static int snd_timer_user_tselect(struct file *file, int err = 0; tu = file->private_data; - mutex_lock(&tu->tread_sem); if (tu->timeri) { snd_timer_close(tu->timeri); tu->timeri = NULL; @@ -1555,7 +1556,6 @@ static int snd_timer_user_tselect(struct file *file, } __err: - mutex_unlock(&tu->tread_sem); return err; } @@ -1768,7 +1768,7 @@ enum { SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), }; -static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, +static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_timer_user *tu; @@ -1785,17 +1785,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, { int xarg; - mutex_lock(&tu->tread_sem); - if (tu->timeri) { /* too late */ - mutex_unlock(&tu->tread_sem); + if (tu->timeri) /* too late */ return -EBUSY; - } - if (get_user(xarg, p)) { - mutex_unlock(&tu->tread_sem); + if (get_user(xarg, p)) return -EFAULT; - } tu->tread = xarg ? 1 : 0; - mutex_unlock(&tu->tread_sem); return 0; } case SNDRV_TIMER_IOCTL_GINFO: @@ -1828,6 +1822,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, return -ENOTTY; } +static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct snd_timer_user *tu = file->private_data; + long ret; + + mutex_lock(&tu->ioctl_lock); + ret = __snd_timer_user_ioctl(file, cmd, arg); + mutex_unlock(&tu->ioctl_lock); + return ret; +} + static int snd_timer_user_fasync(int fd, struct file * file, int on) { struct snd_timer_user *tu; -- cgit v1.1 From 046fdefc1dfbbc9f34b569ae77b3258c4eb588a5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Jan 2016 16:30:58 +0100 Subject: ALSA: timer: Harden slave timer list handling A slave timer instance might be still accessible in a racy way while operating the master instance as it lacks of locking. Since the master operation is mostly protected with timer->lock, we should cope with it while changing the slave instance, too. Also, some linked lists (active_list and ack_list) of slave instances aren't unlinked immediately at stopping or closing, and this may lead to unexpected accesses. This patch tries to address these issues. It adds spin lock of timer->lock (either from master or slave, which is equivalent) in a few places. For avoiding a deadlock, we ensure that the global slave_active_lock is always locked at first before each timer lock. Also, ack and active_list of slave instances are properly unlinked at snd_timer_stop() and snd_timer_close(). Last but not least, remove the superfluous call of _snd_timer_stop() at removing slave links. This is a noop, and calling it may confuse readers wrt locking. Further cleanup will follow in a later patch. Actually we've got reports of use-after-free by syzkaller fuzzer, and this hopefully fixes these issues. Change-Id: I572878b909dda522dbedc84633414185802bc974 Reported-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/timer.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index eb2da96..9eb25d4 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -214,11 +214,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master) slave->slave_id == master->slave_id) { list_move_tail(&slave->open_list, &master->slave_list_head); spin_lock_irq(&slave_active_lock); + spin_lock(&master->timer->lock); slave->master = master; slave->timer = master->timer; if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) list_add_tail(&slave->active_list, &master->slave_active_head); + spin_unlock(&master->timer->lock); spin_unlock_irq(&slave_active_lock); } } @@ -342,15 +344,18 @@ int snd_timer_close(struct snd_timer_instance *timeri) timer->hw.close) timer->hw.close(timer); /* remove slave links */ + spin_lock_irq(&slave_active_lock); + spin_lock(&timer->lock); list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) { - spin_lock_irq(&slave_active_lock); - _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION); list_move_tail(&slave->open_list, &snd_timer_slave_list); slave->master = NULL; slave->timer = NULL; - spin_unlock_irq(&slave_active_lock); + list_del_init(&slave->ack_list); + list_del_init(&slave->active_list); } + spin_unlock(&timer->lock); + spin_unlock_irq(&slave_active_lock); mutex_unlock(®ister_mutex); } if (timeri->private_free) @@ -436,9 +441,12 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri) spin_lock_irqsave(&slave_active_lock, flags); timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; - if (timeri->master) + if (timeri->master && timeri->timer) { + spin_lock(&timeri->timer->lock); list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); + spin_unlock(&timeri->timer->lock); + } spin_unlock_irqrestore(&slave_active_lock, flags); return 1; /* delayed start */ } @@ -484,6 +492,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri, if (!keep_flag) { spin_lock_irqsave(&slave_active_lock, flags); timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&timeri->ack_list); + list_del_init(&timeri->active_list); spin_unlock_irqrestore(&slave_active_lock, flags); } goto __end; -- cgit v1.1 From af47328e8d37683c911edf5703cdc45d8047490e Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 18 Jan 2016 16:36:09 +0100 Subject: pipe: limit the per-user amount of pages allocated in pipes On no-so-small systems, it is possible for a single process to cause an OOM condition by filling large pipes with data that are never read. A typical process filling 4000 pipes with 1 MB of data will use 4 GB of memory. On small systems it may be tricky to set the pipe max size to prevent this from happening. This patch makes it possible to enforce a per-user soft limit above which new pipes will be limited to a single page, effectively limiting them to 4 kB each, as well as a hard limit above which no new pipes may be created for this user. This has the effect of protecting the system against memory abuse without hurting other users, and still allowing pipes to work correctly though with less data at once. The limit are controlled by two new sysctls : pipe-user-pages-soft, and pipe-user-pages-hard. Both may be disabled by setting them to zero. The default soft limit allows the default number of FDs per process (1024) to create pipes of the default size (64kB), thus reaching a limit of 64MB before starting to create only smaller pipes. With 256 processes limited to 1024 FDs each, this results in 1024*64kB + (256*1024 - 1024) * 4kB = 1084 MB of memory allocated for a user. The hard limit is disabled by default to avoid breaking existing applications that make intensive use of pipes (eg: for splicing). Reported-by: socketpair@gmail.com Reported-by: Tetsuo Handa Mitigates: CVE-2013-4312 (Linux 2.0+) Suggested-by: Linus Torvalds Signed-off-by: Willy Tarreau Signed-off-by: Al Viro Conflicts: Documentation/sysctl/fs.txt fs/pipe.c include/linux/sched.h Change-Id: Ic7c678af18129943e16715fdaa64a97a7f0854be --- Documentation/sysctl/fs.txt | 23 ++++++++++++++++++++++ fs/pipe.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/pipe_fs_i.h | 4 ++++ include/linux/sched.h | 1 + kernel/sysctl.c | 14 ++++++++++++++ 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt index 88fd7f5..9330fba 100644 --- a/Documentation/sysctl/fs.txt +++ b/Documentation/sysctl/fs.txt @@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs: - nr_open - overflowuid - overflowgid +- pipe-user-pages-hard +- pipe-user-pages-soft - suid_dumpable - super-max - super-nr @@ -157,6 +159,27 @@ The default is 65534. ============================================================== +pipe-user-pages-hard: + +Maximum total number of pages a non-privileged user may allocate for pipes. +Once this limit is reached, no new pipes may be allocated until usage goes +below the limit again. When set to 0, no limit is applied, which is the default +setting. + +============================================================== + +pipe-user-pages-soft: + +Maximum total number of pages a non-privileged user may allocate for pipes +before the pipe size gets limited to a single page. Once this limit is reached, +new pipes will be limited to a single page in size for this user in order to +limit total memory usage, and trying to increase them using fcntl() will be +denied until usage goes below the limit again. The default value allows to +allocate up to 1024 pipes at their default size. When set to 0, no limit is +applied. + +============================================================== + suid_dumpable: This value can be used to query and set the core dump mode for setuid diff --git a/fs/pipe.c b/fs/pipe.c index a36a4d3..35bca6b 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -35,6 +35,12 @@ unsigned int pipe_max_size = 1048576; */ unsigned int pipe_min_size = PAGE_SIZE; +/* Maximum allocatable pages per user. Hard limit is unset by default, soft + * matches default values. + */ +unsigned long pipe_user_pages_hard; +unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; + /* * We use a start+len construction, which provides full use of the * allocated memory. @@ -932,20 +938,49 @@ const struct file_operations rdwr_pipefifo_fops = { .fasync = pipe_rdwr_fasync, }; +static void account_pipe_buffers(struct pipe_inode_info *pipe, + unsigned long old, unsigned long new) +{ + atomic_long_add(new - old, &pipe->user->pipe_bufs); +} + +static bool too_many_pipe_buffers_soft(struct user_struct *user) +{ + return pipe_user_pages_soft && + atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; +} + +static bool too_many_pipe_buffers_hard(struct user_struct *user) +{ + return pipe_user_pages_hard && + atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; +} + struct pipe_inode_info * alloc_pipe_info(struct inode *inode) { struct pipe_inode_info *pipe; pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); if (pipe) { - pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL); + unsigned long pipe_bufs = PIPE_DEF_BUFFERS; + struct user_struct *user = get_current_user(); + + if (!too_many_pipe_buffers_hard(user)) { + if (too_many_pipe_buffers_soft(user)) + pipe_bufs = 1; + pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL); + } + if (pipe->bufs) { init_waitqueue_head(&pipe->wait); pipe->r_counter = pipe->w_counter = 1; pipe->inode = inode; - pipe->buffers = PIPE_DEF_BUFFERS; + pipe->buffers = pipe_bufs; + pipe->user = user; + account_pipe_buffers(pipe, 0, pipe_bufs); return pipe; } + free_uid(user); kfree(pipe); } @@ -956,6 +991,8 @@ void __free_pipe_info(struct pipe_inode_info *pipe) { int i; + account_pipe_buffers(pipe, pipe->buffers, 0); + free_uid(pipe->user); for (i = 0; i < pipe->buffers; i++) { struct pipe_buffer *buf = pipe->bufs + i; if (buf->ops) @@ -1204,6 +1241,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); } + account_pipe_buffers(pipe, pipe->buffers, nr_pages); pipe->curbuf = 0; kfree(pipe->bufs); pipe->bufs = bufs; @@ -1277,6 +1315,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { ret = -EPERM; goto out; + } else if ((too_many_pipe_buffers_hard(pipe->user) || + too_many_pipe_buffers_soft(pipe->user)) && + !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto out; } ret = pipe_set_size(pipe, nr_pages); break; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 0072a53..1aefc3f 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -43,6 +43,7 @@ struct pipe_buffer { * @fasync_writers: writer side fasync * @inode: inode this pipe is attached to * @bufs: the circular array of pipe buffers + * @user: the user who created this pipe **/ struct pipe_inode_info { wait_queue_head_t wait; @@ -57,6 +58,7 @@ struct pipe_inode_info { struct fasync_struct *fasync_writers; struct inode *inode; struct pipe_buffer *bufs; + struct user_struct *user; }; /* @@ -142,6 +144,8 @@ void pipe_unlock(struct pipe_inode_info *); void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); extern unsigned int pipe_max_size, pipe_min_size; +extern unsigned long pipe_user_pages_hard; +extern unsigned long pipe_user_pages_soft; int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); diff --git a/include/linux/sched.h b/include/linux/sched.h index 8ff666c..ad2f17f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -705,6 +705,7 @@ struct user_struct { unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */ #endif unsigned long locked_shm; /* How many pages of mlocked shm ? */ + atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ #ifdef CONFIG_KEYS struct key *uid_keyring; /* UID specific keyring */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 5b6afb2..3cd04f1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1515,6 +1515,20 @@ static struct ctl_table fs_table[] = { .proc_handler = &pipe_proc_fn, .extra1 = &pipe_min_size, }, + { + .procname = "pipe-user-pages-hard", + .data = &pipe_user_pages_hard, + .maxlen = sizeof(pipe_user_pages_hard), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + }, + { + .procname = "pipe-user-pages-soft", + .data = &pipe_user_pages_soft, + .maxlen = sizeof(pipe_user_pages_soft), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + }, { } }; -- cgit v1.1 From 342ec72c6123f81c16863ba25d57dcf0328104d5 Mon Sep 17 00:00:00 2001 From: RGIB Date: Tue, 3 May 2016 20:55:31 +0200 Subject: smdk4412 : fix screen flickering on n51xx Change-Id: I47c9bd6a6ad8903f8e87828226ee8f98e4eafbd0 --- drivers/gpu/mali400/r3p2/ump/Kbuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/mali400/r3p2/ump/Kbuild b/drivers/gpu/mali400/r3p2/ump/Kbuild index f6bf61e..4ca1aae 100644 --- a/drivers/gpu/mali400/r3p2/ump/Kbuild +++ b/drivers/gpu/mali400/r3p2/ump/Kbuild @@ -37,7 +37,7 @@ ccflags-y += -I$(srctree)/$(src) -I$(srctree)/$(src)/common -I$(srctree)/$(src)/ # MALI_SEC ccflags-y += -I$(srctree)/$(src)/include -ccflags-y += -DUSING_MEMORY=1 -DUMP_MEM_SIZE=1024 +ccflags-y += -DUSING_MEMORY=1 -DUMP_MEM_SIZE=512 ccflags-y += -DMALI_STATE_TRACKING=0 ccflags-$(CONFIG_UMP_DEBUG) += -DDEBUG -- cgit v1.1 From afe9fd7ee230ab733e6de47f6acfc822f2472ec1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jan 2016 13:52:47 +0100 Subject: ALSA: hrtimer: Fix stall by hrtimer_cancel() hrtimer_cancel() waits for the completion from the callback, thus it must not be called inside the callback itself. This was already a problem in the past with ALSA hrtimer driver, and the early commit [fcfdebe70759: ALSA: hrtimer - Fix lock-up] tried to address it. However, the previous fix is still insufficient: it may still cause a lockup when the ALSA timer instance reprograms itself in its callback. Then it invokes the start function even in snd_timer_interrupt() that is called in hrtimer callback itself, results in a CPU stall. This is no hypothetical problem but actually triggered by syzkaller fuzzer. This patch tries to fix the issue again. Now we call hrtimer_try_to_cancel() at both start and stop functions so that it won't fall into a deadlock, yet giving some chance to cancel the queue if the functions have been called outside the callback. The proper hrtimer_cancel() is called in anyway at closing, so this should be enough. Change-Id: Id6224b2a3ade0d217e891e6af09744df4d0b2e5c Reported-and-tested-by: Dmitry Vyukov Cc: Signed-off-by: Takashi Iwai --- sound/core/hrtimer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index b8b31c4..14d483d 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -90,7 +90,7 @@ static int snd_hrtimer_start(struct snd_timer *t) struct snd_hrtimer *stime = t->private_data; atomic_set(&stime->running, 0); - hrtimer_cancel(&stime->hrt); + hrtimer_try_to_cancel(&stime->hrt); hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), HRTIMER_MODE_REL); atomic_set(&stime->running, 1); @@ -101,6 +101,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) { struct snd_hrtimer *stime = t->private_data; atomic_set(&stime->running, 0); + hrtimer_try_to_cancel(&stime->hrt); return 0; } -- cgit v1.1 From fc6dcc59053f65282c7945499349bdbd154e5409 Mon Sep 17 00:00:00 2001 From: Roberto Gibellini Date: Thu, 5 May 2016 04:43:45 -0700 Subject: Revert "smdk4412 : fix screen flickering on n51xx" This reverts commit 342ec72c6123f81c16863ba25d57dcf0328104d5. Change-Id: I15da0e18a6b40bdf46e30f1632dfd899d13c6d75 --- drivers/gpu/mali400/r3p2/ump/Kbuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/mali400/r3p2/ump/Kbuild b/drivers/gpu/mali400/r3p2/ump/Kbuild index 4ca1aae..f6bf61e 100644 --- a/drivers/gpu/mali400/r3p2/ump/Kbuild +++ b/drivers/gpu/mali400/r3p2/ump/Kbuild @@ -37,7 +37,7 @@ ccflags-y += -I$(srctree)/$(src) -I$(srctree)/$(src)/common -I$(srctree)/$(src)/ # MALI_SEC ccflags-y += -I$(srctree)/$(src)/include -ccflags-y += -DUSING_MEMORY=1 -DUMP_MEM_SIZE=512 +ccflags-y += -DUSING_MEMORY=1 -DUMP_MEM_SIZE=1024 ccflags-y += -DMALI_STATE_TRACKING=0 ccflags-$(CONFIG_UMP_DEBUG) += -DDEBUG -- cgit v1.1 From 02ce1dffb72e5f2edd51d556acb7a38ceb44ab4a Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 7 Mar 2016 11:31:10 +0100 Subject: usbnet: cleanup after bind() in probe() In case bind() works, but a later error forces bailing in probe() in error cases work and a timer may be scheduled. They must be killed. This fixes an error case related to the double free reported in http://www.spinics.net/lists/netdev/msg367669.html and needs to go on top of Linus' fix to cdc-ncm. Change-Id: I43b1673bc31b3af05789e461b39c55062735cc56 Signed-off-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index bc2e489..460979d 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1523,6 +1523,13 @@ out3: if (info->unbind) info->unbind (dev, udev); out1: + /* subdrivers must undo all they did in bind() if they + * fail it, but we may fail later and a deferred kevent + * may trigger an error resubmitting itself and, worse, + * schedule a timer. So we kill it all just in case. + */ + cancel_work_sync(&dev->kevent); + del_timer_sync(&dev->delay); free_netdev(net); out: usb_put_dev(xdev); -- cgit v1.1 From b8be5e6f4034e82c271939333b7314ba27c3085e Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 15 Mar 2016 10:14:04 +0100 Subject: USB: cdc-acm: more sanity checking An attack has become available which pretends to be a quirky device circumventing normal sanity checks and crashes the kernel by an insufficient number of interfaces. This patch adds a check to the code path for quirky devices. Change-Id: Ie96a95d833e4ca9c3c3c3557679115ffb7069b5b Signed-off-by: Oliver Neukum CC: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 84e69ea..c8fee56 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -895,6 +895,9 @@ static int acm_probe(struct usb_interface *intf, if (quirks == NO_UNION_NORMAL) { data_interface = usb_ifnum_to_if(usb_dev, 1); control_interface = usb_ifnum_to_if(usb_dev, 0); + /* we would crash */ + if (!data_interface || !control_interface) + return -ENODEV; goto skip_normal_probe; } -- cgit v1.1 From e4941d685b43583bda1d8cc2c8a9662492adcedb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 22 Mar 2016 18:02:52 +0100 Subject: netfilter: x_tables: fix unconditional helper Ben Hawkes says: In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it is possible for a user-supplied ipt_entry structure to have a large next_offset field. This field is not bounds checked prior to writing a counter value at the supplied offset. Problem is that mark_source_chains should not have been called -- the rule doesn't have a next entry, so its supposed to return an absolute verdict of either ACCEPT or DROP. However, the function conditional() doesn't work as the name implies. It only checks that the rule is using wildcard address matching. However, an unconditional rule must also not be using any matches (no -m args). The underflow validator only checked the addresses, therefore passing the 'unconditional absolute verdict' test, while mark_source_chains also tested for presence of matches, and thus proceeeded to the next (not-existent) rule. Unify this so that all the callers have same idea of 'unconditional rule'. Change-Id: I82cf878cc77aa1b65ce492c8f12bd5c93a4d084e Reported-by: Ben Hawkes Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/arp_tables.c | 18 +++++++++--------- net/ipv4/netfilter/ip_tables.c | 23 +++++++++++------------ net/ipv6/netfilter/ip6_tables.c | 23 +++++++++++------------ 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index fd7a3f6..1d6f562 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -350,11 +350,12 @@ unsigned int arpt_do_table(struct sk_buff *skb, } /* All zeroes == unconditional rule. */ -static inline bool unconditional(const struct arpt_arp *arp) +static inline bool unconditional(const struct arpt_entry *e) { static const struct arpt_arp uncond; - return memcmp(arp, &uncond, sizeof(uncond)) == 0; + return e->target_offset == sizeof(struct arpt_entry) && + memcmp(&e->arp, &uncond, sizeof(uncond)) == 0; } /* Figures out from what hook each rule can be called: returns 0 if @@ -393,11 +394,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo, |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS)); /* Unconditional return/END. */ - if ((e->target_offset == sizeof(struct arpt_entry) && + if ((unconditional(e) && (strcmp(t->target.u.user.name, XT_STANDARD_TARGET) == 0) && - t->verdict < 0 && unconditional(&e->arp)) || - visited) { + t->verdict < 0) || visited) { unsigned int oldpos, size; if ((strcmp(t->target.u.user.name, @@ -542,7 +542,7 @@ static bool check_underflow(const struct arpt_entry *e) const struct xt_entry_target *t; unsigned int verdict; - if (!unconditional(&e->arp)) + if (!unconditional(e)) return false; t = arpt_get_target_c(e); if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) @@ -583,9 +583,9 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) { if (!check_underflow(e)) { - pr_err("Underflows must be unconditional and " - "use the STANDARD target with " - "ACCEPT/DROP\n"); + pr_debug("Underflows must be unconditional and " + "use the STANDARD target with " + "ACCEPT/DROP\n"); return -EINVAL; } newinfo->underflow[h] = underflows[h]; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 24e556e..795af4e 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int offset) /* All zeroes == unconditional rule. */ /* Mildly perf critical (only if packet tracing is on) */ -static inline bool unconditional(const struct ipt_ip *ip) +static inline bool unconditional(const struct ipt_entry *e) { static const struct ipt_ip uncond; - return memcmp(ip, &uncond, sizeof(uncond)) == 0; + return e->target_offset == sizeof(struct ipt_entry) && + memcmp(&e->ip, &uncond, sizeof(uncond)) == 0; #undef FWINV } @@ -230,11 +231,10 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e, } else if (s == e) { (*rulenum)++; - if (s->target_offset == sizeof(struct ipt_entry) && + if (unconditional(s) && strcmp(t->target.u.kernel.target->name, XT_STANDARD_TARGET) == 0 && - t->verdict < 0 && - unconditional(&s->ip)) { + t->verdict < 0) { /* Tail of chains: STANDARD target (return/policy) */ *comment = *chainname == hookname ? comments[NF_IP_TRACE_COMMENT_POLICY] @@ -468,11 +468,10 @@ mark_source_chains(const struct xt_table_info *newinfo, e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS)); /* Unconditional return/END. */ - if ((e->target_offset == sizeof(struct ipt_entry) && + if ((unconditional(e) && (strcmp(t->target.u.user.name, XT_STANDARD_TARGET) == 0) && - t->verdict < 0 && unconditional(&e->ip)) || - visited) { + t->verdict < 0) || visited) { unsigned int oldpos, size; if ((strcmp(t->target.u.user.name, @@ -705,7 +704,7 @@ static bool check_underflow(const struct ipt_entry *e) const struct xt_entry_target *t; unsigned int verdict; - if (!unconditional(&e->ip)) + if (!unconditional(e)) return false; t = ipt_get_target_c(e); if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) @@ -747,9 +746,9 @@ check_entry_size_and_hooks(struct ipt_entry *e, newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) { if (!check_underflow(e)) { - pr_err("Underflows must be unconditional and " - "use the STANDARD target with " - "ACCEPT/DROP\n"); + pr_debug("Underflows must be unconditional and " + "use the STANDARD target with " + "ACCEPT/DROP\n"); return -EINVAL; } newinfo->underflow[h] = underflows[h]; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 14cb310..959cc3f 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -208,11 +208,12 @@ get_entry(const void *base, unsigned int offset) /* All zeroes == unconditional rule. */ /* Mildly perf critical (only if packet tracing is on) */ -static inline bool unconditional(const struct ip6t_ip6 *ipv6) +static inline bool unconditional(const struct ip6t_entry *e) { static const struct ip6t_ip6 uncond; - return memcmp(ipv6, &uncond, sizeof(uncond)) == 0; + return e->target_offset == sizeof(struct ip6t_entry) && + memcmp(&e->ipv6, &uncond, sizeof(uncond)) == 0; } static inline const struct xt_entry_target * @@ -269,11 +270,10 @@ get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e, } else if (s == e) { (*rulenum)++; - if (s->target_offset == sizeof(struct ip6t_entry) && + if (unconditional(s) && strcmp(t->target.u.kernel.target->name, XT_STANDARD_TARGET) == 0 && - t->verdict < 0 && - unconditional(&s->ipv6)) { + t->verdict < 0) { /* Tail of chains: STANDARD target (return/policy) */ *comment = *chainname == hookname ? comments[NF_IP6_TRACE_COMMENT_POLICY] @@ -490,11 +490,10 @@ mark_source_chains(const struct xt_table_info *newinfo, e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS)); /* Unconditional return/END. */ - if ((e->target_offset == sizeof(struct ip6t_entry) && + if ((unconditional(e) && (strcmp(t->target.u.user.name, XT_STANDARD_TARGET) == 0) && - t->verdict < 0 && - unconditional(&e->ipv6)) || visited) { + t->verdict < 0) || visited) { unsigned int oldpos, size; if ((strcmp(t->target.u.user.name, @@ -728,7 +727,7 @@ static bool check_underflow(const struct ip6t_entry *e) const struct xt_entry_target *t; unsigned int verdict; - if (!unconditional(&e->ipv6)) + if (!unconditional(e)) return false; t = ip6t_get_target_c(e); if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) @@ -770,9 +769,9 @@ check_entry_size_and_hooks(struct ip6t_entry *e, newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) { if (!check_underflow(e)) { - pr_err("Underflows must be unconditional and " - "use the STANDARD target with " - "ACCEPT/DROP\n"); + pr_debug("Underflows must be unconditional and " + "use the STANDARD target with " + "ACCEPT/DROP\n"); return -EINVAL; } newinfo->underflow[h] = underflows[h]; -- cgit v1.1 From f3bad54455dde6aad5ec62bbb115be8120173f7e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 13 Mar 2016 23:28:00 -0400 Subject: ipv4: Don't do expensive useless work during inetdev destroy. commit fbd40ea0180a2d328c5adc61414dc8bab9335ce2 upstream. When an inetdev is destroyed, every address assigned to the interface is removed. And in this scenerio we do two pointless things which can be very expensive if the number of assigned interfaces is large: 1) Address promotion. We are deleting all addresses, so there is no point in doing this. 2) A full nf conntrack table purge for every address. We only need to do this once, as is already caught by the existing masq_dev_notifier so masq_inet_event() can skip this. Change-Id: I4b2a3ed665543728451c21465fb90ec89f739135 Reported-by: Solar Designer Signed-off-by: David S. Miller Tested-by: Cyrill Gorcunov [bwh: Backported to 3.2: adjust filename, context] Signed-off-by: Ben Hutchings --- net/ipv4/devinet.c | 4 ++++ net/ipv4/fib_frontend.c | 4 ++++ net/ipv4/netfilter/ipt_MASQUERADE.c | 12 ++++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 76db592..085d63f 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -327,6 +327,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, ASSERT_RTNL(); + if (in_dev->dead) + goto no_promotions; + /* 1. Deleting primary ifaddr forces deletion all secondaries * unless alias promotion is set **/ @@ -373,6 +376,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, fib_del_ifaddr(ifa, ifa1); } +no_promotions: /* 2. Unlink it */ *ifap = ifa1->ifa_next; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index a54817a..f7b8dbe 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -765,6 +765,9 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) subnet = 1; } + if (in_dev->dead) + goto no_promotions; + /* Deletion is more complicated than add. * We should take care of not to delete too much :-) * @@ -840,6 +843,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) } } +no_promotions: if (!(ok & BRD_OK)) fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim); if (subnet && ifa->ifa_prefixlen < 31) { diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 9931152..9a30807 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -123,8 +123,16 @@ static int masq_inet_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; - return masq_device_event(this, event, dev); + struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev; + /* The masq_dev_notifier will catch the case of the device going + * down. So if the inetdev is dead and being destroyed we have + * no work to do. Otherwise this is an individual address removal + * and we have to perform the flush. + */ + if (idev->dead) + return NOTIFY_DONE; + + return masq_device_event(this, event, idev->dev); } static struct notifier_block masq_dev_notifier = { -- cgit v1.1 From dbabc204f2217b654d806a42163b46a6e77f0e7c Mon Sep 17 00:00:00 2001 From: Arnab Chaudhuri Date: Wed, 4 May 2016 15:40:00 +0530 Subject: cypress: Port BLN for smdk4210 BLN code originally by neldar. Adapted for SGSII by creams. Ported by gokhanmoral. Change-Id: I3e51026a9057be47586378994f61719177507b84 --- arch/arm/mach-exynos/mach-u1.c | 14 +- drivers/input/keyboard/Kconfig | 2 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/cypressbln/Kconfig | 16 + drivers/input/keyboard/cypressbln/Makefile | 5 + .../input/keyboard/cypressbln/cypress-touchkey.c | 2466 ++++++++++++++++++++ drivers/input/keyboard/cypressbln/issp_defs.h | 101 + drivers/input/keyboard/cypressbln/issp_delays.h | 87 + .../input/keyboard/cypressbln/issp_directives.h | 474 ++++ .../keyboard/cypressbln/issp_driver_routines.c | 498 ++++ drivers/input/keyboard/cypressbln/issp_errors.h | 65 + drivers/input/keyboard/cypressbln/issp_extern.h | 99 + drivers/input/keyboard/cypressbln/issp_main.c | 931 ++++++++ drivers/input/keyboard/cypressbln/issp_revision.h | 65 + drivers/input/keyboard/cypressbln/issp_routines.c | 1044 +++++++++ drivers/input/keyboard/cypressbln/issp_vectors.h | 1146 +++++++++ drivers/input/keyboard/cypressbln/touchkey_fw_M0.h | 747 ++++++ drivers/input/keyboard/cypressbln/touchkey_fw_NA.h | 747 ++++++ .../input/keyboard/cypressbln/touchkey_fw_NAATT.h | 747 ++++++ .../input/keyboard/cypressbln/touchkey_fw_NTT.h | 685 ++++++ drivers/input/keyboard/cypressbln/touchkey_fw_Q1.h | 747 ++++++ drivers/input/keyboard/cypressbln/touchkey_fw_T0.h | 770 ++++++ drivers/input/keyboard/cypressbln/touchkey_fw_U1.h | 747 ++++++ .../input/keyboard/cypressbln/u1-cypress-gpio.h | 28 + drivers/input/touchscreen/mxt224_u1.c | 7 + 25 files changed, 12234 insertions(+), 5 deletions(-) create mode 100644 drivers/input/keyboard/cypressbln/Kconfig create mode 100644 drivers/input/keyboard/cypressbln/Makefile create mode 100644 drivers/input/keyboard/cypressbln/cypress-touchkey.c create mode 100644 drivers/input/keyboard/cypressbln/issp_defs.h create mode 100644 drivers/input/keyboard/cypressbln/issp_delays.h create mode 100644 drivers/input/keyboard/cypressbln/issp_directives.h create mode 100644 drivers/input/keyboard/cypressbln/issp_driver_routines.c create mode 100644 drivers/input/keyboard/cypressbln/issp_errors.h create mode 100644 drivers/input/keyboard/cypressbln/issp_extern.h create mode 100644 drivers/input/keyboard/cypressbln/issp_main.c create mode 100644 drivers/input/keyboard/cypressbln/issp_revision.h create mode 100644 drivers/input/keyboard/cypressbln/issp_routines.c create mode 100644 drivers/input/keyboard/cypressbln/issp_vectors.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_M0.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_NA.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_NAATT.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_NTT.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_Q1.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_T0.h create mode 100644 drivers/input/keyboard/cypressbln/touchkey_fw_U1.h create mode 100644 drivers/input/keyboard/cypressbln/u1-cypress-gpio.h diff --git a/arch/arm/mach-exynos/mach-u1.c b/arch/arm/mach-exynos/mach-u1.c index ab756ab..5d5183c 100644 --- a/arch/arm/mach-exynos/mach-u1.c +++ b/arch/arm/mach-exynos/mach-u1.c @@ -198,7 +198,7 @@ static struct wacom_g5_callbacks *wacom_callbacks; #endif /* CONFIG_EPEN_WACOM_G5SP */ -#ifdef CONFIG_KEYBOARD_CYPRESS_TOUCH +#if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH) || defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) #include #endif @@ -3095,7 +3095,11 @@ REGULATOR_INIT(ldo17, "VTF_2.8V", 2800000, 2800000, 0, REGULATOR_INIT(ldo18, "TOUCH_LED_3.3V", 3300000, 3300000, 0, REGULATOR_CHANGE_STATUS, 1); #else +#if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) && defined(CONFIG_TOUCHKEY_BLN) +REGULATOR_INIT(ldo18, "TOUCH_LED_3.3V", 2500000, 3300000, 0, +#else REGULATOR_INIT(ldo18, "TOUCH_LED_3.3V", 3000000, 3300000, 0, +#endif REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE, 1); #endif REGULATOR_INIT(ldo21, "VDDQ_M1M2_1.2V", 1200000, 1200000, 1, @@ -6194,7 +6198,7 @@ static void p6_wacom_register_callbacks(struct wacom_g5_callbacks *cb) #ifdef CONFIG_S3C_DEV_I2C8_EMUL static struct i2c_board_info i2c_devs8_emul[]; #endif -#ifdef CONFIG_KEYBOARD_CYPRESS_TOUCH +#if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH) || defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) static void touchkey_init_hw(void) { gpio_request(GPIO_3_TOUCH_INT, "3_TOUCH_INT"); @@ -6295,7 +6299,7 @@ static struct touchkey_platform_data touchkey_pdata = { .power_on = touchkey_power_on, .led_power_on = touchkey_led_power_on, }; -#endif /*CONFIG_KEYBOARD_CYPRESS_TOUCH*/ +#endif /*(CONFIG_KEYBOARD_CYPRESS_TOUCH) || (CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN)*/ @@ -6448,7 +6452,7 @@ struct platform_device s3c_device_i2c8 = { /* I2C8 */ static struct i2c_board_info i2c_devs8_emul[] = { -#ifdef CONFIG_KEYBOARD_CYPRESS_TOUCH +#if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH) || defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) { I2C_BOARD_INFO("sec_touchkey", 0x20), .platform_data = &touchkey_pdata, @@ -7983,7 +7987,7 @@ static void __init smdkc210_machine_init(void) ARRAY_SIZE(tuna_i2c15_boardinfo)); #endif #ifdef CONFIG_S3C_DEV_I2C8_EMUL -#ifdef CONFIG_KEYBOARD_CYPRESS_TOUCH +#if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH) || defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) touchkey_init_hw(); #endif i2c_register_board_info(8, i2c_devs8_emul, ARRAY_SIZE(i2c_devs8_emul)); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 61e680e..83820c0 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -570,4 +570,6 @@ config SENSORS_HALL source "drivers/input/keyboard/cypress/Kconfig" +source "drivers/input/keyboard/cypressbln/Kconfig" + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 9ac639a..5664d66 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o obj-$(CONFIG_KEYBOARD_CYPRESS_TOUCH) += cypress/ +obj-$(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN)+= cypressbln/ ifeq ($(TARGET_BUILD_VARIANT), eng) KBUILD_CFLAGS += -DSEC_TKEY_EVENT_DEBUG diff --git a/drivers/input/keyboard/cypressbln/Kconfig b/drivers/input/keyboard/cypressbln/Kconfig new file mode 100644 index 0000000..e1c0a8a --- /dev/null +++ b/drivers/input/keyboard/cypressbln/Kconfig @@ -0,0 +1,16 @@ +# +# Cypress touchkey configuration +# + +config KEYBOARD_CYPRESS_TOUCH_BLN + tristate "Cypress touchkey support" + help + Say Y here to enable the cypress touchkey with BLN support. + + To compile this driver as a module, choose M here. + +config TOUCHKEY_BLN + bool "BLN Support" + depends on KEYBOARD_CYPRESS_TOUCH_BLN + help + BLN support. diff --git a/drivers/input/keyboard/cypressbln/Makefile b/drivers/input/keyboard/cypressbln/Makefile new file mode 100644 index 0000000..a50c126 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Cypress touchkey driver +# + +obj-$(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) += cypress-touchkey.o issp_driver_routines.o issp_main.o issp_routines.o diff --git a/drivers/input/keyboard/cypressbln/cypress-touchkey.c b/drivers/input/keyboard/cypressbln/cypress-touchkey.c new file mode 100644 index 0000000..f06eca5 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/cypress-touchkey.c @@ -0,0 +1,2466 @@ +/* + * Driver for keys on GPIO lines capable of generating interrupts. + * + * Copyright 2005 Phil Blundell + * + * 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. + * + * BLN code originally by neldar. Adapted for SGSII by creams. Ported + * by gokhanmoral. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "u1-cypress-gpio.h" + +#include +#include +#include +#include +#include + +#include "issp_extern.h" +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT540E +#include +#else +#include +#endif + +/* +touchkey register +*/ +#define KEYCODE_REG 0x00 +#define FIRMWARE_VERSION 0x01 +#define TOUCHKEY_MODULE_VERSION 0x02 +#define TOUCHKEY_ADDRESS 0x20 + +#define UPDOWN_EVENT_BIT 0x08 +#define KEYCODE_BIT 0x07 + +#define I2C_M_WR 0 /* for i2c */ + +#define DEVICE_NAME "sec_touchkey" +#define TOUCH_FIRMWARE_V04 0x04 +#define TOUCH_FIRMWARE_V07 0x07 +#define DOOSUNGTECH_TOUCH_V1_2 0x0C + +#if defined(CONFIG_MACH_Q1_BD) +#define TK_FIRMWARE_VER 0x12 +#define TK_MODULE_VER 0x11 +#elif defined(CONFIG_MACH_C1_NA_USCC_REV05) +#define TK_FIRMWARE_VER 0x0E +#define TK_MODULE_VER 0x08 +#else +#define TK_FIRMWARE_VER 0x04 +#define TK_MODULE_VER 0x00 +#endif + +/* + * Standard CM7 LED Notification functionality. + */ +#include + +#define BL_STANDARD 3000 + +int notification_timeout = -1; +int led_timeout; + +static DEFINE_SEMAPHORE(enable_sem); + +static struct timer_list breathing_timer; +static void breathe(struct work_struct *breathe_work); +static DECLARE_WORK(breathe_work, breathe); +//breathing variables +#define MAX_BREATHING_STEPS 10 +static unsigned int breathing = 0; +static int breathing_step_count = 0; +struct breathing_step { + int start; //mV + int end; //mV + int period; //ms + int step; //mV +}; +struct breathing_step breathing_steps[MAX_BREATHING_STEPS]; +static int breathing_idx = 0; +static int breathing_step_idx = 0; + + +static unsigned int touchkey_voltage = 3000; + +#if defined(CONFIG_TARGET_LOCALE_NAATT_TEMP) +/* Temp Fix NAGSM_SEL_ANDROID_MOHAMMAD_ANSARI_20111224*/ +#define CONFIG_TARGET_LOCALE_NAATT +#endif + +#if defined(CONFIG_TARGET_LOCALE_NAATT) +static int touchkey_keycode[5] = { 0, + KEY_MENU, KEY_ENTER, KEY_BACK, KEY_END }; +#elif defined(CONFIG_TARGET_LOCALE_NA) +static int touchkey_keycode[5] = { NULL, + KEY_SEARCH, KEY_BACK, KEY_HOME, KEY_MENU }; +#else +static int touchkey_keycode[3] = { 0, KEY_MENU, KEY_BACK }; +#endif +/* timer related declares */ +static struct timer_list led_timer; +static void bl_off(struct work_struct *bl_off_work); +static DECLARE_WORK(bl_off_work, bl_off); +static struct timer_list notification_timer; +static void notification_off(struct work_struct *notification_off_work); +static DECLARE_WORK(notification_off_work, notification_off); +static const int touchkey_count = sizeof(touchkey_keycode) / sizeof(int); + +#if defined(CONFIG_TARGET_LOCALE_NAATT)\ + || defined(CONFIG_TARGET_LOCALE_NA)\ + || defined(CONFIG_MACH_Q1_BD) + +static u8 home_sensitivity; +static u8 search_sensitivity; +static u16 raw_data0; +static u16 raw_data1; +static u16 raw_data2; +static u16 raw_data3; +static u8 idac0; +static u8 idac1; +static u8 idac2; +static u8 idac3; +static u8 touchkey_threshold; + +static int touchkey_autocalibration(void); +#endif +static int get_touchkey_module_version(void); + +static u8 menu_sensitivity; +static u8 back_sensitivity; + +static int touchkey_enable; +static bool touchkey_probe = true; + +struct device *sec_touchkey; + +#ifdef CONFIG_TOUCHKEY_BLN +#include +#include +#define BLN_VERSION 9 + +bool bln_enabled = false; +bool BLN_ongoing = false; +bool bln_blink_enabled = false; +bool bln_suspended = false; + +static void enable_led_notification(void); +static void disable_led_notification(void); + +static struct wake_lock bln_wake_lock; +#endif + +struct i2c_touchkey_driver { + struct i2c_client *client; + struct input_dev *input_dev; + struct early_suspend early_suspend; +}; +struct i2c_touchkey_driver *touchkey_driver; +struct work_struct touchkey_work; +struct workqueue_struct *touchkey_wq; + +struct work_struct touch_update_work; +struct delayed_work touch_resume_work; + +#ifdef WHY_DO_WE_NEED_THIS +static void __iomem *gpio_pend_mask_mem; +#define INT_PEND_BASE 0xE0200A54 +#endif + +static const struct i2c_device_id sec_touchkey_id[] = { + {"sec_touchkey", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, sec_touchkey_id); + +static void init_hw(void); +static int i2c_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +extern int get_touchkey_firmware(char *version); +static int touchkey_led_status; +static int touchled_cmd_reversed; + +struct i2c_driver touchkey_i2c_driver = { + .driver = { + .name = "sec_touchkey_driver", + }, + .id_table = sec_touchkey_id, + .probe = i2c_touchkey_probe, +}; + +static int touchkey_debug_count; +static char touchkey_debug[104]; +static int touch_version; +static int module_version; +#ifdef CONFIG_TARGET_LOCALE_NA +static int store_module_version; +#endif + +static int touchkey_update_status; + +int touchkey_led_ldo_on(bool on) +{ + struct regulator *regulator; + +#if defined(CONFIG_MACH_S2PLUS) + if (on) { + gpio_direction_output(GPIO_3_TOUCH_EN, 1); + } else { + gpio_direction_output(GPIO_3_TOUCH_EN, 0); + } +#else + if (on) { + regulator = regulator_get(NULL, "touch_led"); + if (IS_ERR(regulator)) + return 0; + regulator_enable(regulator); + regulator_put(regulator); + } else { + regulator = regulator_get(NULL, "touch_led"); + if (IS_ERR(regulator)) + return 0; + if (regulator_is_enabled(regulator)) + regulator_force_disable(regulator); + regulator_put(regulator); + } +#endif + return 0; +} + +int touchkey_ldo_on(bool on) +{ + struct regulator *regulator; + +#if defined(CONFIG_MACH_S2PLUS) + if (on) { + regulator = regulator_get(NULL, "3_touch_1.8v"); + if (IS_ERR(regulator)) + return 0; + regulator_enable(regulator); + regulator_put(regulator); + } else { + regulator = regulator_get(NULL, "3_touch_1.8v"); + if (IS_ERR(regulator)) + return 0; + if (regulator_is_enabled(regulator)) + regulator_force_disable(regulator); + regulator_put(regulator); + } +#else + if (on) { + regulator = regulator_get(NULL, "touch"); + if (IS_ERR(regulator)) + return 0; + regulator_enable(regulator); + regulator_put(regulator); + } else { + regulator = regulator_get(NULL, "touch"); + if (IS_ERR(regulator)) + return 0; + if (regulator_is_enabled(regulator)) + regulator_force_disable(regulator); + regulator_put(regulator); + } +#endif + + return 1; +} + +static void change_touch_key_led_voltage(int vol_mv) +{ + struct regulator *tled_regulator; + + tled_regulator = regulator_get(NULL, "touch_led"); + if (IS_ERR(tled_regulator)) { + pr_err("%s: failed to get resource %s\n", __func__, + "touch_led"); + return; + } + regulator_set_voltage(tled_regulator, vol_mv * 1000, vol_mv * 1000); + regulator_put(tled_regulator); +} + +static ssize_t brightness_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int data; + + if (sscanf(buf, "%d\n", &data) == 1) { + printk(KERN_ERR "[TouchKey] touch_led_brightness: %d\n", data); + change_touch_key_led_voltage(data); + touchkey_voltage = data; + } else { + printk(KERN_ERR "[TouchKey] touch_led_brightness Error\n"); + } + + return size; +} + +void stop_breathing(void) +{ + del_timer(&breathing_timer); + change_touch_key_led_voltage(touchkey_voltage); +} + +static void set_touchkey_debug(char value) +{ + if (touchkey_debug_count == 100) + touchkey_debug_count = 0; + + touchkey_debug[touchkey_debug_count] = value; + touchkey_debug_count++; +} + +static int i2c_touchkey_read(u8 reg, u8 *val, unsigned int len) +{ + int err = 0; + int retry = 2; + struct i2c_msg msg[1]; + + if ((touchkey_driver == NULL) || !(touchkey_enable == 1) + || !touchkey_probe) { + printk(KERN_ERR "[TouchKey] touchkey is not enabled. %d\n", + __LINE__); + return -ENODEV; + } + + while (retry--) { + msg->addr = touchkey_driver->client->addr; + msg->flags = I2C_M_RD; + msg->len = len; + msg->buf = val; + err = i2c_transfer(touchkey_driver->client->adapter, msg, 1); + + if (err >= 0) + return 0; + printk(KERN_ERR "[TouchKey] %s %d i2c transfer error\n", + __func__, __LINE__); + mdelay(10); + } + return err; + +} + +static int i2c_touchkey_write(u8 *val, unsigned int len) +{ + int err = 0; + struct i2c_msg msg[1]; + int retry = 2; + + if ((touchkey_driver == NULL) || !(touchkey_enable == 1) + || !touchkey_probe) { + printk(KERN_ERR "[TouchKey] touchkey is not enabled. %d\n", + __LINE__); + return -ENODEV; + } + + while (retry--) { + msg->addr = touchkey_driver->client->addr; + msg->flags = I2C_M_WR; + msg->len = len; + msg->buf = val; + err = i2c_transfer(touchkey_driver->client->adapter, msg, 1); + + if (err >= 0) + return 0; + + printk(KERN_DEBUG "[TouchKey] %s %d i2c transfer error\n", + __func__, __LINE__); + mdelay(10); + } + return err; +} + +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) +static int touchkey_autocalibration(void) +{ + u8 data[6] = { 0, }; + int count = 0; + int ret = 0; + unsigned short retry = 0; + + while (retry < 3) { + ret = i2c_touchkey_read(KEYCODE_REG, data, 4); + if (ret < 0) { + printk(KERN_ERR "[TouchKey]i2c read fail.\n"); + return ret; + } + printk(KERN_DEBUG + "[TouchKey] %s : data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", + __func__, data[0], data[1], data[2], data[3]); + + /* Send autocal Command */ + data[0] = 0x50; + data[3] = 0x01; + + count = i2c_touchkey_write(data, 4); + + msleep(100); + + /* Check autocal status */ + ret = i2c_touchkey_read(KEYCODE_REG, data, 6); + + if ((data[5] & 0x80)) { + printk(KERN_DEBUG "[Touchkey] autocal Enabled\n"); + break; + } else + printk(KERN_DEBUG + "[Touchkey] autocal disabled, retry %d\n", + retry); + + retry = retry + 1; + } + + if (retry == 3) + printk(KERN_DEBUG "[Touchkey] autocal failed\n"); + + return count; +} +#endif + +#ifdef CONFIG_TARGET_LOCALE_NAATT +static ssize_t set_touchkey_autocal_testmode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int count = 0; + u8 set_data; + int on_off; + + if (sscanf(buf, "%d\n", &on_off) == 1) { + printk(KERN_ERR "[TouchKey] Test Mode : %d\n", on_off); + + if (on_off == 1) { + set_data = 0x40; + count = i2c_touchkey_write(&set_data, 1); + } else { + touchkey_ldo_on(0); + msleep(50); + touchkey_ldo_on(1); + msleep(50); + init_hw(); + msleep(50); +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) + touchkey_autocalibration(); +#endif + } + } else { + printk(KERN_ERR "[TouchKey] touch_led_brightness Error\n"); + } + + return count; +} +#endif + +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) +static ssize_t touchkey_raw_data0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[26] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 26); +#if defined(CONFIG_TARGET_LOCALE_NA) + printk(KERN_DEBUG "called %s data[18] =%d,data[19] = %d\n", __func__, + data[18], data[19]); + raw_data0 = ((0x00FF & data[18]) << 8) | data[19]; +#elif defined(CONFIG_MACH_Q1_BD) + printk(KERN_DEBUG "called %s data[16] =%d,data[17] = %d\n", __func__, + data[16], data[17]); + raw_data0 = ((0x00FF & data[14]) << 8) | data[15]; +#else + printk(KERN_DEBUG "called %s data[18] =%d,data[19] = %d\n", __func__, + data[10], data[11]); + raw_data0 = ((0x00FF & data[10]) << 8) | data[11]; +#endif + return sprintf(buf, "%d\n", raw_data0); +} + +static ssize_t touchkey_raw_data1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[26] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 26); +#if defined(CONFIG_TARGET_LOCALE_NA) + printk(KERN_DEBUG "called %s data[20] =%d,data[21] = %d\n", __func__, + data[20], data[21]); + raw_data1 = ((0x00FF & data[20]) << 8) | data[21]; +#elif defined(CONFIG_MACH_Q1_BD) + printk(KERN_DEBUG "called %s data[14] =%d,data[15] = %d\n", __func__, + data[14], data[15]); + raw_data1 = ((0x00FF & data[16]) << 8) | data[17]; +#else + printk(KERN_DEBUG "called %s data[20] =%d,data[21] = %d\n", __func__, + data[12], data[13]); + raw_data1 = ((0x00FF & data[12]) << 8) | data[13]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", raw_data1); +} + +static ssize_t touchkey_raw_data2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[26] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 26); +#if defined(CONFIG_TARGET_LOCALE_NA) + printk(KERN_DEBUG "called %s data[22] =%d,data[23] = %d\n", __func__, + data[22], data[23]); + raw_data2 = ((0x00FF & data[22]) << 8) | data[23]; +#else + printk(KERN_DEBUG "called %s data[22] =%d,data[23] = %d\n", __func__, + data[14], data[15]); + raw_data2 = ((0x00FF & data[14]) << 8) | data[15]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", raw_data2); +} + +static ssize_t touchkey_raw_data3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[26] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 26); +#if defined(CONFIG_TARGET_LOCALE_NA) + printk(KERN_DEBUG "called %s data[24] =%d,data[25] = %d\n", __func__, + data[24], data[25]); + raw_data3 = ((0x00FF & data[24]) << 8) | data[25]; +#else + printk(KERN_DEBUG "called %s data[24] =%d,data[25] = %d\n", __func__, + data[16], data[17]); + raw_data3 = ((0x00FF & data[16]) << 8) | data[17]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", raw_data3); +} + +static ssize_t touchkey_idac0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[10]; + int ret; +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) + return 0; +#endif + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + printk(KERN_DEBUG "called %s data[6] =%d\n", __func__, data[6]); + idac0 = data[6]; + return sprintf(buf, "%d\n", idac0); +} + +static ssize_t touchkey_idac1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[10]; + int ret; +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) + return 0; +#endif + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + printk(KERN_DEBUG "called %s data[7] = %d\n", __func__, data[7]); + idac1 = data[7]; + return sprintf(buf, "%d\n", idac1); +} + +static ssize_t touchkey_idac2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[10]; + int ret; +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) + return 0; +#endif + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + printk(KERN_DEBUG "called %s data[8] =%d\n", __func__, data[8]); + idac2 = data[8]; + return sprintf(buf, "%d\n", idac2); +} + +static ssize_t touchkey_idac3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[10]; + int ret; +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) + return 0; +#endif + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + printk(KERN_DEBUG "called %s data[9] = %d\n", __func__, data[9]); + idac3 = data[9]; + return sprintf(buf, "%d\n", idac3); +} + +static ssize_t touchkey_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[10]; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + printk(KERN_DEBUG "called %s data[4] = %d\n", __func__, data[4]); + touchkey_threshold = data[4]; + return sprintf(buf, "%d\n", touchkey_threshold); +} +#endif + +#if defined(CONFIG_MACH_C1_NA_SPR_EPIC2_REV00) \ + || defined(CONFIG_MACH_Q1_BD) \ + || defined(CONFIG_MACH_C1_NA_USCC_REV05) \ + || defined(CONFIG_TARGET_LOCALE_NA) +void touchkey_firmware_update(void) +{ + char data[3]; + int retry = 3; + int ret = 0; + + ret = i2c_touchkey_read(KEYCODE_REG, data, 3); + if (ret < 0) { + printk(KERN_DEBUG + "[TouchKey] i2c read fail. do not excute firm update.\n"); + return; + } + + touch_version = data[1]; + module_version = data[2]; + +#ifdef CONFIG_MACH_C1_NA_SPR_EPIC2_REV00 + if (system_rev > 6) { + printk(KERN_DEBUG "[TouchKey] not firmup hw(system_rev=%d)\n", + system_rev); + return; + } +#endif + + if ((touch_version < TK_FIRMWARE_VER) && + (module_version == TK_MODULE_VER)) { + printk(KERN_DEBUG "[TouchKey] firmware auto update excute\n"); + disable_irq(IRQ_TOUCH_INT); + touchkey_update_status = 1; + + while (retry--) { + if (ISSP_main() == 0) { + printk(KERN_DEBUG + "[TouchKey]firmware update succeeded\n"); + touchkey_update_status = 0; + break; + } + msleep(100); + printk(KERN_DEBUG + "[TouchKey] firmware update failed. retry\n"); + } + if (retry <= 0) { + touchkey_ldo_on(0); + touchkey_update_status = -1; + printk(KERN_DEBUG + "[TouchKey] firmware update failed.\n"); + msleep(300); + } + enable_irq(IRQ_TOUCH_INT); + init_hw(); + } else { + printk(KERN_DEBUG + "[TouchKey] firmware auto update do not excute\n"); + printk(KERN_DEBUG + "[TouchKey] firmware_ver(banary=%d, current=%d)\n", + TK_FIRMWARE_VER, touch_version); + printk(KERN_DEBUG + "[TouchKey] module_ver(banary=%d, current=%d)\n", + TK_MODULE_VER, module_version); + return; + } + msleep(100); + i2c_touchkey_read(KEYCODE_REG, data, 3); + touch_version = data[1]; + module_version = data[2]; + printk(KERN_DEBUG "[TouchKey] firm ver = %d, module ver = %d\n", + touch_version, module_version); +} +#else +void touchkey_firmware_update(void) +{ + char data[3]; + int retry; + int ret = 0; + + ret = i2c_touchkey_read(KEYCODE_REG, data, 3); + if (ret < 0) { + printk(KERN_DEBUG + "[TouchKey] i2c read fail. do not excute firm update.\n"); + return; + } + + printk(KERN_ERR "%s F/W version: 0x%x, Module version:0x%x\n", __func__, + data[1], data[2]); + retry = 3; + + touch_version = data[1]; + module_version = data[2]; + + if (touch_version < 0x0A) { + touchkey_update_status = 1; + while (retry--) { + if (ISSP_main() == 0) { + printk(KERN_ERR + "[TOUCHKEY]Touchkey_update succeeded\n"); + touchkey_update_status = 0; + break; + } + printk(KERN_ERR "touchkey_update failed...retry...\n"); + } + if (retry <= 0) { + touchkey_ldo_on(0); + touchkey_update_status = -1; + msleep(300); + } + + init_hw(); + } else { + if (touch_version >= 0x0A) { + printk(KERN_ERR + "[TouchKey] Not F/W update. Cypess touch-key F/W version is latest\n"); + } else { + printk(KERN_ERR + "[TouchKey] Not F/W update. Cypess touch-key version(module or F/W) is not valid\n"); + } + } +} +#endif + +#ifndef TEST_JIG_MODE +void touchkey_work_func(struct work_struct *p) +{ + u8 data[3]; + int ret; + int retry = 10; + int keycode_type = 0; + int pressed; + int status; + + set_touchkey_debug('a'); + + retry = 3; + while (retry--) { + ret = i2c_touchkey_read(KEYCODE_REG, data, 3); + if (!ret) + break; + else { + printk(KERN_DEBUG + "[TouchKey] i2c read failed, ret:%d, retry: %d\n", + ret, retry); + continue; + } + } + if (ret < 0) { + enable_irq(IRQ_TOUCH_INT); + return; + } + set_touchkey_debug(data[0]); + + keycode_type = (data[0] & KEYCODE_BIT); + pressed = !(data[0] & UPDOWN_EVENT_BIT); + + if (keycode_type <= 0 || keycode_type >= touchkey_count) { + printk(KERN_DEBUG "[Touchkey] keycode_type err\n"); + enable_irq(IRQ_TOUCH_INT); + return; + } + + if (pressed) + set_touchkey_debug('P'); + + if (get_tsp_status() && pressed) + printk(KERN_DEBUG "[TouchKey] touchkey pressed but don't send event because touch is pressed.\n"); + else { + input_report_key(touchkey_driver->input_dev, + touchkey_keycode[keycode_type], pressed); + input_sync(touchkey_driver->input_dev); + /* printk(KERN_DEBUG "[TouchKey] keycode:%d pressed:%d\n", + touchkey_keycode[keycode_index], pressed); */ + } + + /* we have timed out or the lights should be on */ + if (led_timeout > 0) { + status = 1; + i2c_touchkey_write((u8 *)&status, 1); /* turn on */ + } + + /* restart the timer */ + if (led_timeout > 0) { + mod_timer(&led_timer, jiffies + msecs_to_jiffies(led_timeout)); + } + + set_touchkey_debug('A'); + enable_irq(IRQ_TOUCH_INT); +} +#else +void touchkey_work_func(struct work_struct *p) +{ + u8 data[18]; + int ret; + int retry = 10; + int keycode_type = 0; + int pressed; + +#if 0 + if (gpio_get_value(_3_GPIO_TOUCH_INT)) { + printk(KERN_DEBUG "[TouchKey] Unknown state.\n", __func__); + enable_irq(IRQ_TOUCH_INT); + return; + } +#endif + + set_touchkey_debug('a'); + +#ifdef CONFIG_CPU_FREQ + /* set_dvfs_target_level(LEV_800MHZ); */ +#endif + + retry = 3; + while (retry--) { +#if defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) + ret = i2c_touchkey_read(KEYCODE_REG, data, 18); +#else + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); +#endif + if (!ret) + break; + else { + printk(KERN_DEBUG + "[TouchKey] i2c read failed, ret:%d, retry: %d\n", + ret, retry); + continue; + } + } + if (ret < 0) { + enable_irq(IRQ_TOUCH_INT); + return; + } +#if defined(CONFIG_TARGET_LOCALE_NA) +#if defined(CONFIG_MACH_C1_NA_SPR_EPIC2_REV00) + menu_sensitivity = data[11]; + home_sensitivity = data[13]; + search_sensitivity = data[15]; + back_sensitivity = data[17]; +#else + if (store_module_version >= 8) { + menu_sensitivity = data[17]; + home_sensitivity = data[15]; + search_sensitivity = data[11]; + back_sensitivity = data[13]; + } else { + menu_sensitivity = data[6]; + home_sensitivity = data[7]; + search_sensitivity = data[8]; + back_sensitivity = data[9]; + } +#endif +#elif defined(CONFIG_MACH_Q1_BD) + menu_sensitivity = data[13]; + back_sensitivity = data[11]; +#else + menu_sensitivity = data[7]; + back_sensitivity = data[9]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + + set_touchkey_debug(data[0]); + + keycode_type = (data[0] & KEYCODE_BIT); + pressed = !(data[0] & UPDOWN_EVENT_BIT); + + if (keycode_type <= 0 || keycode_type >= touchkey_count) { + printk(KERN_DEBUG "[Touchkey] keycode_type err\n"); + enable_irq(IRQ_TOUCH_INT); + return; + } + + if (pressed) + set_touchkey_debug('P'); + + if (get_tsp_status() && pressed) + printk(KERN_DEBUG "[TouchKey] touchkey pressed" + " but don't send event because touch is pressed.\n"); + else { + input_report_key(touchkey_driver->input_dev, + touchkey_keycode[keycode_type], pressed); + input_sync(touchkey_driver->input_dev); + /* printk(KERN_DEBUG "[TouchKey] keycode:%d pressed:%d\n", + touchkey_keycode[keycode_index], pressed); */ + } + + if (keycode_type == 1) + printk(KERN_DEBUG "search key sensitivity = %d\n", + search_sensitivity); + if (keycode_type == 2) + printk(KERN_DEBUG "back key sensitivity = %d\n", + back_sensitivity); +#ifdef CONFIG_TARGET_LOCALE_NA + if (keycode_type == 3) + printk(KERN_DEBUG "home key sensitivity = %d\n", + home_sensitivity); + if (keycode_type == 4) + printk(KERN_DEBUG "menu key sensitivity = %d\n", + menu_sensitivity); +#endif + +#ifdef WHY_DO_WE_NEED_THIS + /* clear interrupt */ + if (readl(gpio_pend_mask_mem) & (0x1 << 1)) { + writel(readl(gpio_pend_mask_mem) | (0x1 << 1), + gpio_pend_mask_mem); + } +#endif + set_touchkey_debug('A'); + enable_irq(IRQ_TOUCH_INT); +} +#endif + +static irqreturn_t touchkey_interrupt(int irq, void *dummy) +{ +#ifdef CONFIG_TOUCHKEY_BLN + printk(KERN_ERR "[TouchKey] interrupt touchkey\n"); +#endif + set_touchkey_debug('I'); + disable_irq_nosync(IRQ_TOUCH_INT); + queue_work(touchkey_wq, &touchkey_work); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static int sec_touchkey_early_suspend(struct early_suspend *h) +{ + int ret; + int i; + + disable_irq(IRQ_TOUCH_INT); + ret = cancel_work_sync(&touchkey_work); + if (ret) { + printk(KERN_DEBUG "[Touchkey] enable_irq ret=%d\n", ret); + enable_irq(IRQ_TOUCH_INT); + } + + /* release keys */ + for (i = 1; i < touchkey_count; ++i) { + input_report_key(touchkey_driver->input_dev, + touchkey_keycode[i], 0); + } + + touchkey_enable = 0; + set_touchkey_debug('S'); + printk(KERN_DEBUG "[TouchKey] sec_touchkey_early_suspend\n"); + if (touchkey_enable < 0) { + printk(KERN_DEBUG "[TouchKey] ---%s---touchkey_enable: %d\n", + __func__, touchkey_enable); + return 0; + } + + gpio_direction_input(_3_GPIO_TOUCH_INT); +#if 0 + gpio_direction_output(_3_GPIO_TOUCH_EN, 0); + gpio_direction_output(_3_TOUCH_SDA_28V, 0); + gpio_direction_output(_3_TOUCH_SCL_28V, 0); + s3c_gpio_setpull(_3_GPIO_TOUCH_INT, S3C_GPIO_PULL_DOWN); +#endif + + /* disable ldo18 */ + touchkey_led_ldo_on(0); + + /* disable ldo11 */ + touchkey_ldo_on(0); + + bln_suspended = 1; + return 0; +} + +static int sec_touchkey_late_resume(struct early_suspend *h) +{ +#ifdef TEST_JIG_MODE + unsigned char get_touch = 0x40; +#endif + int status; + + set_touchkey_debug('R'); + printk(KERN_DEBUG "[TouchKey] sec_touchkey_late_resume\n"); + + /* Avoid race condition with LED notification disable */ + down(&enable_sem); + + /* enable ldo11 */ + touchkey_ldo_on(1); + + if (touchkey_enable < 0) { + printk(KERN_DEBUG "[TouchKey] ---%s---touchkey_enable: %d\n", + __func__, touchkey_enable); + return 0; + } + gpio_direction_output(_3_GPIO_TOUCH_EN, 1); + gpio_direction_output(_3_TOUCH_SDA_28V, 1); + gpio_direction_output(_3_TOUCH_SCL_28V, 1); + + gpio_direction_output(_3_GPIO_TOUCH_INT, 1); + irq_set_irq_type(IRQ_TOUCH_INT, IRQF_TRIGGER_FALLING); + s3c_gpio_cfgpin(_3_GPIO_TOUCH_INT, _3_GPIO_TOUCH_INT_AF); + s3c_gpio_setpull(_3_GPIO_TOUCH_INT, S3C_GPIO_PULL_NONE); + msleep(50); + touchkey_led_ldo_on(1); + +#ifdef WHY_DO_WE_NEED_THIS + /* clear interrupt */ + if (readl(gpio_pend_mask_mem) & (0x1 << 1)) { + writel(readl(gpio_pend_mask_mem) | (0x1 << 1), + gpio_pend_mask_mem); + } +#endif + + touchkey_enable = 1; + + bln_suspended = 0; + /* see if late_resume is running before DISABLE_BL */ + if (BLN_ongoing) { + /* if a notification timeout was set, disable the timer */ + if (notification_timeout > 0) { + del_timer(¬ification_timer); + } + if (breathing) stop_breathing(); + + /* we were using a wakelock, unlock it */ + if( wake_lock_active(&bln_wake_lock) ){ + printk(KERN_DEBUG "[TouchKey] touchkey clear wake_lock\n"); + wake_unlock(&bln_wake_lock); + } + /* force DISABLE_BL to ignore the led state because we want it left on */ + BLN_ongoing = 0; + } + +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) +#if defined(CONFIG_TARGET_LOCALE_NA) + if (store_module_version >= 8) { +#endif + msleep(50); + touchkey_autocalibration(); + msleep(200); +#if defined(CONFIG_TARGET_LOCALE_NA) + } +#endif /* CONFIG_TARGET_LOCALE_NA */ +#endif + + if (touchled_cmd_reversed) { + touchled_cmd_reversed = 0; + i2c_touchkey_write((u8 *) &touchkey_led_status, 1); + printk(KERN_DEBUG "LED returned on\n"); + } +#ifdef TEST_JIG_MODE + i2c_touchkey_write(&get_touch, 1); +#endif + + /* restart the timer if needed */ + if (led_timeout > 0) { + mod_timer(&led_timer, jiffies + msecs_to_jiffies(led_timeout)); + } + + /* all done, turn on IRQ */ + enable_irq(IRQ_TOUCH_INT); + + /* Avoid race condition with LED notification disable */ + up(&enable_sem); + + return 0; +} +#endif + +#ifdef CONFIG_TOUCHKEY_BLN + +static void touchkey_activate(void){ + + if( !wake_lock_active(&bln_wake_lock) ){ + printk(KERN_DEBUG "[TouchKey] touchkey get wake_lock\n"); + wake_lock(&bln_wake_lock); + } + + printk(KERN_DEBUG "[TouchKey] touchkey activate.\n"); + touchkey_ldo_on(1); + + msleep(50); + touchkey_led_ldo_on(1); + + touchkey_enable = 1; +} + +static void touchkey_deactivate(void){ + + touchkey_led_ldo_on(0); + touchkey_ldo_on(0); + + if( wake_lock_active(&bln_wake_lock) ){ + printk(KERN_DEBUG "[TouchKey] touchkey clear wake_lock\n"); + wake_unlock(&bln_wake_lock); + } + + touchkey_enable = 0; +} + +static void bln_late_resume(struct early_suspend *h){ + + /* the lights should be off */ + //we only need this part to disable lights way before ROM specific parts interfere -gm + int status; + status = 2; + i2c_touchkey_write((u8 *)&status, 1); /* turn off */ +} + +static struct early_suspend bln_suspend_data = { + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, + .resume = bln_late_resume, +}; + +static void enable_touchkey_backlights(void){ + int status = 1; + printk(KERN_ERR "[TouchKey] enable LED from BLN app\n"); + i2c_touchkey_write((u8 *)&status, 1 ); +} + +static void disable_touchkey_backlights(void){ + int status = 2; + printk(KERN_ERR "[TouchKey] disable LED from BLN app\n"); + i2c_touchkey_write((u8 *)&status, 1 ); +} + +static void enable_led_notification(void){ + + if( bln_enabled ){ + if( touchkey_enable != 1 ){ + if( bln_suspended ){ + touchkey_activate(); + } + } + if( touchkey_enable == 1 ){ + printk(KERN_DEBUG "[TouchKey] BLN_ongoing set to true\n"); + BLN_ongoing = true; + enable_touchkey_backlights(); + } + /* See if a timeout value has been set for the notification */ + if (notification_timeout > 0) { + /* restart the timer */ + mod_timer(¬ification_timer, jiffies + msecs_to_jiffies(notification_timeout)); + } + if ( breathing ) mod_timer(&breathing_timer, jiffies + 4); + + } + +} + +static void disable_led_notification(void){ + + down(&enable_sem); + bln_blink_enabled = false; + BLN_ongoing = false; + printk(KERN_DEBUG "[TouchKey] BLN_ongoing set to false\n"); + + if( touchkey_enable == 1 ){ + disable_touchkey_backlights(); + if( bln_suspended ){ + touchkey_deactivate(); + } + /* a notification timeout was set, disable the timer */ + if (notification_timeout > 0) { + del_timer(¬ification_timer); + } + if (breathing) stop_breathing(); + } + up(&enable_sem); +} + +static ssize_t bln_status_read( struct device *dev, struct device_attribute *attr, char *buf ){ + return sprintf(buf,"%u\n", (bln_enabled ? 1 : 0 )); +} + +static ssize_t bln_status_write( struct device *dev, struct device_attribute *attr, const char *buf, size_t size ){ + unsigned int data; + + if(sscanf(buf,"%u\n", &data) == 1 ){ + if( data == 0 || data == 1 ){ + + if( data == 1 ){ + bln_enabled = true; + } + + if( data == 0 ){ + bln_enabled = false; + if( BLN_ongoing ) + disable_led_notification(); + } + + }else{ + /* error */ + } + }else{ + if( !strncmp(buf, "on", 2) ) bln_enabled = true; + if( !strncmp(buf, "off", 3) ) { + bln_enabled = false; + if( BLN_ongoing ) + disable_led_notification(); + } + } + + return size; +} + +static ssize_t notification_led_status_read( struct device *dev, struct device_attribute *attr, char *buf ){ + return sprintf(buf,"%u\n", (BLN_ongoing ? 1 : 0 )); +} + +static ssize_t notification_led_status_write( struct device *dev, struct device_attribute *attr, const char *buf, size_t size ){ + unsigned int data; + + + if(sscanf(buf,"%u\n", &data ) == 1 ){ + if( data == 0 || data == 1 ){ + if( data == 1 ) + enable_led_notification(); + + if( data == 0 ) + disable_led_notification(); + }else{ + /* error */ + } + }else{ + /* error */ + } + + return size; +} + +static ssize_t blink_control_read( struct device *dev, struct device_attribute *attr, char *buf ){ + return sprintf( buf, "%u\n", (bln_blink_enabled ? 1 : 0 ) ); +} + +static ssize_t blink_control_write( struct device *dev, struct device_attribute *attr, const char *buf, size_t size ){ + unsigned int data; + + if( sscanf(buf, "%u\n", &data ) == 1 ){ + if( data == 0 || data == 1 ){ + if (data == 1){ + bln_blink_enabled = true; + disable_touchkey_backlights(); + } + + if(data == 0){ + bln_blink_enabled = false; + enable_touchkey_backlights(); + } + } + } + + return size; +} + +static ssize_t bln_version( struct device *dev, struct device_attribute *attr, char *buf ){ + return sprintf(buf,"%u\n", BLN_VERSION); +} + +static DEVICE_ATTR(blink_control, S_IRUGO | S_IWUGO, blink_control_read, blink_control_write ); +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUGO, bln_status_read, bln_status_write ); +static DEVICE_ATTR(notification_led, S_IRUGO | S_IWUGO, notification_led_status_read, notification_led_status_write ); +static DEVICE_ATTR(version, S_IRUGO, bln_version, NULL ); + +static struct attribute *bln_notification_attributes[] = { + &dev_attr_blink_control.attr, + &dev_attr_enabled.attr, + &dev_attr_notification_led.attr, + &dev_attr_version.attr, + NULL +}; + + +/* + * Start of the main LED Notify code block + */ +static void bl_off(struct work_struct *bl_off_work) +{ + int status; + + /* do nothing if there is an active notification */ + if (BLN_ongoing == 1 || touchkey_enable != 1) + return; + + /* we have timed out, turn the lights off */ + status = 2; + i2c_touchkey_write((u8 *)&status, 1); + + return; +} + +static void handle_led_timeout(unsigned long data) +{ + /* we cannot call the timeout directly as it causes a kernel spinlock BUG, schedule it instead */ + schedule_work(&bl_off_work); +} + +static void notification_off(struct work_struct *notification_off_work) +{ + /* do nothing if there is no active notification */ + if (BLN_ongoing != 1 || touchkey_enable != 1) + return; + + disable_touchkey_backlights(); + touchkey_deactivate(); + BLN_ongoing = 0; +} + +static void handle_notification_timeout(unsigned long data) +{ + /* we cannot call the timeout directly as it causes a kernel spinlock BUG, schedule it instead */ + schedule_work(¬ification_off_work); +} + +static ssize_t led_timeout_read( struct device *dev, struct device_attribute *attr, char *buf ) +{ + return sprintf(buf,"%d\n", led_timeout); +} + +static ssize_t led_timeout_write( struct device *dev, struct device_attribute *attr, const char *buf, size_t size ) +{ + sscanf(buf,"%d\n", &led_timeout); + if(led_timeout == 0) del_timer(&led_timer); + else mod_timer(&led_timer, jiffies + msecs_to_jiffies(led_timeout)); + return size; +} + +static ssize_t notification_timeout_read( struct device *dev, struct device_attribute *attr, char *buf ) +{ + return sprintf(buf,"%d\n", notification_timeout); +} + +static ssize_t notification_timeout_write( struct device *dev, struct device_attribute *attr, const char *buf, size_t size ) +{ + sscanf(buf,"%d\n", ¬ification_timeout); + return size; +} + +static void breathe(struct work_struct *notification_off_work) +{ + int data; + if (BLN_ongoing != 1 || touchkey_enable != 1) + return; + + if( breathing_steps[breathing_step_idx].start <= breathing_steps[breathing_step_idx].end ) + { + data = breathing_steps[breathing_step_idx].start + + breathing_idx++ * breathing_steps[breathing_step_idx].step; + if( data > breathing_steps[breathing_step_idx].end ) + { + breathing_idx = 0; + breathing_step_idx++; + if( breathing_step_idx >= breathing_step_count ) breathing_step_idx = 0; + data = breathing_steps[breathing_step_idx].start; + } + } + else + { + data = breathing_steps[breathing_step_idx].start - + breathing_idx++ * breathing_steps[breathing_step_idx].step; + if( data < breathing_steps[breathing_step_idx].end ) + { + breathing_idx = 0; + breathing_step_idx++; + if( breathing_step_idx >= breathing_step_count ) breathing_step_idx = 0; + data = breathing_steps[breathing_step_idx].start; + } + } + + change_touch_key_led_voltage(data); + mod_timer(&breathing_timer, jiffies + msecs_to_jiffies(breathing_steps[breathing_step_idx].period)); +} + +static void handle_breathe(unsigned long data) +{ + schedule_work(&breathe_work); +} + +static ssize_t breathing_read( struct device *dev, struct device_attribute *attr, char *buf ) +{ + return sprintf(buf,"%d\n", breathing); +} +static ssize_t breathing_write( struct device *dev, struct device_attribute *attr, const char *buf, size_t size ) +{ + if( !strncmp(buf, "on", 2) ) breathing = 1; + else if( !strncmp(buf, "off", 3) ) breathing = 0; + else sscanf(buf,"%d\n", &breathing); + if( breathing != 1 ) stop_breathing(); + return size; +} + +void reset_breathing_steps(void) +{ + //this will reset steps to have steady bln + breathing_step_count = 0; + breathing_steps[0].start = 3000; + breathing_steps[0].end = 3000; + breathing_steps[0].period = 1000; + breathing_steps[0].step = 50; +} + +static ssize_t breathing_steps_read( struct device *dev, struct device_attribute *attr, char *buf ) +{ + int count = ( breathing_step_count == 0 ? 1 : breathing_step_count ); + int i, len = 0; + for(i=0; i= MAX_BREATHING_STEPS) return -EINVAL; + ret = sscanf(buf, "%d %d %d %d", &bstart, &bend, &bperiod, &bstep); + if(ret != 4) return -EINVAL; + breathing_steps[breathing_step_count].start = bstart; + breathing_steps[breathing_step_count].end = bend; + breathing_steps[breathing_step_count].period = bperiod; + breathing_steps[breathing_step_count].step = bstep; + breathing_step_count++; + breathing_idx = 0; + breathing_step_idx = 0; + return size; +} + +static struct miscdevice led_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "notification", +}; + +static DEVICE_ATTR(led, S_IRUGO | S_IWUGO, notification_led_status_read, notification_led_status_write ); +static DEVICE_ATTR(notification_timeout, S_IRUGO | S_IWUGO, notification_timeout_read, notification_timeout_write ); +static DEVICE_ATTR(led_timeout, S_IRUGO | S_IWUGO, led_timeout_read, led_timeout_write ); +static DEVICE_ATTR(bl_timeout, S_IRUGO | S_IWUGO, led_timeout_read, led_timeout_write ); +static DEVICE_ATTR(notification_enabled, S_IRUGO | S_IWUGO, bln_status_read, bln_status_write ); +static DEVICE_ATTR(breathing, S_IRUGO | S_IWUGO, breathing_read, breathing_write ); +static DEVICE_ATTR(breathing_steps, S_IRUGO | S_IWUGO, breathing_steps_read, breathing_steps_write ); + +static struct attribute *led_notification_attributes[] = { + &dev_attr_led.attr, + &dev_attr_led_timeout.attr, + &dev_attr_bl_timeout.attr, + &dev_attr_notification_timeout.attr, + &dev_attr_notification_enabled.attr, + &dev_attr_breathing.attr, + &dev_attr_breathing_steps.attr, + NULL +}; + +static struct attribute_group led_notification_group = { + .attrs = led_notification_attributes, +}; + +static struct attribute_group bln_notification_group = { + .attrs = bln_notification_attributes, +}; + +static struct miscdevice bln_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "backlightnotification", +}; + +extern void (*mxt224_touch_cb)(void); + +void cypress_notify_touch(void) +{ + unsigned int status; + if (led_timeout > 0) { + status = 1; + i2c_touchkey_write((u8 *)&status, 1); /* turn on */ + } + if (led_timeout > 0) { + mod_timer(&led_timer, jiffies + msecs_to_jiffies(led_timeout)); + } +} + +#endif + +extern int mcsdl_download_binary_data(void); +static int i2c_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct input_dev *input_dev; + int err = 0; + unsigned char data; + int i; + int module_version; + int status; + + printk(KERN_DEBUG "[TouchKey] i2c_touchkey_probe\n"); + + touchkey_driver = + kzalloc(sizeof(struct i2c_touchkey_driver), GFP_KERNEL); + if (touchkey_driver == NULL) { + dev_err(dev, "failed to create our state\n"); + return -ENOMEM; + } + + touchkey_driver->client = client; + touchkey_driver->client->irq = IRQ_TOUCH_INT; + strlcpy(touchkey_driver->client->name, "sec_touchkey", I2C_NAME_SIZE); + + input_dev = input_allocate_device(); + + if (!input_dev) + return -ENOMEM; + + touchkey_driver->input_dev = input_dev; + + input_dev->name = DEVICE_NAME; + input_dev->phys = "sec_touchkey/input0"; + input_dev->id.bustype = BUS_HOST; + + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_LED, input_dev->evbit); + set_bit(LED_MISC, input_dev->ledbit); + set_bit(EV_KEY, input_dev->evbit); + + for (i = 1; i < touchkey_count; i++) + set_bit(touchkey_keycode[i], input_dev->keybit); + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } +#ifdef WHY_DO_WE_NEED_THIS + gpio_pend_mask_mem = ioremap(INT_PEND_BASE, 0x10); +#endif + +#if defined(CONFIG_MACH_S2PLUS) + gpio_request(GPIO_3_TOUCH_EN, "gpio_3_touch_en"); +#endif + + /* enable ldo18 */ + touchkey_ldo_on(1); + + msleep(50); + + touchkey_enable = 1; + data = 1; + + module_version = get_touchkey_module_version(); + if (module_version < 0) { + printk(KERN_ERR "[TouchKey] Probe fail\n"); + input_unregister_device(input_dev); + touchkey_probe = false; + return -ENODEV; + } + +#ifdef CONFIG_TARGET_LOCALE_NA + store_module_version = module_version; +#endif /* CONFIG_TARGET_LOCALE_NA */ + if (request_irq + (IRQ_TOUCH_INT, touchkey_interrupt, IRQF_TRIGGER_FALLING, + DEVICE_NAME, NULL)) { + printk(KERN_ERR "[TouchKey] %s Can't allocate irq ..\n", + __func__); + return -EBUSY; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + touchkey_driver->early_suspend.suspend = + (void *)sec_touchkey_early_suspend; + touchkey_driver->early_suspend.resume = + (void *)sec_touchkey_late_resume; + register_early_suspend(&touchkey_driver->early_suspend); +#endif + + touchkey_led_ldo_on(1); + +#if defined(CONFIG_MACH_C1_NA_SPR_EPIC2_REV00)\ + || defined(CONFIG_MACH_Q1_BD) \ + || defined(CONFIG_MACH_C1_NA_USCC_REV05) + + touchkey_firmware_update(); +#endif + +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) + /*touchkey_firmware_update(); */ +#if defined(CONFIG_TARGET_LOCALE_NA) + if (store_module_version >= 8) { +#endif + msleep(100); + err = touchkey_autocalibration(); + if (err < 0) { + printk(KERN_ERR + "[TouchKey] probe autocalibration fail\n"); + return err; + } +#if defined(CONFIG_TARGET_LOCALE_NA) + } +#endif /* CONFIG_TARGET_LOCALE_NA */ +#endif + set_touchkey_debug('K'); + +#ifdef CONFIG_TOUCHKEY_BLN + err = misc_register( &bln_device ); + if( err ){ + printk(KERN_ERR "[BLN] sysfs misc_register failed.\n"); + }else{ + if( sysfs_create_group( &bln_device.this_device->kobj, &bln_notification_group) < 0){ + printk(KERN_ERR "[BLN] sysfs create group failed.\n"); + } + } + + /* BLN early suspend */ + register_early_suspend(&bln_suspend_data); + + //this miscdevice is for cm9 + err = misc_register( &led_device ); + if( err ){ + printk(KERN_ERR "[LED] sysfs misc_register failed.\n"); + }else{ + if( sysfs_create_group( &led_device.this_device->kobj, &led_notification_group) < 0){ + printk(KERN_ERR "[LED] sysfs create group failed.\n"); + } + } + + /* Setup the timer for the timeouts */ + setup_timer(&led_timer, handle_led_timeout, 0); + setup_timer(¬ification_timer, handle_notification_timeout, 0); + setup_timer(&breathing_timer, handle_breathe, 0); + + led_timeout = 0; + reset_breathing_steps(); + + /* wake lock for BLN */ + wake_lock_init(&bln_wake_lock, WAKE_LOCK_SUSPEND, "bln_wake_lock"); + /* turn off the LED on probe */ + status = 2; + i2c_touchkey_write((u8 *)&status, 1); + + mxt224_touch_cb = cypress_notify_touch; +#endif + + return 0; +} + +static void init_hw(void) +{ + gpio_direction_output(_3_GPIO_TOUCH_EN, 1); + msleep(200); + s3c_gpio_setpull(_3_GPIO_TOUCH_INT, S3C_GPIO_PULL_NONE); + irq_set_irq_type(IRQ_TOUCH_INT, IRQF_TRIGGER_FALLING); + s3c_gpio_cfgpin(_3_GPIO_TOUCH_INT, _3_GPIO_TOUCH_INT_AF); +} + +static int get_touchkey_module_version() +{ + char data[3] = { 0, }; + int ret = 0; + + ret = i2c_touchkey_read(KEYCODE_REG, data, 3); + if (ret < 0) { + printk(KERN_ERR "[TouchKey] module version read fail\n"); + return ret; + } else { + printk(KERN_DEBUG "[TouchKey] Module Version: %d\n", data[2]); + return data[2]; + } +} + +int touchkey_update_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +ssize_t touchkey_update_read(struct file *filp, char *buf, size_t count, + loff_t *f_pos) +{ + char data[3] = { 0, }; + + get_touchkey_firmware(data); + put_user(data[1], buf); + + return 1; +} + +int touchkey_update_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static ssize_t touch_version_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char data[3] = { 0, }; + int count; + + init_hw(); + i2c_touchkey_read(KEYCODE_REG, data, 3); + + count = sprintf(buf, "0x%x\n", data[1]); + + printk(KERN_DEBUG "[TouchKey] touch_version_read 0x%x\n", data[1]); + printk(KERN_DEBUG "[TouchKey] module_version_read 0x%x\n", data[2]); + + return count; +} + +static ssize_t touch_version_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + printk(KERN_DEBUG "[TouchKey] input data --> %s\n", buf); + + return size; +} + +void touchkey_update_func(struct work_struct *p) +{ + int retry = 10; +#if defined(CONFIG_TARGET_LOCALE_NAATT) + char data[3]; + i2c_touchkey_read(KEYCODE_REG, data, 3); + printk(KERN_DEBUG "[%s] F/W version: 0x%x, Module version:0x%x\n", + __func__, data[1], data[2]); +#endif + touchkey_update_status = 1; + printk(KERN_DEBUG "[TouchKey] %s start\n", __func__); + touchkey_enable = 0; + while (retry--) { + if (ISSP_main() == 0) { + printk(KERN_DEBUG + "[TouchKey] touchkey_update succeeded\n"); + init_hw(); + enable_irq(IRQ_TOUCH_INT); + touchkey_enable = 1; +#if defined(CONFIG_MACH_Q1_BD) + touchkey_autocalibration(); +#else +#if defined(CONFIG_TARGET_LOCALE_NA) + if (store_module_version >= 8) + touchkey_autocalibration(); +#endif +#endif + touchkey_update_status = 0; + return; + } +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) + touchkey_ldo_on(0); + msleep(300); + init_hw(); +#endif + } + + touchkey_update_status = -1; + printk(KERN_DEBUG "[TouchKey] touchkey_update failed\n"); + return; +} + +static ssize_t touch_update_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) { + printk(KERN_DEBUG + "[TouchKey] Skipping f/w update : module_version =%d\n", + store_module_version); + touchkey_update_status = 0; + return 1; + } else { +#endif /* CONFIG_TARGET_LOCALE_NA */ + printk(KERN_DEBUG "[TouchKey] touchkey firmware update\n"); + + if (*buf == 'S') { + disable_irq(IRQ_TOUCH_INT); + INIT_WORK(&touch_update_work, touchkey_update_func); + queue_work(touchkey_wq, &touch_update_work); + } + return size; +#ifdef CONFIG_TARGET_LOCALE_NA + } +#endif /* CONFIG_TARGET_LOCALE_NA */ +} + +static ssize_t touch_update_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0; + + printk(KERN_DEBUG + "[TouchKey] touch_update_read: touchkey_update_status %d\n", + touchkey_update_status); + + if (touchkey_update_status == 0) + count = sprintf(buf, "PASS\n"); + else if (touchkey_update_status == 1) + count = sprintf(buf, "Downloading\n"); + else if (touchkey_update_status == -1) + count = sprintf(buf, "Fail\n"); + + return count; +} + +static ssize_t touch_led_control(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + int data; + int errnum; + + if (sscanf(buf, "%d\n", &data) == 1) { +#if defined(CONFIG_MACH_Q1_BD) + if (data == 1) + data = 0x10; + else if (data == 2) + data = 0x20; +#elif defined(CONFIG_TARGET_LOCALE_NA) + if (store_module_version >= 8) { + if (data == 1) + data = 0x10; + else if (data == 2) + data = 0x20; + } +#endif +#ifdef CONFIG_TOUCHKEY_BLN + printk(KERN_ERR "[TouchKey] system calling LED Notification control\n"); +#endif + errnum = i2c_touchkey_write((u8 *) &data, 1); + if( data == 1 && led_timeout > 0 ) + mod_timer(&led_timer, jiffies + msecs_to_jiffies(led_timeout)); + if (errnum == -ENODEV) + touchled_cmd_reversed = 1; + + touchkey_led_status = data; + } else { + printk(KERN_DEBUG "[TouchKey] touch_led_control Error\n"); + } + + return size; +} + +static ssize_t touchkey_enable_disable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return size; +} + +#if defined(CONFIG_TARGET_LOCALE_NAATT) || defined(CONFIG_TARGET_LOCALE_NA) +static ssize_t touchkey_menu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[18] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 18); +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) { + printk(KERN_DEBUG "called %s data[12] =%d,data[13] = %d\n", + __func__, data[12], data[13]); + menu_sensitivity = ((0x00FF & data[12]) << 8) | data[13]; + } else { + printk(KERN_DEBUG "called %s data[17] =%d\n", __func__, + data[17]); + menu_sensitivity = data[17]; + } +#else + printk(KERN_DEBUG "called %s data[10] =%d,data[11] = %d\n", __func__, + data[10], data[11]); + menu_sensitivity = ((0x00FF & data[10]) << 8) | data[11]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", menu_sensitivity); +} + +static ssize_t touchkey_home_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[18] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 18); +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) { + printk(KERN_DEBUG "called %s data[10] =%d,data[11] = %d\n", + __func__, data[10], data[11]); + home_sensitivity = ((0x00FF & data[10]) << 8) | data[11]; + } else { + printk(KERN_DEBUG "called %s data[15] =%d\n", __func__, + data[15]); + home_sensitivity = data[15]; + } +#else + printk(KERN_DEBUG "called %s data[12] =%d,data[13] = %d\n", __func__, + data[12], data[13]); + home_sensitivity = ((0x00FF & data[12]) << 8) | data[13]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", home_sensitivity); +} + +static ssize_t touchkey_back_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[18] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 18); +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) { + printk(KERN_DEBUG "called %s data[8] =%d,data[9] = %d\n", + __func__, data[8], data[9]); + back_sensitivity = ((0x00FF & data[8]) << 8) | data[9]; + } else { + printk(KERN_DEBUG "called %s data[13] =%d\n", __func__, + data[13]); + back_sensitivity = data[13]; + } +#else + printk(KERN_DEBUG "called %s data[14] =%d,data[15] = %d\n", __func__, + data[14], data[15]); + back_sensitivity = ((0x00FF & data[14]) << 8) | data[15]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", back_sensitivity); +} + +static ssize_t touchkey_search_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[18] = { 0, }; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 18); +#ifdef CONFIG_TARGET_LOCALE_NA + if (store_module_version < 8) { + printk(KERN_DEBUG "called %s data[6] =%d,data[7] = %d\n", + __func__, data[6], data[7]); + search_sensitivity = ((0x00FF & data[6]) << 8) | data[7]; + } else { + printk(KERN_DEBUG "called %s data[11] =%d\n", __func__, + data[11]); + search_sensitivity = data[11]; + } +#else + printk(KERN_DEBUG "called %s data[16] =%d,data[17] = %d\n", __func__, + data[16], data[17]); + search_sensitivity = ((0x00FF & data[16]) << 8) | data[17]; +#endif /* CONFIG_TARGET_LOCALE_NA */ + return sprintf(buf, "%d\n", search_sensitivity); +} +#else +static ssize_t touchkey_menu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(CONFIG_MACH_Q1_BD) + u8 data[14] = { 0, }; + int ret; + + ret = i2c_touchkey_read(KEYCODE_REG, data, 14); + + printk(KERN_DEBUG "called %s data[13] =%d\n", __func__, data[13]); + menu_sensitivity = data[13]; +#else + u8 data[10]; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + menu_sensitivity = data[7]; +#endif + return sprintf(buf, "%d\n", menu_sensitivity); +} + +static ssize_t touchkey_back_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#if defined(CONFIG_MACH_Q1_BD) + u8 data[14] = { 0, }; + int ret; + + ret = i2c_touchkey_read(KEYCODE_REG, data, 14); + + printk(KERN_DEBUG "called %s data[11] =%d\n", __func__, data[11]); + back_sensitivity = data[11]; +#else + u8 data[10]; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + ret = i2c_touchkey_read(KEYCODE_REG, data, 10); + back_sensitivity = data[9]; +#endif + return sprintf(buf, "%d\n", back_sensitivity); +} +#endif + +#if defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) +static ssize_t autocalibration_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int data; + + sscanf(buf, "%d\n", &data); + + if (data == 1) + touchkey_autocalibration(); + + return size; +} + +static ssize_t autocalibration_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[6]; + int ret; + + printk(KERN_DEBUG "called %s\n", __func__); + + ret = i2c_touchkey_read(KEYCODE_REG, data, 6); + if ((data[5] & 0x80)) + return sprintf(buf, "Enabled\n"); + else + return sprintf(buf, "Disabled\n"); + +} +#endif /* CONFIG_TARGET_LOCALE_NA */ + +static ssize_t touch_sensitivity_control(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned char data = 0x40; + i2c_touchkey_write(&data, 1); + return size; +} + +static ssize_t set_touchkey_firm_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "0x%x\n", TK_FIRMWARE_VER); +} + +static ssize_t set_touchkey_update_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* TO DO IT */ + int count = 0; + int retry = 3; + touchkey_update_status = 1; + +#ifdef TEST_JIG_MODE + unsigned char get_touch = 0x40; +#endif + + while (retry--) { + if (ISSP_main() == 0) { + printk(KERN_ERR + "[TOUCHKEY]Touchkey_update succeeded\n"); + touchkey_update_status = 0; + count = 1; + break; + } + printk(KERN_ERR "touchkey_update failed... retry...\n"); + } + if (retry <= 0) { + /* disable ldo11 */ + touchkey_ldo_on(0); + msleep(300); + count = 0; + printk(KERN_ERR "[TOUCHKEY]Touchkey_update fail\n"); + touchkey_update_status = -1; + return count; + } + + init_hw(); /* after update, re initalize. */ + +#ifdef TEST_JIG_MODE + i2c_touchkey_write(&get_touch, 1); +#endif + + return count; + +} + +static ssize_t set_touchkey_firm_version_read_show(struct device *dev, + struct device_attribute + *attr, char *buf) +{ + char data[3] = { 0, }; + int count; + + init_hw(); + /*if (get_touchkey_firmware(data) != 0) { */ + i2c_touchkey_read(KEYCODE_REG, data, 3); + /*} */ + count = sprintf(buf, "0x%x\n", data[1]); + + printk(KERN_DEBUG "[TouchKey] touch_version_read 0x%x\n", data[1]); + printk(KERN_DEBUG "[TouchKey] module_version_read 0x%x\n", data[2]); + return count; +} + +static ssize_t set_touchkey_firm_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int count = 0; + + printk(KERN_DEBUG + "[TouchKey] touch_update_read: touchkey_update_status %d\n", + touchkey_update_status); + + if (touchkey_update_status == 0) + count = sprintf(buf, "PASS\n"); + else if (touchkey_update_status == 1) + count = sprintf(buf, "Downloading\n"); + else if (touchkey_update_status == -1) + count = sprintf(buf, "Fail\n"); + + return count; +} + +static DEVICE_ATTR(recommended_version, S_IRUGO | S_IWUSR | S_IWGRP, + touch_version_read, touch_version_write); +static DEVICE_ATTR(updated_version, S_IRUGO | S_IWUSR | S_IWGRP, + touch_update_read, touch_update_write); +static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + touch_led_control); +static DEVICE_ATTR(enable_disable, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + touchkey_enable_disable); +static DEVICE_ATTR(touchkey_menu, S_IRUGO | S_IWUSR | S_IWGRP, + touchkey_menu_show, NULL); +static DEVICE_ATTR(touchkey_back, S_IRUGO | S_IWUSR | S_IWGRP, + touchkey_back_show, NULL); +#if defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_TARGET_LOCALE_NAATT) +static DEVICE_ATTR(touchkey_home, S_IRUGO, touchkey_home_show, NULL); +static DEVICE_ATTR(touchkey_search, S_IRUGO, touchkey_search_show, NULL); +#endif /* CONFIG_TARGET_LOCALE_NA */ +static DEVICE_ATTR(touch_sensitivity, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + touch_sensitivity_control); +/*20110223N1 firmware sync*/ +static DEVICE_ATTR(touchkey_firm_update, S_IRUGO | S_IWUSR | S_IWGRP, + set_touchkey_update_show, NULL);/* firmware update */ +static DEVICE_ATTR(touchkey_firm_update_status, S_IRUGO | S_IWUSR | S_IWGRP, + set_touchkey_firm_status_show, NULL);/* firmware update status */ +static DEVICE_ATTR(touchkey_firm_version_phone, S_IRUGO | S_IWUSR | S_IWGRP, + set_touchkey_firm_version_show, NULL);/* PHONE */ +static DEVICE_ATTR(touchkey_firm_version_panel, S_IRUGO | S_IWUSR | S_IWGRP, + set_touchkey_firm_version_read_show, NULL); +/*PART*/ +/*end N1 firmware sync*/ +static DEVICE_ATTR(touchkey_brightness, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + brightness_control); + +#if defined(CONFIG_TARGET_LOCALE_NAATT) +static DEVICE_ATTR(touchkey_autocal_start, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + set_touchkey_autocal_testmode); +#endif + +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) +static DEVICE_ATTR(touchkey_raw_data0, S_IRUGO, touchkey_raw_data0_show, NULL); +static DEVICE_ATTR(touchkey_raw_data1, S_IRUGO, touchkey_raw_data1_show, NULL); +static DEVICE_ATTR(touchkey_raw_data2, S_IRUGO, touchkey_raw_data2_show, NULL); +static DEVICE_ATTR(touchkey_raw_data3, S_IRUGO, touchkey_raw_data3_show, NULL); +static DEVICE_ATTR(touchkey_idac0, S_IRUGO, touchkey_idac0_show, NULL); +static DEVICE_ATTR(touchkey_idac1, S_IRUGO, touchkey_idac1_show, NULL); +static DEVICE_ATTR(touchkey_idac2, S_IRUGO, touchkey_idac2_show, NULL); +static DEVICE_ATTR(touchkey_idac3, S_IRUGO, touchkey_idac3_show, NULL); +static DEVICE_ATTR(touchkey_threshold, S_IRUGO, touchkey_threshold_show, NULL); +#endif + +#if defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) +static DEVICE_ATTR(autocal_enable, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + autocalibration_enable); +static DEVICE_ATTR(autocal_stat, S_IRUGO | S_IWUSR | S_IWGRP, + autocalibration_status, NULL); +#endif /* CONFIG_TARGET_LOCALE_NA */ +static int __init touchkey_init(void) +{ + int ret = 0; + +#ifdef TEST_JIG_MODE + unsigned char get_touch = 0x40; +#endif + + sec_touchkey = device_create(sec_class, NULL, 0, NULL, "sec_touchkey"); + + if (IS_ERR(sec_touchkey)) + printk(KERN_ERR "Failed to create device(sec_touchkey)!\n"); + + if (device_create_file(sec_touchkey, &dev_attr_touchkey_firm_update) < + 0) { + printk(KERN_ERR "Failed to create device file(%s)!\n", + dev_attr_touchkey_firm_update.attr.name); + } + if (device_create_file + (sec_touchkey, &dev_attr_touchkey_firm_update_status) < 0) { + printk(KERN_ERR "Failed to create device file(%s)!\n", + dev_attr_touchkey_firm_update_status.attr.name); + } + if (device_create_file + (sec_touchkey, &dev_attr_touchkey_firm_version_phone) < 0) { + printk(KERN_ERR "Failed to create device file(%s)!\n", + dev_attr_touchkey_firm_version_phone.attr.name); + } + if (device_create_file + (sec_touchkey, &dev_attr_touchkey_firm_version_panel) < 0) { + printk(KERN_ERR "Failed to create device file(%s)!\n", + dev_attr_touchkey_firm_version_panel.attr.name); + } + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_brightness) < 0) { + printk(KERN_ERR "Failed to create device file(%s)!\n", + dev_attr_touchkey_brightness.attr.name); + } +#if defined(CONFIG_TARGET_LOCALE_NAATT) + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_autocal_start) < + 0) { + printk(KERN_ERR "Failed to create device file(%s)!\n", + dev_attr_touchkey_brightness.attr.name); + } +#endif + + if (device_create_file(sec_touchkey, + &dev_attr_recommended_version) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_recommended_version.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_updated_version) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_updated_version.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_brightness) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_brightness.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_enable_disable) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_enable_disable.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_menu) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_menu.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_back) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_back.attr.name); + } +#if defined(CONFIG_TARGET_LOCALE_NAATT) \ +|| defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_raw_data0) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_raw_data0.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_raw_data1) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_raw_data1.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_raw_data2) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_raw_data2.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_raw_data3) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_raw_data3.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_idac0) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_idac0.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_idac1) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_idac1.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_idac2) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_idac2.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_idac3) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_idac3.attr.name); + } + + if (device_create_file(sec_touchkey, + &dev_attr_touchkey_threshold) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_threshold.attr.name); + } +#endif + +#if defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_TARGET_LOCALE_NAATT) + if (device_create_file(sec_touchkey, &dev_attr_touchkey_home) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_home.attr.name); + } + + if (device_create_file(sec_touchkey, &dev_attr_touchkey_search) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touchkey_search.attr.name); + } +#endif /* CONFIG_TARGET_LOCALE_NA */ + +#if defined(CONFIG_TARGET_LOCALE_NA) || defined(CONFIG_MACH_Q1_BD) + if (device_create_file(sec_touchkey, &dev_attr_autocal_enable) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_autocal_enable.attr.name); + } + + if (device_create_file(sec_touchkey, &dev_attr_autocal_stat) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_autocal_stat.attr.name); + } +#endif /* CONFIG_TARGET_LOCALE_NA */ + + if (device_create_file(sec_touchkey, + &dev_attr_touch_sensitivity) < 0) { + pr_err("Failed to create device file(%s)!\n", + dev_attr_touch_sensitivity.attr.name); + } + touchkey_wq = create_singlethread_workqueue("sec_touchkey_wq"); + if (!touchkey_wq) + return -ENOMEM; + + INIT_WORK(&touchkey_work, touchkey_work_func); + + init_hw(); + + ret = i2c_add_driver(&touchkey_i2c_driver); + + if (ret) { + printk(KERN_ERR + "[TouchKey] registration failed, module not inserted.ret= %d\n", + ret); + } +#ifdef TEST_JIG_MODE + i2c_touchkey_write(&get_touch, 1); +#endif + return ret; + +} + +static void __exit touchkey_exit(void) +{ + printk(KERN_DEBUG "[TouchKey] %s\n", __func__); + i2c_del_driver(&touchkey_i2c_driver); + +#ifdef CONFIG_TOUCHKEY_BLN + misc_deregister(&bln_device); + wake_lock_destroy(&bln_wake_lock); + del_timer(&led_timer); + del_timer(¬ification_timer); + del_timer(&breathing_timer); +#endif + + if (touchkey_wq) + destroy_workqueue(touchkey_wq); + +#ifndef CONFIG_MACH_Q1_BD + gpio_free(_3_TOUCH_SDA_28V); + gpio_free(_3_TOUCH_SCL_28V); + gpio_free(_3_GPIO_TOUCH_EN); +#endif + gpio_free(_3_GPIO_TOUCH_INT); +} + +late_initcall(touchkey_init); +module_exit(touchkey_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("@@@"); +MODULE_DESCRIPTION("touch keypad"); diff --git a/drivers/input/keyboard/cypressbln/issp_defs.h b/drivers/input/keyboard/cypressbln/issp_defs.h new file mode 100644 index 0000000..5878300 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_defs.h @@ -0,0 +1,101 @@ +// filename: ISSP_Defs.h +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +--------------------------------------------------------------------------*/ +#ifndef INC_ISSP_DEFS +#define INC_ISSP_DEFS + +#include "issp_directives.h" + +// Block-Verify Uses 64-Bytes of RAM +// #define TARGET_DATABUFF_LEN 64 +#define TARGET_DATABUFF_LEN 128 // **** CY8C20x66 Device **** + +// The number of Flash blocks in each part is defined here. This is used in +// main programming loop when programming and verifying the blocks. + +#ifdef CY8CTMx30x // **** CY8C20x66 Device **** +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 256 +#define SECURITY_BYTES_PER_BANK 64 +#endif + +#ifdef CY8C20x66 // **** CY8C20x66 Device **** +#ifdef CY8C20246 // **** CY8C20x66 Device **** +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 128 +#define SECURITY_BYTES_PER_BANK 64 +#elif defined(CY8C20236) +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 64 +#define SECURITY_BYTES_PER_BANK 64 +#else +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 256 +#define SECURITY_BYTES_PER_BANK 64 +#endif +#endif +#ifdef CY8C21x23 +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 64 +#define SECURITY_BYTES_PER_BANK 64 +#endif +#ifdef CY8C21x34 +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 128 +#define SECURITY_BYTES_PER_BANK 64 +#endif +#ifdef CY8C24x23A +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 64 +#define SECURITY_BYTES_PER_BANK 64 +#endif +#ifdef CY8C24x94 +#define NUM_BANKS 2 +#define BLOCKS_PER_BANK 128 +#define SECURITY_BYTES_PER_BANK 32 +#endif +#ifdef CY8C27x43 +#define NUM_BANKS 1 +#define BLOCKS_PER_BANK 256 +#define SECURITY_BYTES_PER_BANK 64 +#endif +#ifdef CY8C29x66 +#define NUM_BANKS 4 +#define BLOCKS_PER_BANK 128 +#define SECURITY_BYTES_PER_BANK 32 +#endif +#endif //(INC_ISSP_DEFS) +#endif //(PROJECT_REV_) +//end of file ISSP_Defs.h diff --git a/drivers/input/keyboard/cypressbln/issp_delays.h b/drivers/input/keyboard/cypressbln/issp_delays.h new file mode 100644 index 0000000..e727310 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_delays.h @@ -0,0 +1,87 @@ +// filename: ISSP_Delays.h +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +-----------------------------------------------------------------------------*/ +#ifndef INC_ISSP_DELAYS +#define INC_ISSP_DELAYS + +// The Delay() routine, in ISSP_Driver_Routines.c, has a delay of n+3 usec, +// where n is the value passed to the routine. This is true for the m8c micro- +// processor in the PSoC when it is running at a CPU clock of 24MHz. +// +// PROCESSOR_SPECIFIC +// If another processor is used, or if the m8c is running at a slower clock +// speed, then the delay parameters will be different. This file makes changing +// the delays simpiler when porting the program to other processors. + +// DELAY_M is the slope of the Delay = Mx + B equation +#define DELAY_M 1 +// DELAY_B is the offset of the delay in Delay = Mx + B. +#define DELAY_B 3 + +/////////////////////////////////////////////////////////////////////////////// +// CAUTION: +// For the above parameters the minimum delay value is 3 (this would result in +// 0 being passed for a minimum delay. A value less than 3 would actually +// create a negative number, causing a very long delay +/////////////////////////////////////////////////////////////////////////////// + +// TRANSITION_TIMEOUT is a loop counter for a 100msec timeout when waiting for +// a high-to-low transition. This is used in the polling loop of +// fDetectHiLoTransition(). Each pass through the loop takes approximately 15 +// usec. 100 msec is about 6740 loops. 13480 +#define TRANSITION_TIMEOUT 0x100000 //6740 + +// XRES_DELAY is the time duration for which XRES is asserted. This defines +// a 63 usec delay. +// The minimum Xres time (from the device datasheet) is 10 usec. +//mhsong #define XRES_CLK_DELAY ((63 - DELAY_B) / DELAY_M) +#define XRES_CLK_DELAY 63 + +// POWER_CYCLE_DELAY is the time required when power is cycled to the target +// device to create a power reset after programming has been completed. The +// actual time of this delay will vary from system to system depending on the +// bypass capacitor size. A delay of 150 usec is used here. +//mhsong #define POWER_CYCLE_DELAY ((150 - DELAY_B) / DELAY_M) +#define POWER_CYCLE_DELAY 150 + +// DELAY_100us delays 100 usec. This is used in fXRESInitializeTargetForISSP to +// time the wait for Vdd to become stable after a power up. A loop runs 10 of +// these for a total delay of 1 msec. +//mhsong #define DELAY100us ((100 - DELAY_B) / DELAY_M) +#define DELAY100us 100 + +#endif //(INC_ISSP_DELAYS) +#endif //(PROJECT_REV_) +//end of file ISSP_Delays.h diff --git a/drivers/input/keyboard/cypressbln/issp_directives.h b/drivers/input/keyboard/cypressbln/issp_directives.h new file mode 100644 index 0000000..b6cdddc --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_directives.h @@ -0,0 +1,474 @@ +// filename: ISSP_Directives.h +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +----------------------------------------------------------------------------*/ + +// --------------------- Compiler Directives ---------------------------------- +#ifndef INC_ISSP_DIRECTIVES +#define INC_ISSP_DIRECTIVES + +// This directive will enable a Genral Purpose test-point on P1.7 +// It can be toggled as needed to measure timing, execution, etc... +// A "Test Point" sets a GPIO pin of the host processor high or low. This GPIO +// pin can be observed with an oscilloscope to verify the timing of key +// programming steps. TPs have been added in main() that set Port 0, pin 1 +// high during bulk erase, during each block write and during security write. +// The timing of these programming steps should be verified as correct as part +// of the validation process of the final program. + +//JBA +//#define USE_TP + +// **************************************************************************** +// ************* USER ATTENTION REQUIRED: TARGET SUPPLY VOLTAGE *************** +// **************************************************************************** +// This directive causes the proper Initialization vector #3 to be sent +// to the Target, based on what the Target Vdd programming voltage will +// be. Either 5V (if #define enabled) or 3.3V (if #define disabled). + +//JBA +//#define TARGET_VOLTAGE_IS_5V + +// **************************************************************************** +// **************** USER ATTENTION REQUIRED: PROGRAMMING MODE ***************** +// **************************************************************************** +// This directive selects whether code that uses reset programming mode or code +// that uses power cycle programming is use. Reset programming mode uses the +// external reset pin (XRES) to enter programming mode. Power cycle programming +// mode uses the power-on reset to enter programming mode. +// Applying signals to various pins on the target device must be done in a +// deliberate order when using power cycle mode. Otherwise, high signals to GPIO +// pins on the target will power the PSoC through the protection diodes. + +//JBA +// choose the RESET MODE or POWER MODE +// #define RESET_MODE + +// **************************************************************************** +// ****************** USER ATTENTION REQUIRED: TARGET PSOC ******************** +// **************************************************************************** +// The directives below enable support for various PSoC devices. The root part +// number to be programmed should be un-commented so that its value becomes +// defined. All other devices should be commented out. +// Select one device to be supported below: + +/*************** CY8CTMA30x, CY8CTMG30x, CY8CTST30x series by KIMC, 2009.08.11 ***********************************/ +//#define CY8CTST300_36 // CY8CTST300_36LQXI // 2009.08.11, not tested. +//#define CY8CTST300_48 // CY8CTST300_48LTXI // 2009.08.11, not tested. +//#define CY8CTST300_49 // CY8CTST300_49FNXI // 2009.08.11, not tested. +//#define CY8CTMA300_36 // CY8CTMA300_36LQXI // 2009.08.11, Test OK. +//#define CY8CTMA300_48 // CY8CTMA300_48LTXI // 2009.08.11, not tested. +//#define CY8CTMA300_49 // CY8CTMA300_49FNXI // 2009.08.11, not tested. +//#define CY8CTMG300_36 // CY8CTMG300_36LQXI // 2009.08.11, not tested. +//#define CY8CTMG300_48 // CY8CTMG300_48LTXI // 2009.08.11, not tested. +//#define CY8CTMG300_49 // CY8CTMG300_49FNXI // 2009.08.11, not tested. +//#define CY8CTMG300B_36 // CY8CTMG300B_36LQXI // 2009.08.11, not tested. +//#define CY8CTMA300B_36 // CY8CTMA300B_36LQXI // 2009.08.11, not tested. +//#define CY8CTST300B_36 // CY8CTST300B_36LQXI // 2009.08.11, not tested. +//#define CY8CTMA301_36 // CY8CTMA301_36LQXI // 2009.08.11, not tested. +//#define CY8CTMA301_48 // CY8CTMA301_48LTXI // 2009.08.11, not tested. +//#define CY8CTMA301D_36 // CY8CTMA301D_36LQXI // 2009.08.11, not tested. +//#define CY8CTMA301D_48 // CY8CTMA301D_48LTXI // 2009.08.11, not tested. +//#define CY8CTMA300D_36 // CY8CTMA300D_36LQXI // 2009.08.11, not tested. +//#define CY8CTMA300D_48 // CY8CTMA300D_48LTXI // 2009.08.11, not tested. +//#define CY8CTMA300D_49 // CY8CTMA300D_49FNXIT // 2009.08.11, not tested. +/****************************************************************************************************/ + +/*************** CY8CTMG/TST series modified by KJHW, 2009.08.14 *********************************************/ +//#define CY8CTMG110 +//#define CY8CTST200_24PIN +//#define CY8CTST200_32PIN +//#define CY8CTMG200_24PIN +//#define CY8CTMG200_32PIN +/***************************************************************************************************/ + +#define CY8C20236 +// **** CY8C20x66 devices **** +//#define CY8C20246 /// 2009.03.26. kimc +//#define CY8C20266 +//#define CY8C20366 +//#define CY8C20466 +//#define CY8C20566 +//#define CY8C20666 +//#define CY8C20066 +//#define CY8C200661 + +// **** CY8C21x23 devices **** +//#define CY8C21123 +//#define CY8C21223 +//#define CY8C21323 +//#define CY8C21002 + +// **** CY8C21x34 devices **** +//#define CY8C21234 +//#define CY8C21334 +//#define CY8C21434 +//#define CY8C21534 +//#define CY8C21634 +//#define CY8C21001 + +// **** CY8C24x23A devices **** +//#define CY8C24123A +//#define CY8C24223A +//#define CY8C24423A +//#define CY8C24000A + +// **** CY8C24x94 devices **** +//#define CY8C24794 +//#define CY8C24894 +//#define CY8C24994 +//#define CY8C24094 + +// **** CY8C27x34 devices **** +//#define CY8C27143 +//#define CY8C27243 +//#define CY8C27443 +//#define CY8C27543 +//#define CY8C27643 +//#define CY8C27002 + +// **** CY8C29x66 devices **** +//#define CY8C29466 +//#define CY8C29566 +//#define CY8C29666 +//#define CY8C29866 +//#define CY8C29002 + +//----------------------------------------------------------------------------- +// This section sets the Family that has been selected. These are used to +// simplify other conditional compilation blocks. +//----------------------------------------------------------------------------- + +/*************** CY8CTMA30x, CY8CTMG30x, CY8CTST30x series by KIMC, 2009.08.11 ***********************************/ +#ifdef CY8CTST300_36 // CY8CTST300_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTST300_48 // CY8CTST300_48LTXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTST300_49 // CY8CTST300_49FNXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300_36 // CY8CTMA300_36LQXI // 2009.08.11, test OK +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300_48 // CY8CTMA300_48LTXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300_49 // CY8CTMA300_49FNXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMG300_36 // CY8CTMG300_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMG300_48 // CY8CTMG300_48LTXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMG300_49 // CY8CTMG300_49FNXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMG300B_36 // CY8CTMG300B_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300B_36 // CY8CTMA300B_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTST300B_36 // CY8CTST300B_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA301_36 // CY8CTMA301_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA301_48 // CY8CTMA301_48LTXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA301D_36 // CY8CTMA301D_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA301D_48 // CY8CTMA301D_48LTXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300D_36 // CY8CTMA300D_36LQXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300D_48 // CY8CTMA300D_48LTXI // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +#ifdef CY8CTMA300D_49 // CY8CTMA300D_49FNXIT // 2009.08.11, not tested. +#define CY8CTMx30x +#define CY8C20x66 +#endif +/**************************************************/ + +/*************** CY8CTMG/TST series modified by KJHW, 2009.08.14 *********************************************/ + +#ifdef CY8CTMG110 +#define CY8C21x34 +#endif +#ifdef CY8CTST200_24PIN +#define CY8C20x66 +#endif +#ifdef CY8CTST200_32PIN +#define CY8C20x66 +#endif +#ifdef CY8CTMG200_24PIN +#define CY8C20x66 +#endif +#ifdef CY8CTMG200_32PIN +#define CY8C20x66 +#endif + +/***************************************************************************************************/ +#ifdef CY8C20236 +#define CY8C20x66 +#endif +#ifdef CY8C20246 /// 2009.03.26. kimc +#define CY8C20x66 +#endif +#ifdef CY8C20266 +#define CY8C20x66 +#endif +#ifdef CY8C20366 +#define CY8C20x66 +#endif +#ifdef CY8C20466 +#define CY8C20x66 +#endif +#ifdef CY8C20566 +#define CY8C20x66 +#endif +#ifdef CY8C20666 +#define CY8C20x66 +#endif +#ifdef CY8C20066 +#define CY8C20x66 +#endif +#ifdef CY8C200661 +#define CY8C20x66 +#endif + +#ifdef CY8C21123 +#define CY8C21x23 +#endif +#ifdef CY8C21223 +#define CY8C21x23 +#endif +#ifdef CY8C21323 +#define CY8C21x23 +#endif +#ifdef CY8C21002 +#define CY8C21x23 +#endif +#ifdef CY8C21234 +#define CY8C21x34 +#endif +#ifdef CY8C21334 +#define CY8C21x34 +#endif +#ifdef CY8C21434 +#define CY8C21x34 +#endif +#ifdef CY8C21534 +#define CY8C21x34 +#endif +#ifdef CY8C21634 +#define CY8C21x34 +#endif +#ifdef CY8C21001 +#define CY8C21x34 +#endif +#ifdef CY8C24123A +#define CY8C24x23A +#endif +#ifdef CY8C24223A +#define CY8C24x23A +#endif +#ifdef CY8C24423A +#define CY8C24x23A +#endif +#ifdef CY8C24000A +#define CY8C24x23A +#endif +#ifdef CY8C24794 +#define CY8C24x94 +#endif +#ifdef CY8C24894 +#define CY8C24x94 +#endif +#ifdef CY8C24994 +#define CY8C24x94 +#endif +#ifdef CY8C24094 +#define CY8C24x94 +#endif +#ifdef CY8C27143 +#define CY8C27x43 +#endif +#ifdef CY8C27243 +#define CY8C27x43 +#endif +#ifdef CY8C27443 +#define CY8C27x43 +#endif +#ifdef CY8C27543 +#define CY8C27x43 +#endif +#ifdef CY8C27643 +#define CY8C27x43 +#endif +#ifdef CY8C27002 +#define CY8C27x43 +#endif +#ifdef CY8C29466 +#define CY8C29x66 +#endif +#ifdef CY8C29566 +#define CY8C29x66 +#endif +#ifdef CY8C29666 +#define CY8C29x66 +#endif +#ifdef CY8C29866 +#define CY8C29x66 +#endif +#ifdef CY8C29002 +#define CY8C29x66 +#endif + +//----------------------------------------------------------------------------- +// The directives below are used for Krypton. +// See the Krypton programming spec 001-15870 rev *A for more details. (The +// spec uses "mnemonics" instead of "directives" +//----------------------------------------------------------------------------- +#ifdef CY8C20x66 +#define TSYNC + +#define ID_SETUP_1 //PTJ: ID_SETUP_1 is similar to init1_v +#define ID_SETUP_2 //PTJ: ID_SETUP_2 is similar to init2_v +#define SET_BLOCK_NUM +#define CHECKSUM_SETUP //PTJ: CHECKSUM_SETUP_20x66 is the same as CHECKSUM-SETUP in 001-15870 +#define READ_CHECKSUM +#define PROGRAM_AND_VERIFY //PTJ: PROGRAM_BLOCK_20x66 is the same as PROGRAM-AND-VERIFY in 001-15870 +#define ERASE +#define SECURE +#define READ_SECURITY +#define READ_WRITE_SETUP +#define WRITE_BYTE +#define VERIFY_SETUP +#define READ_STATUS +#define READ_BYTE + //READ_ID_WORD //PTJ: 3rd Party Progrmmer will have to write code to handle this directive, we do it out own way in this code, see read_id_v +#endif +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// The directives below are used to define various sets of vectors that differ +// for more than one set of PSoC parts. +//----------------------------------------------------------------------------- +// **** Select a Checksum Setup Vector **** +#ifdef CY8C21x23 +#define CHECKSUM_SETUP_21_27 +#endif +#ifdef CY8C21x34 +#define CHECKSUM_SETUP_21_27 +#endif +#ifdef CY8C24x23A +#define CHECKSUM_SETUP_24_24A +#endif +#ifdef CY8C24x94 +#define CHECKSUM_SETUP_24_29 +#endif +#ifdef CY8C27x43 +#define CHECKSUM_SETUP_21_27 +#endif +#ifdef CY8C29x66 +#define CHECKSUM_SETUP_24_29 +#endif + +// **** Select a Program Block Vector **** + +#ifdef CY8C21x23 +#define PROGRAM_BLOCK_21_24_29 +#endif +#ifdef CY8C21x34 +#define PROGRAM_BLOCK_21_24_29 +#endif +#ifdef CY8C24x23A +#define PROGRAM_BLOCK_21_24_29 +#endif +#ifdef CY8C24x94 +#define PROGRAM_BLOCK_21_24_29 +#endif +#ifdef CY8C27x43 +#define PROGRAM_BLOCK_27 +#endif +#ifdef CY8C29x66 +#define PROGRAM_BLOCK_21_24_29 +#endif + +//----------------------------------------------------------------------------- +// The directives below are used to control switching banks if the device is +// has multiple banks of Flash. +//----------------------------------------------------------------------------- +// **** Select a Checksum Setup Vector **** +#ifdef CY8C24x94 +#define MULTI_BANK +#endif +#ifdef CY8C29x66 +#define MULTI_BANK +#endif + +// ---------------------------------------------------------------------------- +#endif //(INC_ISSP_DIRECTIVES) +#endif //(PROJECT_REV_) +//end of file ISSP_Directives.h diff --git a/drivers/input/keyboard/cypressbln/issp_driver_routines.c b/drivers/input/keyboard/cypressbln/issp_driver_routines.c new file mode 100644 index 0000000..a3b4fcf --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_driver_routines.c @@ -0,0 +1,498 @@ +// filename: ISSP_Driver_Routines.c +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +--------------------------------------------------------------------------*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//mhsong #include // part specific constants and macros +//mhsong #include "PSoCAPI.h" // PSoC API definitions for all User Modules +#include "issp_defs.h" +#include "issp_errors.h" +#include "issp_directives.h" +#include "u1-cypress-gpio.h" + +extern unsigned char bTargetDataPtr; +extern unsigned char abTargetDataOUT[TARGET_DATABUFF_LEN]; + +/* enable ldo11 */ +extern int touchkey_ldo_on(bool on); + +// ****************************** PORT BIT MASKS ****************************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +#define SDATA_PIN 0x80 // P1.0 -> P1.4 +#define SCLK_PIN 0x40 // P1.1 -> P1.3 +#define XRES_PIN 0x40 // P2.0 -> P1.6 +#define TARGET_VDD 0x08 // P2.1 + +unsigned int nBlockCount = 1; // test, KIMC + +extern unsigned char firmware_data[]; + +// ((((((((((((((((((((((( DEMO ISSP SUBROUTINE SECTION ))))))))))))))))))))))) +// ((((( Demo Routines can be deleted in final ISSP project if not used ))))) +// ((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))) + +// ============================================================================ +// InitTargetTestData() +// !!!!!!!!!!!!!!!!!!FOR TEST!!!!!!!!!!!!!!!!!!!!!!!!!! +// PROCESSOR_SPECIFIC +// Loads a 64-Byte array to use as test data to program target. Ultimately, +// this data should be fed to the Host by some other means, ie: I2C, RS232, +// etc. Data should be derived from hex file. +// Global variables affected: +// bTargetDataPtr +// abTargetDataOUT +// ============================================================================ +void InitTargetTestData(unsigned char bBlockNum, unsigned char bBankNum) +{ + // create unique data for each block + for (bTargetDataPtr = 0; bTargetDataPtr < TARGET_DATABUFF_LEN; + bTargetDataPtr++) { + abTargetDataOUT[bTargetDataPtr] = nBlockCount; + // abTargetDataOUT[bTargetDataPtr] = bTargetDataPtr + bBlockNum + bBankNum; + } + nBlockCount++; +} + +// ============================================================================ +// LoadArrayWithSecurityData() +// !!!!!!!!!!!!!!!!!!FOR TEST!!!!!!!!!!!!!!!!!!!!!!!!!! +// PROCESSOR_SPECIFIC +// Most likely this data will be fed to the Host by some other means, ie: I2C, +// RS232, etc., or will be fixed in the host. The security data should come +// from the hex file. +// bStart - the starting byte in the array for loading data +// bLength - the number of byte to write into the array +// bType - the security data to write over the range defined by bStart and +// bLength +// ============================================================================ +void LoadArrayWithSecurityData(unsigned char bStart, unsigned char bLength, + unsigned char bType) +{ + // Now, write the desired security-bytes for the range specified + for (bTargetDataPtr = bStart; bTargetDataPtr < bLength; + bTargetDataPtr++) { + abTargetDataOUT[bTargetDataPtr] = bType; + } +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// Delay() +// This delay uses a simple "nop" loop. With the CPU running at 24MHz, each +// pass of the loop is about 1 usec plus an overhead of about 3 usec. +// total delay = (n + 3) * 1 usec +// To adjust delays and to adapt delays when porting this application, see the +// ISSP_Delays.h file. +// **************************************************************************** +void Delay(unsigned char n) // by KIMC +{ + udelay(n); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// LoadProgramData() +// The final application should load program data from HEX file generated by +// PSoC Designer into a 64 byte host ram buffer. +// 1. Read data from next line in hex file into ram buffer. One record +// (line) is 64 bytes of data. +// 2. Check host ram buffer + record data (Address, # of bytes) against hex +// record checksum at end of record line +// 3. If error reread data from file or abort +// 4. Exit this Function and Program block or verify the block. +// This demo program will, instead, load predetermined data into each block. +// The demo does it this way because there is no comm link to get data. +// **************************************************************************** +void LoadProgramData(unsigned char bBlockNum, unsigned char bBankNum) +{ + // >>> The following call is for demo use only. <<< + // Function InitTargetTestData fills buffer for demo + // InitTargetTestData(bBlockNum, bBankNum); + // create unique data for each block + int dataNum = 0; + for (dataNum = 0; dataNum < TARGET_DATABUFF_LEN; dataNum++) { + abTargetDataOUT[dataNum] = + firmware_data[bBlockNum * TARGET_DATABUFF_LEN + dataNum]; + // abTargetDataOUT[bTargetDataPtr] = bTargetDataPtr + bBlockNum + bBankNum; + } + + // Note: + // Error checking should be added for the final version as noted above. + // For demo use this function just returns VOID. +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// fLoadSecurityData() +// Load security data from hex file into 64 byte host ram buffer. In a fully +// functional program (not a demo) this routine should do the following: +// 1. Read data from security record in hex file into ram buffer. +// 2. Check host ram buffer + record data (Address, # of bytes) against hex +// record checksum at end of record line +// 3. If error reread security data from file or abort +// 4. Exit this Function and Program block +// In this demo routine, all of the security data is set to unprotected (0x00) +// and it returns. +// This function always returns PASS. The flag return is reserving +// functionality for non-demo versions. +// **************************************************************************** +signed char fLoadSecurityData(unsigned char bBankNum) +{ + // >>> The following call is for demo use only. <<< + // Function LoadArrayWithSecurityData fills buffer for demo +// LoadArrayWithSecurityData(0,SECURITY_BYTES_PER_BANK, 0x00); + LoadArrayWithSecurityData(0, SECURITY_BYTES_PER_BANK, 0xFF); //PTJ: 0x1B (00 01 10 11) is more interesting security data than 0x00 for testing purposes + + // Note: + // Error checking should be added for the final version as noted above. + // For demo use this function just returns PASS. + return (PASS); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// fSDATACheck() +// Check SDATA pin for high or low logic level and return value to calling +// routine. +// Returns: +// 0 if the pin was low. +// 1 if the pin was high. +// **************************************************************************** +unsigned char fSDATACheck(void) +{ + gpio_direction_input(_3_TOUCH_SDA_28V); + if (gpio_get_value(_3_TOUCH_SDA_28V)) + return (1); + else + return (0); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SCLKHigh() +// Set the SCLK pin High +// **************************************************************************** +void SCLKHigh(void) +{ + gpio_direction_output(_3_TOUCH_SCL_28V, 1); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SCLKLow() +// Make Clock pin Low +// **************************************************************************** +void SCLKLow(void) +{ + gpio_direction_output(_3_TOUCH_SCL_28V, 0); +} + +#ifndef RESET_MODE // Only needed for power cycle mode +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetSCLKHiZ() +// Set SCLK pin to HighZ drive mode. +// **************************************************************************** +void SetSCLKHiZ(void) +{ + gpio_direction_input(_3_TOUCH_SCL_28V); +} +#endif + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetSCLKStrong() +// Set SCLK to an output (Strong drive mode) +// **************************************************************************** +void SetSCLKStrong(void) +{ + //gpio_direction_output(_3_TOUCH_SCL_28V, 1); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetSDATAHigh() +// Make SDATA pin High +// **************************************************************************** +void SetSDATAHigh(void) +{ + gpio_direction_output(_3_TOUCH_SDA_28V, 1); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetSDATALow() +// Make SDATA pin Low +// **************************************************************************** +void SetSDATALow(void) +{ + gpio_direction_output(_3_TOUCH_SDA_28V, 0); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetSDATAHiZ() +// Set SDATA pin to an input (HighZ drive mode). +// **************************************************************************** +void SetSDATAHiZ(void) +{ + gpio_direction_input(_3_TOUCH_SDA_28V); // ENA-> DIS +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetSDATAStrong() +// Set SDATA for transmission (Strong drive mode) -- as opposed to being set to +// High Z for receiving data. +// **************************************************************************** +void SetSDATAStrong(void) +{ + //gpio_direction_output(_3_TOUCH_SDA_28V, 1); +} + +#ifdef RESET_MODE +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetXRESStrong() +// Set external reset (XRES) to an output (Strong drive mode). +// **************************************************************************** +void SetXRESStrong(void) +{ + //gpio_tlmm_config(EXT_TSP_RST); + //gpio_out(EXT_TSP_RST, GPIO_HIGH_VALUE); + //clk_busy_wait(1000); + //clk_busy_wait(1000); + //clk_busy_wait(1000); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// AssertXRES() +// Set XRES pin High +// **************************************************************************** +void AssertXRES(void) +{ +#if 0 + gpio_tlmm_config(EXT_TSP_RST); + gpio_out(EXT_TSP_RST, GPIO_HIGH_VALUE); + clk_busy_wait(1000); + clk_busy_wait(1000); + clk_busy_wait(1000); +#endif +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// DeassertXRES() +// Set XRES pin low. +// **************************************************************************** +void DeassertXRES(void) +{ + //gpio_out(EXT_TSP_RST, GPIO_LOW_VALUE); +} +#else +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// SetTargetVDDStrong() +// Set VDD pin (PWR) to an output (Strong drive mode). +// **************************************************************************** +void SetTargetVDDStrong(void) +{ +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// ApplyTargetVDD() +// Provide power to the target PSoC's Vdd pin through a GPIO. +// **************************************************************************** +void ApplyTargetVDD(void) +{ + int ret; + + gpio_direction_input(_3_TOUCH_SDA_28V); + gpio_direction_input(_3_TOUCH_SCL_28V); + gpio_direction_output(_3_GPIO_TOUCH_EN, 1); + + /* enable ldo */ + ret = touchkey_ldo_on(1); + if (ret == 0) + printk(KERN_ERR "[Touchkey]regulator get fail!!!\n"); + + mdelay(1); +} + +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// RemoveTargetVDD() +// Remove power from the target PSoC's Vdd pin. +// **************************************************************************** +void RemoveTargetVDD(void) +{ + gpio_direction_output(_3_GPIO_TOUCH_EN, 0); + + touchkey_ldo_on(0); +} +#endif + +#ifdef USE_TP +// ********************* LOW-LEVEL ISSP SUBROUTINE SECTION ******************** +// **************************************************************************** +// **** PROCESSOR SPECIFIC **** +// **************************************************************************** +// **** USER ATTENTION REQUIRED **** +// **************************************************************************** +// A "Test Point" sets a GPIO pin of the host processor high or low. +// This GPIO pin can be observed with an oscilloscope to verify the timing of +// key programming steps. TPs have been added in main() that set Port 0, pin 1 +// high during bulk erase, during each block write and during security write. +// The timing of these programming steps should be verified as correct as part +// of the validation process of the final program. +// **************************************************************************** +void InitTP(void) +{ +} + +void SetTPHigh(void) +{ +} + +void SetTPLow(void) +{ +} + +void ToggleTP(void) +{ +} +#endif +#endif //(PROJECT_REV_) +//end of file ISSP_Drive_Routines.c diff --git a/drivers/input/keyboard/cypressbln/issp_errors.h b/drivers/input/keyboard/cypressbln/issp_errors.h new file mode 100644 index 0000000..e6f0673 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_errors.h @@ -0,0 +1,65 @@ +// filename: ISSP_Errors.h +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +--------------------------------------------------------------------------*/ +#ifndef INC_ISSP_ERRORS +#define INC_ISSP_ERRORS + +// The following are defines for error messages from the ISSP program. +#define PASS 0 +// PASS is used to indicate that a function completed successfully. +#define ERROR -1 +// ERROR is a generic failure used within lower level functions before the +// error is reported. This should not be seen as an error that is reported +// from main. +#define INIT_ERROR 1 +// INIT_ERROR means a step in chip initialization failed. +#define SiID_ERROR 2 +// SiID_ERROR means that the Silicon ID check failed. This happens if the +// target part does not match the device type that the ISSP program is +// configured for. +#define ERASE_ERROR 3 +// ERASE_ERROR means that the bulk erase step failed. +#define BLOCK_ERROR 4 +// BLOCK_ERROR means that a step in programming a Flash block or the verify +// of the block failed. +#define VERIFY_ERROR 5 +// VERIFY_ERROR means that the checksum verification failed. +#define SECURITY_ERROR 6 +// SECURITY_ERROR means that the write of the security information failed. +#define STATUS_ERROR 7 + +#endif //(INC_ISSP_ERRORS) +#endif //(PROJECT_REV_) +//end of file ISSP_Errors.h diff --git a/drivers/input/keyboard/cypressbln/issp_extern.h b/drivers/input/keyboard/cypressbln/issp_extern.h new file mode 100644 index 0000000..fad9122 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_extern.h @@ -0,0 +1,99 @@ +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. +*/ + +#ifndef INC_ISSP_EXTERN +#define INC_ISSP_EXTERN + +#include "issp_directives.h" + +extern signed char fXRESInitializeTargetForISSP(void); +extern signed char fPowerCycleInitializeTargetForISSP(void); +extern signed char fEraseTarget(void); +extern unsigned int iLoadTarget(void); +extern void ReStartTarget(void); +extern signed char fVerifySiliconID(void); +extern signed char fAccTargetBankChecksum(unsigned int *); +extern void SetBankNumber(unsigned char); +extern signed char fProgramTargetBlock(unsigned char, unsigned char); +extern signed char fVerifyTargetBlock(unsigned char, unsigned char); +extern signed char fVerifySetup(unsigned char, unsigned char); +extern signed char fReadByteLoop(void); /*PTJ: read bytes after VERIFY-SETUP*/ +extern signed char fSecureTargetFlash(void); + +extern signed char fReadStatus(void); /*PTJ: READ-STATUS*/ +extern signed char fReadCalRegisters(void); +extern signed char fReadWriteSetup(void); /*PTJ: READ-WRITE-SETUP*/ +extern signed char fReadSecurity(void); /*PTJ: READ-SECURITY*/ + +extern signed char fSyncDisable(void); /*PTJ: SYNC-DISABLE rev 307*/ +extern signed char fSyncEnable(void); /*PTJ: SYNC-ENABLE rev 307*/ + +extern void InitTargetTestData(void); +extern void LoadArrayWithSecurityData(unsigned char, unsigned char, + unsigned char); + +extern void LoadProgramData(unsigned char, unsigned char); +extern signed char fLoadSecurityData(unsigned char); +extern void Delay(unsigned char); +extern unsigned char fSDATACheck(void); +extern void SCLKHigh(void); +extern void SCLKLow(void); +#ifndef RESET_MODE /*only needed when power cycle mode*/ +extern void SetSCLKHiZ(void); +#endif +extern void SetSCLKStrong(void); +extern void SetSDATAHigh(void); +extern void SetSDATALow(void); +extern void SetSDATAHiZ(void); +extern void SetSDATAStrong(void); +extern void AssertXRES(void); +extern void DeassertXRES(void); +extern void SetXRESStrong(void); +extern void ApplyTargetVDD(void); +extern void RemoveTargetVDD(void); +extern void SetTargetVDDStrong(void); + +extern unsigned char fIsError; + +#ifdef USE_TP +extern void InitTP(void); +extern void SetTPHigh(void); +extern void SetTPLow(void); +extern void ToggleTP(void); +#endif + +extern int ISSP_main(void); + +#endif /*(INC_ISSP_EXTERN)*/ +#endif /*(PROJECT_REV_)*/ diff --git a/drivers/input/keyboard/cypressbln/issp_main.c b/drivers/input/keyboard/cypressbln/issp_main.c new file mode 100644 index 0000000..a799e3b --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_main.c @@ -0,0 +1,931 @@ +// filename: main.c +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress��?product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +---------------------------------------------------------------------------*/ + +/* ############################################################################ + ################### CRITICAL PROJECT CONSTRAINTS ######################## + ############################################################################ + + ISSP programming can only occur within a temperature range of 5C to 50C. + - This project is written without temperature compensation and using + programming pulse-widths that match those used by programmers such as the + Mini-Prog and the ISSP Programmer. + This means that the die temperature of the PSoC device cannot be outside + of the above temperature range. + If a wider temperature range is required, contact your Cypress Semi- + conductor FAE or sales person for assistance. + + The project can be configured to program devices at 5V or at 3.3V. + - Initialization of the device is different for different voltages. The + initialization is hardcoded and can only be set for one voltage range. + The supported voltages ranges are 3.3V (3.0V to 3.6V) and 5V (4.75V to + 5.25V). See the device datasheet for more details. If varying voltage + ranges must be supported, contact your Cypress Semiconductor FAE or sales + person for assistance. + - ISSP programming for the 2.7V range (2.7V to 3.0V) is not supported. + + This program does not support programming all PSoC Devices + - It does not support obsoleted PSoC devices. A list of devices that are + not supported is shown here: + CY8C22x13 - not supported + CY8C24x23 - not supported (CY8C24x23A is supported) + CY8C25x43 - not supported + CY8C26x43 - not supported + - It does not suport devices that have not been released for sale at the + time this version was created. If you need to ISSP program a newly released + device, please contact Cypress Semiconductor Applications, your FAE or + sales person for assistance. + The CY8C20x23 devices are not supported at the time of this release. + + ############################################################################ + ##########################################################################*/ + +/* This program uses information found in Cypress Semiconductor application +// notes "Programming - In-System Serial Programming Protocol", AN2026. The +// version of this application note that applies to the specific PSoC that is +// being In-System Serial Programmed should be read and and understood when +// using this program. (http://www.cypress.com) + +// This project is included with releases of PSoC Programmer software. It is +// important to confirm that the latest revision of this software is used when +// it is used. The revision of this copy can be found in the Project History +// table below. +*/ + +/*///////////////////////////////////////////////////////////////////////////// +// PROJECT HISTORY +// date revision author description +// -------- -------- ------ ----------------------------------------------- +// 7/23/08 3.04 ptj 1. CPU clock speed set to to 12MHz (default) after +// TSYNC is disabled. Previously, it was being set +// to 3MHz on accident. This affects the following +// mnemonics: +// ID_SETUP_1, +// VERIFY_SETUP, +// ERASE, +// SECURE, +// CHECKSUM_SETUP, +// PROGRAM_AND_VERIFY, +// ID_SETUP_2, +// SYNC_DISABLE +// +// 6/06/08 3.03 ptj 1. The Verify_Setup section can tell when flash +// is fully protected. bTargetStatus[0] shows a +// 01h when flash is "W" Full Protection +// 7/23/08 2. READ-ID-WORD updated to read Revision ID from +// Accumulator A and X, registers T,F0h and T,F3h +// +// 5/30/08 3.02 ptj 1. All vectors work. +// 2. Main function will execute the +// following programming sequence: +// . id setup 1 +// . id setup 2 +// . erase +// . program and verify +// . verify (although not necessary) +// . secure flash +// . read security +// . checksum +// +// 05/28/08 3.01 ptj TEST CODE - NOT COMPLETE +// 1. The sequence surrounding PROGRAM-AND-VERIFY was +// improved and works according to spec. +// 2. The sequence surroudning VERIFY-SETUP was devel- +// oped and improved. +// 3. TSYNC Enable is used to in a limited way +// 4. Working Vectors: ID-SETUP-1, ID-SETUP-2, ERASE, +// PROGRAM-AND-VERIFY, SECURE, VERIFY-SETUP, +// CHECKSUM-SETUP +// 5. Un-tested Vectors: READ-SECURITY +// +// 05/23/08 3.00 ptj TEST CODE - NOT COMPLETE +// 1. This code is a test platform for the development +// of the Krypton (cy8c20x66) Programming Sequence. +// See 001-15870 rev *A. This code works on "rev B" +// silicon. +// 2. Host is Hydra 29000, Target is Krypton "rev B" +// 3. Added Krypton device support +// 4. TYSNC Enable/Disable is not used. +// 5. Working Vectors: ID-SETUP-1, ID-SETUP-2, ERASE, +// PROGRAM-AND-VERIFY, SECURE +// 6. Non-working Vectors: CHECKSUM-SETUP +// 7. Un-tested Vectors: READ-SECURITY, VERIFY-SETUP +// 8. Other minor (non-SROM) vectors not listed. +// +// 09/23/07 2.11 dkn 1. Added searchable comments for the HSSP app note. +// 2. Added new device vectors. +// 3. Verified write and erase pulsewidths. +// 4. Modified some functions for easier porting to +// other processors. +// +// 09/23/06 2.10 mwl 1. Added #define SECURITY_BYTES_PER_BANK to +// file issp_defs.h. Modified function +// fSecureTargetFlashMain() in issp_routines.c +// to use new #define. Modified function +// fLoadSecurityData() in issp_driver_routines.c +// to accept a bank number. Modified the secure +// data loop in main.c to program multiple banks. +// +// 2. Created function fAccTargetBankChecksum to +// read and add the target bank checksum to the +// referenced accumulator. This allows the +// checksum loop in main.c to function at the +// same level of abstraction as the other +// programming steps. Accordingly, modified the +// checksum loop in main.c. Deleted previous +// function fVerifyTargetChecksum(). +// +// 09/08/06 2.00 mwl 1. Array variable abTargetDataOUT was not +// getting intialized anywhere and compiled as a +// one-byte array. Modified issp_driver_routines.c +// line 44 to initialize with TARGET_DATABUFF_LEN. +// +// 2. Function void LoadProgramData(unsigned char +// bBlockNum) in issp_driver_routines.c had only +// one parameter bBlockNum but was being called +// from function main() with two parameters, +// LoadProgramData(bBankCounter, (unsigned char) +// iBlockCounter). Modified function +// LoadProgramData() to accept both parameters, +// and in turn modified InitTargetTestData() to +// accept and use both as well. +// +// 3. Corrected byte set_bank_number[1] +// inissp_vectors.h. Was 0xF2, correct value is +// 0xE2. +// +// 4. Corrected the logic to send the SET_BANK_NUM +// vectors per the AN2026B flow chart.The previous +// code version was sending once per block, but +// should have been once per bank. Removed the +// conditionally-compiled bank setting code blocks +// from the top of fProgramTargetBlock() and +// fVerifyTargetBlock() int issp_routines.c and +// created a conditionally-compiled subroutine in +// that same file called SetBankNumber(). Two +// conditionally-compiled calls to SetBankNumber() +// were added to main.c(). +// +// 5. Corrected CY8C29x66 NUM_BANKS and +// BLOCKS_PER_BANK definitions in ISSP_Defs.h. +// Was 2 and 256 respectively, correct values are +// 4 and 128. +// +// 6. Modified function fVerifyTargetChecksum() +// in issp_routines.c to read and accumulate multiple +// banks of checksums as required for targets +// CY8C24x94 and CY8C29x66. Would have kept same +// level of abstraction as other multi-bank functions +// in main.c, but this implementation impacted the +// code the least. +// +// 7. Corrected byte checksum_v[26] of +// CHECKSUM_SETUP_24_29 in issp_vectors.h. Was 0x02, +// correct value is 0x04. +// +// 06/30/06 1.10 jvy Added support for 24x94 and 29x66 devices. +// 06/09/06 1.00 jvy Changed CPU Clk to 12MHz (from 24MHz) so the +// host can run from 3.3V. +// Simplified init of security data. +// 06/05/06 0.06 jvy Version #ifdef added to each file to make sure +// all of the file are from the same revision. +// Added flags to prevent multiple includes of H +// files. +// Changed pre-determined data for demo to be +// unique for each block. +// Changed block verify to check all blocks after +// block programming has been completed. +// Added SetSCLKHiZ to explicitly set the Clk to +// HighZ before power cycle acquire. +// Fixed wrong vectors in setting Security. +// Fixed wrong vectors in Block program. +// Added support for pods +// 06/05/06 0.05 jvy Comments from code review. First copy checked +// into perforce. Code has been updated enough to +// compile, by implementing some comments and +// fixing others. +// 06/04/06 0.04 jvy made code more efficient in bReceiveByte(), and +// SendVector() by re-arranging so that local vars +// could be eliminated. +// added defines for the delays used in the code. +// 06/02/06 0.03 jvy added number of Flash block adjustment to +// programming. added power cycle programming +// mode support. This is the version initially +// sent out for peer review. +// 06/02/06 0.02 jvy added framework for multiple chip support from +// one source code set using compiler directives. +// added timeout to high-low trasition detection. +// added multiple error message to allow tracking +// of failures. +// 05/30/06 0.01 jvy initial version, +// created from DBD's issp_27xxx_v3 program. +/////////////////////////////////////////////////////////////////////////////*/ + +/* (((((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))))) + PSoC In-System Serial Programming (ISSP) Template + This PSoC Project is designed to be used as a template for designs that + require PSoC ISSP Functions. + + This project is based on the AN2026 series of Application Notes. That app + note should be referenced before any modifications to this project are made. + + The subroutines and files were created in such a way as to allow easy cut & + paste as needed. There are no customer-specific functions in this project. + This demo of the code utilizes a PSoC as the Host. + + Some of the subroutines could be merged, or otherwise reduced, but they have + been written as independently as possible so that the specific steps involved + within each function can easily be seen. By merging things, some code-space + savings could be realized. + + As is, and with all features enabled, the project consumes approximately 3500 + bytes of code space, and 19-Bytes of RAM (not including stack usage). The + Block-Verify requires a 64-Byte buffer for read-back verification. This same + buffer could be used to hold the (actual) incoming program data. + + Please refer to the compiler-directives file "directives.h" to see the various + features. + + The pin used in this project are assigned as shown below. The HOST pins are + arbitrary and any 3 pins could be used (the masks used to control the pins + must be changed). The TARGET pins cannot be changed, these are fixed function + pins on the PSoC. + The PWR pin is used to provide power to the target device if power cycle + programming mode is used. The compiler directive RESET_MODE in ISSP_directives.h + is used to select the programming mode. This pin could control the enable on + a voltage regulator, or could control the gate of a FET that is used to turn + the power to the PSoC on. + The TP pin is a Test Point pin that can be used signal from the host processor + that the program has completed certain tasks. Predefined test points are + included that can be used to observe the timing for bulk erasing, block + programming and security programming. + + SIGNAL HOST TARGET + --------------------- + SDATA P1.7 P1.0 + SCLK P1.6 P1.1 + XRES P2.0 XRES + PWR P2.3 Vdd + TP P0.7 n/a + + For test & demonstration, this project generates the program data internally. + It does not take-in the data from an external source such as I2C, UART, SPI, + etc. However, the program was written in such a way to be portable into such + designs. The spirit of this project was to keep it stripped to the minimum + functions required to do the ISSP functions only, thereby making a portable + framework for integration with other projects. + + The high-level functions have been written in C in order to be portable to + other processors. The low-level functions that are processor dependent, such + as toggling pins and implementing specific delays, are all found in the file + ISSP_Drive_Routines.c. These functions must be converted to equivalent + functions for the HOST processor. Care must be taken to meet the timing + requirements when converting to a new processor. ISSP timing information can + be found in Application Note AN2026. All of the sections of this program + that need to be modified for the host processor have "PROCESSOR_SPECIFIC" in + the comments. By performing a "Find in files" using "PROCESSOR_SPECIFIC" these + sections can easily be identified. + + The variables in this project use Hungarian notation. Hungarian prepends a + lower case letter to each variable that identifies the variable type. The + prefixes used in this program are defined below: + b = byte length variable, signed char and unsigned char + i = 2-byte length variable, signed int and unsigned int + f = byte length variable used as a flag (TRUE = 0, FALSE != 0) + ab = an array of byte length variables + + After this program has been ported to the desired host processor the timing + of the signals must be confirmed. The maximum SCLK frequency must be checked + as well as the timing of the bulk erase, block write and security write + pulses. + + The maximum SCLK frequency for the target device can be found in the device + datasheet under AC Programming Specifications with a Symbol of "Fsclk". + An oscilloscope should be used to make sure that no half-cycles (the high + time or the low time) are shorter than the half-period of the maximum + freqency. In other words, if the maximum SCLK frequency is 8MHz, there can be + no high or low pulses shorter than 1/(2*8MHz), or 62.5 nsec. + + The test point (TP) functions, enabled by the define USE_TP, provide an output + from the host processor that brackets the timing of the internal bulk erase, + block write and security write programming pulses. An oscilloscope, along with + break points in the PSoC ICE Debugger should be used to verify the timing of + the programming. The Application Note, "Host-Sourced Serial Programming" + explains how to do these measurements and should be consulted for the expected + timing of the erase and program pulses. + + ############################################################################ + ############################################################################ + +(((((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))))) */ + +/*---------------------------------------------------------------------------- +// C main line +//---------------------------------------------------------------------------- +*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include // part specific constants and macros +//#include "PSoCAPI.h" // PSoC API definitions for all User Modules +// ------ Declarations Associated with ISSP Files & Routines ------- +// Add these to your project as needed. +#include "issp_extern.h" +#include "issp_directives.h" +#include "issp_defs.h" +#include "issp_errors.h" +#include "u1-cypress-gpio.h" +/* ------------------------------------------------------------------------- */ + +/* enable ldo11 */ +extern int touchkey_ldo_on(bool on); + +unsigned char bBankCounter; +unsigned int iBlockCounter; +unsigned int iChecksumData; +unsigned int iChecksumTarget; + +//update version "eclair/vendor/samsung/apps/Lcdtest/src/com/sec/android/app/lcdtest/touch_firmware.java" +#if defined(CONFIG_MACH_Q1_BD) +#include "touchkey_fw_Q1.h" +#elif defined(CONFIG_ARIES_NTT) +#include "touchkey_fw_NTT.h" +#elif defined(CONFIG_TARGET_LOCALE_NA) +#include "touchkey_fw_NA.h" +#elif defined(CONFIG_TARGET_LOCALE_NAATT) +#include "touchkey_fw_NAATT.h" +#else +#include "touchkey_fw_U1.h" +#endif + +//////I2C + +#define EXT_I2C_SCL_HIGH \ + do { \ + int delay_count; \ + gpio_direction_output(_3_TOUCH_SCL_28V, 1); \ + gpio_direction_input(_3_TOUCH_SCL_28V); \ + delay_count = 100000; \ + while(delay_count--) \ + { \ + if(gpio_get_value(_3_TOUCH_SCL_28V)) \ + break; \ + udelay(1); \ + } \ + } while(0); +#define EXT_I2C_SCL_LOW gpio_direction_output(_3_TOUCH_SCL_28V, 0); +#define EXT_I2C_SDA_HIGH gpio_direction_output(_3_TOUCH_SDA_28V, 1); +#define EXT_I2C_SDA_LOW gpio_direction_output(_3_TOUCH_SDA_28V, 0); +#define TRUE 1 +#define FALSE 0 + +static void EXT_I2C_LOW(u32 delay) +{ + EXT_I2C_SDA_LOW; + //udelay(delay); + EXT_I2C_SCL_HIGH; + //udelay(delay); + EXT_I2C_SCL_LOW; + //udelay(delay); +} + +static void EXT_I2C_HIGH(u32 delay) +{ + EXT_I2C_SDA_HIGH; + //udelay(delay); + EXT_I2C_SCL_HIGH; + //udelay(delay); + EXT_I2C_SCL_LOW; + //udelay(delay); +} + +static void EXT_I2C_START(u32 delay) +{ + EXT_I2C_SDA_HIGH; + EXT_I2C_SCL_HIGH; + //udelay(delay); + EXT_I2C_SDA_LOW; + //udelay(delay); + EXT_I2C_SCL_LOW; + //udelay(delay); + +} + +static void EXT_I2C_END(u32 delay) +{ + EXT_I2C_SDA_LOW; + EXT_I2C_SCL_HIGH; + //udelay(delay); + EXT_I2C_SDA_HIGH; +} + +static int EXT_I2C_ACK(u32 delay) +{ + u32 ack; + + /* SDA -> Input */ + gpio_direction_input(_3_TOUCH_SDA_28V); + + udelay(delay); + EXT_I2C_SCL_HIGH; + //udelay(delay); + ack = gpio_get_value(_3_TOUCH_SDA_28V); + EXT_I2C_SCL_LOW; + //udelay(delay); + if (ack) + printk("EXT_I2C No ACK\n"); + + return ack; +} + +static void EXT_I2C_NACK(u32 delay) +{ + EXT_I2C_SDA_HIGH; + EXT_I2C_SCL_HIGH; + //udelay(delay); + EXT_I2C_SCL_LOW; + //udelay(delay); +} + +static void EXT_I2C_SEND_ACK(u32 delay) +{ + gpio_direction_output(_3_TOUCH_SDA_28V, 0); + EXT_I2C_SCL_HIGH; + //udelay(delay); + EXT_I2C_SCL_LOW; + //udelay(delay); + +} + +#define EXT_I2C_DELAY 1 +//============================================================ +// +// Porting section 6. I2C function calling +// +// Connect baseband i2c function +// +// Warning 1. !!!! Burst mode is not supported. Transfer 1 byte Only. +// +// Every i2c packet has to +// " START > Slave address > One byte > STOP " at download mode. +// +// Warning 2. !!!! Check return value of i2c function. +// +// _i2c_read_(), _i2c_write_() must return +// TRUE (1) if success, +// FALSE(0) if failed. +// +// If baseband i2c function returns different value, convert return value. +// ex> baseband_return = baseband_i2c_read( slave_addr, pData, cLength ); +// return ( baseband_return == BASEBAND_RETURN_VALUE_SUCCESS ); +// +// +// Warning 3. !!!! Check Slave address +// +// Slave address is '0x7F' at download mode. ( Diffrent with Normal touch working mode ) +// '0x7F' is original address, +// If shift << 1 bit, It becomes '0xFE' +// +//============================================================ + +static int +_i2c_read_(unsigned char SlaveAddr, unsigned char *pData, unsigned char cLength) +{ + unsigned int i; + int delay_count = 10000; + + EXT_I2C_START(EXT_I2C_DELAY); + + SlaveAddr = SlaveAddr << 1; + for (i = 8; i > 1; i--) { + if ((SlaveAddr >> (i - 1)) & 0x1) + EXT_I2C_HIGH(EXT_I2C_DELAY); + else + EXT_I2C_LOW(EXT_I2C_DELAY); + } + + EXT_I2C_HIGH(EXT_I2C_DELAY); //readwrite + + if (EXT_I2C_ACK(EXT_I2C_DELAY)) { + EXT_I2C_END(EXT_I2C_DELAY); + return FALSE; + } + + udelay(10); + gpio_direction_input(_3_TOUCH_SCL_28V); + delay_count = 100000; + while (delay_count--) { + if (gpio_get_value(_3_TOUCH_SCL_28V)) + break; + udelay(1); + } + while (cLength--) { + *pData = 0; + for (i = 8; i > 0; i--) { + //udelay(EXT_I2C_DELAY); + EXT_I2C_SCL_HIGH; + //udelay(EXT_I2C_DELAY); + *pData |= + (!!(gpio_get_value(_3_TOUCH_SDA_28V)) << (i - 1)); + //udelay(EXT_I2C_DELAY); + EXT_I2C_SCL_LOW; + //udelay(EXT_I2C_DELAY); + } + + if (cLength) { + EXT_I2C_SEND_ACK(EXT_I2C_DELAY); + udelay(10); + pData++; + gpio_direction_input(_3_TOUCH_SDA_28V); + gpio_direction_input(_3_TOUCH_SCL_28V); + delay_count = 100000; + while (delay_count--) { + if (gpio_get_value(_3_TOUCH_SCL_28V)) + break; + udelay(1); + } + } else + EXT_I2C_NACK(EXT_I2C_DELAY); + } + + EXT_I2C_END(EXT_I2C_DELAY); + + return (TRUE); + +} + +#define TOUCHKEY_ADDRESS 0x20 + +int get_touchkey_firmware(char *version) +{ + int retry = 3; + while (retry--) { + if (_i2c_read_(TOUCHKEY_ADDRESS, version, 3)) + return 0; + + } + return (-1); + //printk("%s F/W version: 0x%x, Module version:0x%x\n",__FUNCTION__, version[1],version[2]); +} + +/* ========================================================================= */ +// ErrorTrap() +// Return is not valid from main for PSOC, so this ErrorTrap routine is used. +// For some systems returning an error code will work best. For those, the +// calls to ErrorTrap() should be replaced with a return(bErrorNumber). For +// other systems another method of reporting an error could be added to this +// function -- such as reporting over a communcations port. +/* ========================================================================= */ +void ErrorTrap(unsigned char bErrorNumber) +{ +#ifndef RESET_MODE + // Set all pins to highZ to avoid back powering the PSoC through the GPIO + // protection diodes. + SetSCLKHiZ(); + SetSDATAHiZ(); + // If Power Cycle programming, turn off the target + RemoveTargetVDD(); +#endif + printk("\r\nErrorTrap: errorNumber: %d\n", bErrorNumber); + + // TODO: write retry code or some processing. + return; +// while (1); +} + +/* ========================================================================= */ +/* MAIN LOOP */ +/* Based on the diagram in the AN2026 */ +/* ========================================================================= */ + +int ISSP_main(void) +{ + unsigned long flags; + + // -- This example section of commands show the high-level calls to ------- + // -- perform Target Initialization, SilcionID Test, Bulk-Erase, Target --- + // -- RAM Load, FLASH-Block Program, and Target Checksum Verification. ---- + + // >>>> ISSP Programming Starts Here <<<< + // Acquire the device through reset or power cycle + s3c_gpio_setpull(_3_TOUCH_SCL_28V, S3C_GPIO_PULL_NONE); + s3c_gpio_setpull(_3_TOUCH_SDA_28V, S3C_GPIO_PULL_NONE); + gpio_direction_output(_3_GPIO_TOUCH_EN, 0); + /* disable ldo11 */ + touchkey_ldo_on(0); + msleep(1); +#ifdef RESET_MODE + gpio_tlmm_config(LED_26V_EN); + gpio_tlmm_config(EXT_TSP_SCL); + gpio_tlmm_config(EXT_TSP_SDA); + gpio_tlmm_config(LED_RST); + + gpio_out(LED_RST, GPIO_LOW_VALUE); + clk_busy_wait(10); + + gpio_out(LED_26V_EN, GPIO_HIGH_VALUE); + for (temp = 0; temp < 16; temp++) { + clk_busy_wait(1000); + dog_kick(); + } + + // Initialize the Host & Target for ISSP operations + printk("fXRESInitializeTargetForISSP Start\n"); + + //INTLOCK(); + local_save_flags(flags); + local_irq_disable(); + if (fIsError = fXRESInitializeTargetForISSP()) { + ErrorTrap(fIsError); + return fIsError; + } + //INTFREE(); +#else + //INTLOCK(); + local_irq_save(flags); + // Initialize the Host & Target for ISSP operations + if ((fIsError = fPowerCycleInitializeTargetForISSP())) { + ErrorTrap(fIsError); + return fIsError; + } + //INTFREE(); +#endif /* RESET_MODE */ + +#if 0 // issp_test_2010 block + printk("fXRESInitializeTargetForISSP END\n"); + + // Run the SiliconID Verification, and proceed according to result. + printk("fVerifySiliconID START\n"); +#endif + + //INTLOCK(); + fVerifySiliconID(); // .. error // issp_test_20100709 unblock +#if 0 + if (fIsError = fVerifySiliconID()) { + ErrorTrap(fIsError); + return fIsError; + } +#endif + + //INTFREE(); + local_irq_restore(flags); + //printk("fVerifySiliconID END\n"); // issp_test_2010 block + + // Bulk-Erase the Device. + //printk("fEraseTarget START\n"); // issp_test_2010 block + //INTLOCK(); + local_irq_save(flags); + if ((fIsError = fEraseTarget())) { + ErrorTrap(fIsError); + return fIsError; + } + //INTFREE(); + local_irq_restore(flags); + //printk("fEraseTarget END\n"); // issp_test_2010 block + + //==============================================================// + // Program Flash blocks with predetermined data. In the final application + // this data should come from the HEX output of PSoC Designer. + //printk("Program Flash Blocks Start\n"); + + iChecksumData = 0; // Calculte the device checksum as you go + for (bBankCounter = 0; bBankCounter < NUM_BANKS; bBankCounter++) //PTJ: NUM_BANKS should be 1 for Krypton + { + local_irq_save(flags); + for (iBlockCounter = 0; iBlockCounter < BLOCKS_PER_BANK; + iBlockCounter++) { + //printk("Program Loop : iBlockCounter %d \n",iBlockCounter); + //INTLOCK(); + // local_irq_save(flags); + + //PTJ: READ-WRITE-SETUP used here to select SRAM Bank 1, and TSYNC Enable +#ifdef CY8C20x66 + if ((fIsError = fSyncEnable())) { + ErrorTrap(fIsError); + return fIsError; + } + if ((fIsError = fReadWriteSetup())) { // send write command - swanhan + ErrorTrap(fIsError); + return fIsError; + } +#endif + //firmware read. + //LoadProgramData(bBankCounter, (unsigned char)iBlockCounter); //PTJ: this loads the Hydra with test data, not the krypton + LoadProgramData((unsigned char)iBlockCounter, bBankCounter); //PTJ: this loads the Hydra with test data, not the krypton + iChecksumData += iLoadTarget(); //PTJ: this loads the Krypton + + //dog_kick(); + if ((fIsError = + fProgramTargetBlock(bBankCounter, + (unsigned char)iBlockCounter))) { + ErrorTrap(fIsError); + return fIsError; + } +#ifdef CY8C20x66 //PTJ: READ-STATUS after PROGRAM-AND-VERIFY + if ((fIsError = fReadStatus())) { + ErrorTrap(fIsError); + return fIsError; + } +#endif + //INTFREE(); + //local_irq_restore(flags); + } + local_irq_restore(flags); + } + + //printk("\r\n Program Flash Blocks End\n"); + +#if 0 // verify check pass or check. + printk("\r\n Verify Start", 0, 0, 0); + //=======================================================// + //PTJ: Doing Verify + //PTJ: this code isnt needed in the program flow because we use PROGRAM-AND-VERIFY (ProgramAndVerify SROM Func) + //PTJ: which has Verify built into it. + // Verify included for completeness in case host desires to do a stand-alone verify at a later date. + for (bBankCounter = 0; bBankCounter < NUM_BANKS; bBankCounter++) { + for (iBlockCounter = 0; iBlockCounter < BLOCKS_PER_BANK; + iBlockCounter++) { + printk("Verify Loop: iBlockCounter %d", iBlockCounter, + 0, 0); + INTLOCK(); + LoadProgramData(bBankCounter, + (unsigned char)iBlockCounter); + + //PTJ: READ-WRITE-SETUP used here to select SRAM Bank 1, and TSYNC Enable +#ifdef CY8C20x66 + if (fIsError = fReadWriteSetup()) { + ErrorTrap(fIsError); + } +#endif + + dog_kick(); + + if (fIsError = + fVerifySetup(bBankCounter, + (unsigned char)iBlockCounter)) { + ErrorTrap(fIsError); + } +#ifdef CY8C20x66 //PTJ: READ-STATUS after VERIFY-SETUP + if (fIsError = fSyncEnable()) { //PTJ: 307, added for tsync enable testing + ErrorTrap(fIsError); + } + if (fIsError = fReadStatus()) { + ErrorTrap(fIsError); + } + //PTJ: READ-WRITE-SETUP used here to select SRAM Bank 1, and TSYNC Enable + if (fIsError = fReadWriteSetup()) { + ErrorTrap(fIsError); + } + if (fIsError = fSyncDisable()) { //PTJ: 307, added for tsync enable testing + ErrorTrap(fIsError); + } +#endif + INTFREE(); + } + } + printk("Verify End", 0, 0, 0); +#endif /* #if 1 */ +#if 1 /* security start */ + //=======================================================// + // Program security data into target PSoC. In the final application this + // data should come from the HEX output of PSoC Designer. + //printk("Program security data START\n"); + //INTLOCK(); + local_irq_save(flags); + for (bBankCounter = 0; bBankCounter < NUM_BANKS; bBankCounter++) { + //PTJ: READ-WRITE-SETUP used here to select SRAM Bank 1 +#ifdef CY8C20x66 + if ((fIsError = fSyncEnable())) { //PTJ: 307, added for tsync enable testing. + ErrorTrap(fIsError); + return fIsError; + } + if ((fIsError = fReadWriteSetup())) { + ErrorTrap(fIsError); + return fIsError; + } +#endif + // Load one bank of security data from hex file into buffer + if ((fIsError = fLoadSecurityData(bBankCounter))) { + ErrorTrap(fIsError); + return fIsError; + } + // Secure one bank of the target flash + if ((fIsError = fSecureTargetFlash())) { + ErrorTrap(fIsError); + return fIsError; + } + } + //INTFREE(); + local_irq_restore(flags); + + //printk("Program security data END\n"); + + //==============================================================// + //PTJ: Do READ-SECURITY after SECURE + + //Load one bank of security data from hex file into buffer + //loads abTargetDataOUT[] with security data that was used in secure bit stream + //INTLOCK(); + local_irq_save(flags); + if ((fIsError = fLoadSecurityData(bBankCounter))) { + ErrorTrap(fIsError); + return fIsError; + } +#ifdef CY8C20x66 + if ((fIsError = fReadSecurity())) { + ErrorTrap(fIsError); + return fIsError; + } +#endif + //INTFREE(); + local_irq_restore(flags); + //printk("Load security data END\n"); +#endif /* security end */ + + //=======================================================// + //PTJ: Doing Checksum after READ-SECURITY + //INTLOCK(); + local_irq_save(flags); + iChecksumTarget = 0; + for (bBankCounter = 0; bBankCounter < NUM_BANKS; bBankCounter++) { + if ((fIsError = fAccTargetBankChecksum(&iChecksumTarget))) { + ErrorTrap(fIsError); + return fIsError; + } + } + + //INTFREE(); + local_irq_restore(flags); + + //printk("Checksum : iChecksumTarget (0x%X)\n", (unsigned char)iChecksumTarget); + //printk ("Checksum : iChecksumData (0x%X)\n", (unsigned char)iChecksumData); + + if ((unsigned short)(iChecksumTarget & 0xffff) != + (unsigned short)(iChecksumData & 0xffff)) { + ErrorTrap(VERIFY_ERROR); + return fIsError; + } + //printk("Doing Checksum END\n"); + + // *** SUCCESS *** + // At this point, the Target has been successfully Initialize, ID-Checked, + // Bulk-Erased, Block-Loaded, Block-Programmed, Block-Verified, and Device- + // Checksum Verified. + + // You may want to restart Your Target PSoC Here. + ReStartTarget(); //Touch IC Reset. + + //printk("ReStartTarget\n"); + + return 0; +} + +// end of main() + +#endif //(PROJECT_REV_) end of file main.c diff --git a/drivers/input/keyboard/cypressbln/issp_revision.h b/drivers/input/keyboard/cypressbln/issp_revision.h new file mode 100644 index 0000000..ca1990c --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_revision.h @@ -0,0 +1,65 @@ +/* filename: ISSP_Revision.h + Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +--------------------------------------------------------------------------*/ +#ifndef INC_ISSP_REVISION +#define INC_ISSP_REVISION +// The PROJECT_REV_xyz is used to make sure that the files in the project +// are all from the same revision of the program. Each file starts with an +// ifdef that will prevent the file from being compiled if it is not the +// correct revision +// Set the revision to 3.04 +#define PROJECT_REV_304 + +#endif //(INC_ISSP_REVISION) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//end of file ISSP_Revision.h diff --git a/drivers/input/keyboard/cypressbln/issp_routines.c b/drivers/input/keyboard/cypressbln/issp_routines.c new file mode 100644 index 0000000..0186e2b --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_routines.c @@ -0,0 +1,1044 @@ +// filename: ISSP_Routines.c +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress?product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +--------------------------------------------------------------------------*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//mhsong #include // part specific constants and macros +//mhsong #include "PSoCAPI.h" // PSoC API definitions for all User Modules +#include "issp_defs.h" +#include "issp_vectors.h" +#include "issp_extern.h" +#include "issp_errors.h" +#include "issp_directives.h" +#include "issp_delays.h" + +unsigned char bTargetDataIN; +unsigned char abTargetDataOUT[TARGET_DATABUFF_LEN]; + +unsigned char bTargetAddress; +unsigned char bTargetDataPtr = 0; +unsigned char bTargetID[10]; +unsigned char bTargetStatus[10]; //PTJ: created to support READ-STATUS in fReadStatus() + +unsigned char fIsError; + +/* ((((((((((((((((((((( LOW-LEVEL ISSP SUBROUTINE SECTION )))))))))))))))))))) + (( The subroutines in this section use functions from the C file )) + (( ISSP_Drive_Routines.c. The functions in that file interface to the )) + (( processor specific hardware. So, these functions should work as is, if )) + (( the routines in ISSP_Drive_Routines.c are correctly converted. )) + (((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))*/ + +// ============================================================================ +// RunClock() +// Description: +// Run Clock without sending/receiving bits. Use this when transitioning from +// write to read and read to write "num_cycles" is number of SCLK cycles, not +// number of counter cycles. +// +// SCLK cannot run faster than the specified maximum frequency of 8MHz. Some +// processors may need to have delays added after setting SCLK low and setting +// SCLK high in order to not exceed this specification. The maximum frequency +// of SCLK should be measured as part of validation of the final program +// +// ============================================================================ +void RunClock(unsigned int iNumCycles) +{ + int i; + + for (i = 0; i < iNumCycles; i++) { + SCLKLow(); + SCLKHigh(); + } + // function exits with CLK high. +} + +// ============================================================================ +// bReceiveBit() +// Clocks the SCLK pin (high-low-high) and reads the status of the SDATA pin +// after the rising edge. +// +// SCLK cannot run faster than the specified maximum frequency of 8MHz. Some +// processors may need to have delays added after setting SCLK low and setting +// SCLK high in order to not exceed this specification. The maximum frequency +// of SCLK should be measured as part of validation of the final program +// +// Returns: +// 0 if SDATA was low +// 1 if SDATA was high +// ============================================================================ +unsigned char bReceiveBit(void) +{ + SCLKLow(); + SCLKHigh(); + if (fSDATACheck()) { + return (1); + } else { + return (0); + } +} + +// ============================================================================ +// bReceiveByte() +// Calls ReceiveBit 8 times to receive one byte. +// Returns: +// The 8-bit values recieved. +// ============================================================================ +unsigned char bReceiveByte(void) +{ + unsigned char b; + unsigned char bCurrByte = 0x00; + + for (b = 0; b < 8; b++) { + bCurrByte = (bCurrByte << 1) + bReceiveBit(); + } + return (bCurrByte); +} + +// ============================================================================ +// SendByte() +// This routine sends up to one byte of a vector, one bit at a time. +// bCurrByte the byte that contains the bits to be sent. +// bSize the number of bits to be sent. Valid values are 1 to 8. +// +// SCLK cannot run faster than the specified maximum frequency of 8MHz. Some +// processors may need to have delays added after setting SCLK low and setting +// SCLK high in order to not exceed this specification. The maximum frequency +// of SCLK should be measured as part of validation of the final program +// +// There is no returned value. +// ============================================================================ +void SendByte(unsigned char bCurrByte, unsigned char bSize) +{ + unsigned char b = 0; + + for (b = 0; b < bSize; b++) { + if (bCurrByte & 0x80) { + // Send a '1' + SetSDATAHigh(); + SCLKHigh(); + SCLKLow(); + } else { + // Send a '0' + SetSDATALow(); + SCLKHigh(); + SCLKLow(); + } + bCurrByte = bCurrByte << 1; + } +} + +// ============================================================================ +// SendVector() +// This routine sends the vector specifed. All vectors constant strings found +// in ISSP_Vectors.h. The data line is returned to HiZ after the vector is +// sent. +// bVect a pointer to the vector to be sent. +// nNumBits the number of bits to be sent. +// bCurrByte scratch var to keep the byte to be sent. +// +// There is no returned value. +// ============================================================================ +void SendVector(const unsigned char *bVect, unsigned int iNumBits) +{ + SetSDATAStrong(); + while (iNumBits > 0) { + if (iNumBits >= 8) { + SendByte(*(bVect), 8); + iNumBits -= 8; + bVect++; + } else { + SendByte(*(bVect), iNumBits); + iNumBits = 0; + } + } + SetSDATALow(); // issp_test_20100709 add + SetSDATAHiZ(); +} + +// ============================================================================ +// fDetectHiLoTransition() +// Waits for transition from SDATA = 1 to SDATA = 0. Has a 100 msec timeout. +// TRANSITION_TIMEOUT is a loop counter for a 100msec timeout when waiting for +// a high-to-low transition. This is used in the polling loop of +// fDetectHiLoTransition(). The timing of the while(1) loops can be calculated +// and the number of loops is counted, using iTimer, to determine when 100 +// msec has passed. +// +//// SCLK cannot run faster than the specified maximum frequency of 8MHz. Some +// processors may need to have delays added after setting SCLK low and setting +// SCLK high in order to not exceed this specification. The maximum frequency +// of SCLK should be measured as part of validation of the final program +// +// Returns: +// 0 if successful +// -1 if timed out. +// ============================================================================ +signed char fDetectHiLoTransition(void) +{ + // nTimer breaks out of the while loops if the wait in the two loops totals + // more than 100 msec. Making this static makes the loop run a faster. + // This is really a processor/compiler dependency and it not needed. + static unsigned int iTimer; + + // NOTE: + // These loops look unconventional, but it is necessary to check SDATA_PIN + // as shown because the transition can be missed otherwise, due to the + // length of the SDATA Low-High-Low after certain commands. + + // Generate clocks for the target to pull SDATA High + //dog_kick(); + iTimer = TRANSITION_TIMEOUT; + printk(KERN_DEBUG + "Generate clocks for the target to pull SDATA High\n"); + while (1) { + SCLKLow(); + if (fSDATACheck()) // exit once SDATA goes HI + break; + SCLKHigh(); + // If the wait is too long then timeout + if (iTimer-- == 0) { + return (ERROR); + } + } + //dog_kick(); + // Generate Clocks and wait for Target to pull SDATA Low again + iTimer = TRANSITION_TIMEOUT; // reset the timeout counter + printk(KERN_DEBUG + "Generate Clocks and wait for Target to pull SDATA Low again\n"); + while (1) { + SCLKLow(); //issp_test_20100709 unblock + if (!fSDATACheck()) { // exit once SDATA returns LOW + break; + } + SCLKHigh(); //issp_test_20100709 unblock + // If the wait is too long then timeout + if (iTimer-- == 0) { + return (ERROR); + } + } + printk("fDetectHiLoTransition OUT!!!!\n"); + return (PASS); +} + +/* ((((((((((((((((((((( HIGH-LEVEL ISSP ROUTINE SECTION )))))))))))))))))))))) + (( These functions are mostly made of calls to the low level routines )) + (( above. This should isolate the processor-specific changes so that )) + (( these routines do not need to be modified. )) + (((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))*/ + +#ifdef RESET_MODE +// ============================================================================ +// fXRESInitializeTargetForISSP() +// Implements the intialization vectors for the device. +// Returns: +// 0 if successful +// INIT_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fXRESInitializeTargetForISSP(void) +{ + // Configure the pins for initialization + SetSDATAHiZ(); + SetSCLKStrong(); + SCLKLow(); + // Cycle reset and put the device in programming mode when it exits reset + AssertXRES(); + DeassertXRES(); + // !!! NOTE: + // The timing spec that requires that the first Init-Vector happen within + // 1 msec after the reset/power up. For this reason, it is not advisable + // to separate the above RESET_MODE or POWER_CYCLE_MODE code from the + // Init-Vector instructions below. Doing so could introduce excess delay + // and cause the target device to exit ISSP Mode. + + //PTJ: Send id_setup_1 instead of init1_v + //PTJ: both send CA Test Key and do a Calibrate1 SROM function + SendVector(id_setup_1, num_bits_id_setup_1); + if (fIsError = fDetectHiLoTransition()) { +// TX8SW_CPutString("\r\n fDetectHiLoTransition Error"); + printk("\r\n fDetectHiLoTransition Error\n"); + return (INIT_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + // NOTE: DO NOT not wait for HiLo on SDATA after vector Init-3 + // it does not occur (per spec). + return (PASS); +} +#else //else = the part is power cycle programmed + +// ============================================================================ +// fPowerCycleInitializeTargetForISSP() +// Implements the intialization vectors for the device. +// The first time fDetectHiLoTransition is called the Clk pin is highZ because +// the clock is not needed during acquire. +// Returns: +// 0 if successful +// INIT_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fPowerCycleInitializeTargetForISSP(void) +{ + // unsigned char n; + + // Set all pins to highZ to avoid back powering the PSoC through the GPIO + // protection diodes. + SetSCLKHiZ(); + SetSDATAHiZ(); + + // Turn on power to the target device before other signals + SetTargetVDDStrong(); + ApplyTargetVDD(); + // wait 1msec for the power to stabilize +#if 0 + for (n = 0; n < 10; n++) { + Delay(DELAY100us); + } +#endif + // Set SCLK to high Z so there is no clock and wait for a high to low + // transition on SDAT. SCLK is not needed this time. + SetSCLKHiZ(); +// printk(KERN_DEBUG "fDetectHiLoTransition\n"); + if ((fIsError = fDetectHiLoTransition())) { + return (INIT_ERROR); + } + // Configure the pins for initialization +// SetSDATAHiZ(); // issp_test_20100709 block + SetSCLKStrong(); + SCLKLow(); //PTJ: DO NOT SET A BREAKPOINT HERE AND EXPECT SILICON ID TO PASS! + + // !!! NOTE: + // The timing spec that requires that the first Init-Vector happen within + // 1 msec after the reset/power up. For this reason, it is not advisable + // to separate the above RESET_MODE or POWER_CYCLE_MODE code from the + // Init-Vector instructions below. Doing so could introduce excess delay + // and cause the target device to exit ISSP Mode. + + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); //PTJ: rev308, added to match spec +// printk("SendVector(id_setup_1)\n",0,0,0); + SendVector(id_setup_1, num_bits_id_setup_1); + if ((fIsError = fDetectHiLoTransition())) { + return (INIT_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + // NOTE: DO NOT not wait for HiLo on SDATA after vector Init-3 + // it does not occur (per spec). + return (PASS); +} +#endif + +// ============================================================================ +// fVerifySiliconID() +// Returns: +// 0 if successful +// Si_ID_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fVerifySiliconID(void) +{ + SendVector(id_setup_2, num_bits_id_setup_2); + printk("fVerifySiliconID: SendVector id_stup2 END\n"); + + if ((fIsError = fDetectHiLoTransition())) { + printk("fVerifySiliconID(): fDetectHiLoTransition Error\n"); + return (SiID_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + SendVector(tsync_enable, num_bits_tsync_enable); + printk + ("fVerifySiliconID: SendVector(wait_and_poll_end) (tsync_enable) END\n"); + + //Send Read ID vector and get Target ID + SendVector(read_id_v, 11); // Read-MSB Vector is the first 11-Bits + RunClock(2); // Two SCLK cycles between write & read + bTargetID[0] = bReceiveByte(); + RunClock(1); + SendVector(read_id_v + 2, 12); // 1+11 bits starting from the 3rd byte + + RunClock(2); // Read-LSB Command + bTargetID[1] = bReceiveByte(); + + RunClock(1); + SendVector(read_id_v + 4, 1); // 1 bit starting from the 5th byte + + //read Revision ID from Accumulator A and Accumulator X + SendVector(read_id_v + 5, 11); //11 bits starting from the 6th byte + RunClock(2); + bTargetID[2] = bReceiveByte(); //Read from Acc.X + RunClock(1); + SendVector(read_id_v + 7, 12); //1+11 bits starting from the 8th byte + + RunClock(2); + bTargetID[3] = bReceiveByte(); //Read from Acc.A + + RunClock(1); + SendVector(read_id_v + 4, 1); //1bit starting from the 5th byte, + + SendVector(tsync_disable, num_bits_tsync_disable); + + // Print READ-ID + /* + TX8SW_CPutString("\r\n Silicon-ID : "); + TX8SW_PutChar(' '); + TX8SW_PutSHexByte(bTargetID[0]); + TX8SW_PutChar(' '); + TX8SW_PutSHexByte(bTargetID[1]); + TX8SW_PutChar(' '); + TX8SW_PutSHexByte(bTargetID[2]); + TX8SW_PutChar(' '); + TX8SW_PutSHexByte(bTargetID[3]); + TX8SW_PutChar(' '); + */ +#if 0 // issp_test_20100709 block + printk("issp_routines.c: ID0:0x%X, ID1:0x%X, ID2: 0x%X, ID2: 0x%X\n", + bTargetID[0], bTargetID[1], bTargetID[2], bTargetID[3]); + + if ((bTargetID[0] != target_id_v[0]) || (bTargetID[1] != target_id_v[1]) + || (bTargetID[2] != target_id_v[2]) + || (bTargetID[3] != target_id_v[3])) { + return (SiID_ERROR); + } else { + return (PASS); + } +#else + return (PASS); + +#endif +} + +// PTJ: ======================================================================= +// fReadStatus() +// Returns: +// 0 if successful +// _____ if timed out on handshake to the device. +// ============================================================================ +signed char fReadStatus(void) +{ + SendVector(tsync_enable, num_bits_tsync_enable); //PTJ: + + //Send Read ID vector and get Target ID + SendVector(read_id_v, 11); // Read-MSB Vector is the first 11-Bits + RunClock(2); // Two SCLK cycles between write & read + bTargetStatus[0] = bReceiveByte(); + RunClock(1); + //SendVector(read_id_v+2, 12); // 12 bits starting from the 3rd character + + //RunClock(2); // Read-LSB Command + //bTargetStatus[1] = bReceiveByte(); + + //RunClock(1); + SendVector(read_id_v + 4, 1); // 1 bit starting from the 5th character + + SendVector(tsync_disable, num_bits_tsync_disable); + + if (bTargetStatus[0] == target_status00_v) { + return (PASS); //PTJ: Status = 00 means Success, the SROM function did what it was supposed to + } + if (bTargetStatus[0] == target_status01_v) { + return (STATUS_ERROR); //PTJ: Status = 01 means that function is not allowed because of block level protection, for test with verify_setup (VERIFY-SETUP) + } + if (bTargetStatus[0] == target_status03_v) { + return (STATUS_ERROR); //PTJ: Status = 03 is fatal error, SROM halted + } + if (bTargetStatus[0] == target_status04_v) { + return (STATUS_ERROR); //PTJ: Status = 04 means there was a checksum faliure with either the smart write code checksum, or the smart write paramters checksum, for test with PROGRAM-AND-VERIFY + } + if (bTargetStatus[0] == target_status06_v) { + return (STATUS_ERROR); //PTJ: Status = 06 means that Calibrate1 failed, for test with id_setup_1 (ID-SETUP-1) + } else { + return (STATUS_ERROR); + } +} + +// PTJ: ======================================================================= +// fReadCalRegisters() +// PTJ: use this to read some cal registers that should be loaded by Calibrate1 in id_setup_1 +// Returns: +// 0 if successful +// _____ if timed out on handshake to the device. +// ============================================================================ +signed char fReadCalRegisters(void) +{ + SendVector(tsync_enable, num_bits_tsync_enable); + + SendVector(Switch_Bank1, 22); + + SendVector(read_IMOtrim, 11); // Read-MSB Vector is the first 11-Bits + RunClock(2); // Two SCLK cycles between write & read + bTargetStatus[0] = bReceiveByte(); + RunClock(1); + // Set SDATA to Strong Drive here because SendByte() does not + SetSDATAStrong(); + SendByte(read_reg_end, 1); + + SendVector(read_SPCtrim, 11); // Read-MSB Vector is the first 11-Bits + RunClock(2); // Two SCLK cycles between write & read + bTargetStatus[1] = bReceiveByte(); + RunClock(1); + // Set SDATA to Strong Drive here because SendByte() does not + SetSDATAStrong(); + SendByte(read_reg_end, 1); + + SendVector(read_VBGfinetrim, 11); // Read-MSB Vector is the first 11-Bits + RunClock(2); // Two SCLK cycles between write & read + bTargetStatus[2] = bReceiveByte(); + RunClock(1); + // Set SDATA to Strong Drive here because SendByte() does not + SetSDATAStrong(); + SendByte(read_reg_end, 1); + + SendVector(Switch_Bank0, 22); + + SendVector(tsync_disable, num_bits_tsync_disable); + + if (bTargetStatus[0] == target_status00_v) { + return (PASS); //PTJ: Status = 00 means Success, the SROM function did what it was supposed to + } + return PASS; +} + +// PTJ: ======================================================================= +// fReadWriteSetup() +// PTJ: The READ-WRITE-SETUP vector will enable TSYNC and switches the device +// to SRAM bank1 for PROGRAM-AND-VERIFY, SECURE and VERIFY-SETUP. +// Returns: +// 0 if successful +// _____ if timed out on handshake to the device. +// ============================================================================ +signed char fReadWriteSetup(void) +{ + SendVector(read_write_setup, num_bits_read_write_setup); + return (PASS); //PTJ: is there anything else that should be done? +} + +// PTJ: ======================================================================= +// fSyncEnable() +// PTJ: The SYNC-ENABLE vector will enable TSYNC +// +// Returns: +// 0 if successful +// _____ if timed out on handshake to the device. +// ============================================================================ +signed char fSyncEnable(void) +{ + SendVector(tsync_enable, num_bits_tsync_enable); //PTJ: 307 for tsync enable testing + return (PASS); //PTJ: is there anything else that should be done? +} + +// PTJ: ======================================================================= +// fSyncDisable() +// PTJ: The SYNC-ENABLE vector will enable TSYNC +// +// Returns: +// 0 if successful +// _____ if timed out on handshake to the device. +// ============================================================================ +signed char fSyncDisable(void) +{ + SendVector(tsync_disable, num_bits_tsync_disable); //PTJ: 307 for tsync enable testing + return (PASS); +} + +// ============================================================================ +// fEraseTarget() +// Perform a bulk erase of the target device. +// Returns: +// 0 if successful +// ERASE_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fEraseTarget(void) +{ + SendVector(erase, num_bits_erase); + if ((fIsError = fDetectHiLoTransition())) { +// TX8SW_CPutString("\r\n fDetectHiLoTransition"); + //printk("\r\n fDetectHiLoTransition\n"); // issp_test_2010 block + return (ERASE_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + return (PASS); +} + +extern unsigned int iBlockCounter; +// ============================================================================ +// LoadTarget() +// Transfers data from array in Host to RAM buffer in the target. +// Returns the checksum of the data. +// ============================================================================ +unsigned int iLoadTarget(void) +{ + unsigned char bTemp; + unsigned int iChecksumData = 0; + + // Set SDATA to Strong Drive here because SendByte() does not + SetSDATAStrong(); + + // Transfer the temporary RAM array into the target. + // In this section, a 128-Byte array was specified by #define, so the entire + // 128-Bytes are written in this loop. + bTargetAddress = 0x00; + bTargetDataPtr = 0x00; + + while (bTargetDataPtr < TARGET_DATABUFF_LEN) { + bTemp = abTargetDataOUT[bTargetDataPtr]; + iChecksumData += bTemp; + + SendByte(write_byte_start, 4); //PTJ: we need to be able to write 128 bytes from address 0x80 to 0xFF + SendByte(bTargetAddress, 7); //PTJ: we need to be able to write 128 bytes from address 0x80 to 0xFF + SendByte(bTemp, 8); + SendByte(write_byte_end, 3); + + // !!!NOTE: + // SendByte() uses MSbits, so inc by '2' to put the 0..128 address into + // the seven MSBit locations. + // + // This can be confusing, but check the logic: + // The address is only 7-Bits long. The SendByte() subroutine will + // send however-many bits, BUT...always reads them bits from left-to- + // right. So in order to pass a value of 0..128 as the address using + // SendByte(), we have to left justify the address by 1-Bit. + // This can be done easily by incrementing the address each time by + // '2' rather than by '1'. + + bTargetAddress += 2; //PTJ: inc by 2 in order to support a 128 byte address space, MSB~1 for address + bTargetDataPtr++; + } + + return (iChecksumData); +} + +#ifdef MULTI_BANK +// ============================================================================ +// SetBankNumber() +// Set the bank number in the target device. +// Returns: +// none +// ============================================================================ +void SetBankNumber(unsigned char bBankNumber) +{ + // Send the bank-select vector. + SendVector(set_bank_number, 33); + + // Set the drive here because SendByte() does not. + SetSDATAStrong(); + SendByte(bBankNumber, 8); + SendVector(set_bank_number_end, 25); +} +#endif + +// ============================================================================ +// fProgramTargetBlock() +// Program one block with data that has been loaded into a RAM buffer in the +// target device. +// Returns: +// 0 if successful +// BLOCK_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fProgramTargetBlock(unsigned char bBankNumber, + unsigned char bBlockNumber) +{ + + SendVector(tsync_enable, num_bits_tsync_enable); + + SendVector(set_block_num, num_bits_set_block_num); + + // Set the drive here because SendByte() does not. + SetSDATAStrong(); + SendByte(bBlockNumber, 8); + SendByte(set_block_num_end, 3); + + SendVector(tsync_disable, num_bits_tsync_disable); //PTJ: + + // Send the program-block vector. + SendVector(program_and_verify, num_bits_program_and_verify); //PTJ: PROGRAM-AND-VERIFY + // wait for acknowledge from target. + if ((fIsError = fDetectHiLoTransition())) { + return (BLOCK_ERROR); + } + // Send the Wait-For-Poll-End vector + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + return (PASS); + + //PTJ: Don't do READ-STATUS here because that will + //PTJ: require that we return multiple error values, if error occurs +} + +// ============================================================================ +// fAddTargetBankChecksum() +// Reads and adds the target bank checksum to the referenced accumulator. +// Returns: +// 0 if successful +// VERIFY_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fAccTargetBankChecksum(unsigned int *pAcc) +{ + unsigned int wCheckSumData; + + SendVector(checksum_setup, num_bits_checksum_setup); //PTJ:CHECKSUM-SETUP, it is taking 100ms > time > 200ms to complete the checksum + if ((fIsError = fDetectHiLoTransition())) { //100ms is default + return (VERIFY_ERROR); + } + + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + SendVector(tsync_enable, num_bits_tsync_enable); + + //Send Read Checksum vector and get Target Checksum + SendVector(read_checksum_v, 11); // first 11-bits is ReadCKSum-MSB + RunClock(2); // Two SCLKs between write & read + bTargetDataIN = bReceiveByte(); + wCheckSumData = bTargetDataIN << 8; + + RunClock(1); // See Fig. 6 + SendVector(read_checksum_v + 2, 12); // 12 bits starting from 3rd character + RunClock(2); // Read-LSB Command + bTargetDataIN = bReceiveByte(); + wCheckSumData |= (bTargetDataIN & 0xFF); + RunClock(1); + SendVector(read_checksum_v + 3, 1); // Send the final bit of the command //PTJ: read_checksum_v may have to change if TSYNC needs to be enabled + + SendVector(tsync_disable, num_bits_tsync_disable); + + *pAcc = wCheckSumData; + + return (PASS); +} + +// ============================================================================ +// ReStartTarget() +// After programming, the target PSoC must be reset to take it out of +// programming mode. This routine performs a reset. +// ============================================================================ +void ReStartTarget(void) +{ +#ifdef RESET_MODE + // Assert XRES, then release, then disable XRES-Enable + AssertXRES(); + Delay(XRES_CLK_DELAY); + DeassertXRES(); +#else + // Set all pins to highZ to avoid back powering the PSoC through the GPIO + // protection diodes. + SetSCLKHiZ(); + SetSDATAHiZ(); + // Cycle power on the target to cause a reset + RemoveTargetVDD(); + mdelay(300); + ApplyTargetVDD(); +#endif +} + +// ============================================================================ +// fVerifySetup() +// Verify the block just written to. This can be done byte-by-byte before the +// protection bits are set. +// Returns: +// 0 if successful +// BLOCK_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fVerifySetup(unsigned char bBankNumber, unsigned char bBlockNumber) +{ + SendVector(tsync_enable, num_bits_tsync_enable); + + SendVector(set_block_num, num_bits_set_block_num); + + //Set the drive here because SendByte() does not + SetSDATAStrong(); + SendByte(bBlockNumber, 8); + SendByte(set_block_num_end, 3); //PTJ: + + SendVector(tsync_disable, num_bits_tsync_disable); //PTJ: + + SendVector(verify_setup, num_bits_my_verify_setup); //PTJ: + if ((fIsError = fDetectHiLoTransition())) { + return (BLOCK_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + return (PASS); +} + +// ============================================================================ +// fReadByteLoop() +// Reads the data back from Target SRAM and compares it to expected data in +// Host SRAM +// Returns: +// 0 if successful +// BLOCK_ERROR if timed out on handshake to the device. +// ============================================================================ + +signed char fReadByteLoop(void) +{ + bTargetAddress = 0; + bTargetDataPtr = 0; + + while (bTargetDataPtr < TARGET_DATABUFF_LEN) { + //Send Read Byte vector and then get a byte from Target + SendVector(read_byte_v, 4); + // Set the drive here because SendByte() does not + SetSDATAStrong(); + SendByte(bTargetAddress, 7); + + RunClock(2); // Run two SCLK cycles between writing and reading + SetSDATAHiZ(); // Set to HiZ so Target can drive SDATA + bTargetDataIN = bReceiveByte(); + + RunClock(1); + SendVector(read_byte_v + 1, 1); // Send the ReadByte Vector End + + // Test the Byte that was read from the Target against the original + // value (already in the 128-Byte array "abTargetDataOUT[]"). If it + // matches, then bump the address & pointer,loop-back and continue. + // If it does NOT match abort the loop and return and error. + if (bTargetDataIN != abTargetDataOUT[bTargetDataPtr]) { +#ifdef TX_ON + UART_PutCRLF(); + UART_CPutString("bTargetDataIN : "); + UART_PutHexByte(bTargetDataIN); + UART_CPutString(" abTargetDataOUT : "); + UART_PutHexByte(abTargetDataOUT[bTargetDataPtr]); +#endif + return (BLOCK_ERROR); + } + + bTargetDataPtr++; + // Increment the address by 2 to accomodate 7-Bit addressing + // (puts the 7-bit address into MSBit locations for "SendByte()"). + bTargetAddress += 2; + + } + + return (PASS); +} + +// ============================================================================ +// fVerifyTargetBlock() +// Verify the block just written to. This can be done byte-by-byte before the +// protection bits are set. +// Returns: +// 0 if successful +// BLOCK_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fVerifyTargetBlock(unsigned char bBankNumber, + unsigned char bBlockNumber) +{ + SendVector(set_block_number, 11); + + //Set the drive here because SendByte() does not + SetSDATAStrong(); + SendByte(bBlockNumber, 8); + SendByte(set_block_number_end, 3); + + SendVector(verify_setup_v, num_bits_verify_setup); + if ((fIsError = fDetectHiLoTransition())) { + return (BLOCK_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + bTargetAddress = 0; + bTargetDataPtr = 0; + + while (bTargetDataPtr < TARGET_DATABUFF_LEN) { + //Send Read Byte vector and then get a byte from Target + SendVector(read_byte_v, 4); //PTJ 308: this was changed from sending the first 5 bits to sending the first 4 + // Set the drive here because SendByte() does not + SetSDATAStrong(); + SendByte(bTargetAddress, 6); + + RunClock(2); // Run two SCLK cycles between writing and reading + SetSDATAHiZ(); // Set to HiZ so Target can drive SDATA + bTargetDataIN = bReceiveByte(); + + RunClock(1); + SendVector(read_byte_v + 1, 1); // Send the ReadByte Vector End + + // Test the Byte that was read from the Target against the original + // value (already in the 128-Byte array "abTargetDataOUT[]"). If it + // matches, then bump the address & pointer,loop-back and continue. + // If it does NOT match abort the loop and return an error. + if (bTargetDataIN != abTargetDataOUT[bTargetDataPtr]) + return (BLOCK_ERROR); + + bTargetDataPtr++; + // Increment the address by four to accomodate 6-Bit addressing + // (puts the 6-bit address into MSBit locations for "SendByte()"). + bTargetAddress += 4; + } + return (PASS); +} + +// ============================================================================ +// fSecureTargetFlash() +// Before calling, load the array, abTargetDataOUT, with the desired security +// settings using LoadArrayWithSecurityData(StartAddress,Length,SecurityType). +// The can be called multiple times with different SecurityTypes as needed for +// particular Flash Blocks. Or set them all the same using the call below: +// LoadArrayWithSecurityData(0,SECURITY_BYTES_PER_BANK, 0); +// Returns: +// 0 if successful +// SECURITY_ERROR if timed out on handshake to the device. +// ============================================================================ +signed char fSecureTargetFlash(void) +{ + unsigned char bTemp; + + // Transfer the temporary RAM array into the target + bTargetAddress = 0x00; + bTargetDataPtr = 0x00; + + SetSDATAStrong(); + while (bTargetDataPtr < SECURITY_BYTES_PER_BANK) { + bTemp = abTargetDataOUT[bTargetDataPtr]; + SendByte(write_byte_start, 4); + SendByte(bTargetAddress, 7); + SendByte(bTemp, 8); + SendByte(write_byte_end, 3); + + // SendBytes() uses MSBits, so increment the address by '2' to put + // the 0..n address into the seven MSBit locations + bTargetAddress += 2; //PTJ: inc by 2 in order to support a 128 byte address space + bTargetDataPtr++; + } + + SendVector(secure, num_bits_secure); //PTJ: + if ((fIsError = fDetectHiLoTransition())) { + return (SECURITY_ERROR); + } + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + return (PASS); +} + +// ============================================================================ +// PTJ: fReadSecurity() +// This reads from SM0 with Read Supervisory SPC command. +// Need to have SPC Test Mode enabled before using these commands? +// Returns: +// 0 if successful +// __________ if timed out on handshake to the device. +// ============================================================================ +signed char fReadSecurity(void) +{ + SendVector(ReadSecuritySetup, num_bits_ReadSecuritySetup); +// SendVector(SPCTestMode_enable, num_bits_SPCTestMode_enable); + + bTargetAddress = 0x00; + while (bTargetAddress < (SECURITY_BYTES_PER_BANK * 2)) { //PTJ: we do SECURITY_BYTES_PER_BANK * 2 because we bTargetAddress += 2 + + //PTJ: TSYNC Enable + SendVector(tsync_enable, num_bits_tsync_enable); + + SendVector(read_security_pt1, num_bits_read_security_pt1); //PTJ: + // Set the drive here because SendByte() does not. + SetSDATAStrong(); + SendByte(bTargetAddress, 7); //PTJ: hardcode MSb of address as 0 in bit stream + SendVector(read_security_pt1_end, + num_bits_read_security_pt1_end); + + //PTJ: TSYNC Disable + SendVector(tsync_disable, num_bits_tsync_disable); + + SendVector(read_security_pt2, num_bits_read_security_pt2); + + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + SendVector(read_security_pt3, num_bits_read_security_pt3); + + SetSDATAStrong(); + SendByte(bTargetAddress, 7); + + SendVector(read_security_pt3_end, + num_bits_read_security_pt3_end); + + SendVector(wait_and_poll_end, num_bits_wait_and_poll_end); + + bTargetAddress += 2; + } + + bTargetAddress = 0x00; + bTargetDataPtr = 0x00; + + SendVector(tsync_enable, num_bits_tsync_enable); //PTJ: 307, added for tsync testing + while (bTargetAddress < (SECURITY_BYTES_PER_BANK * 2)) { //PTJ: we do SECURITY_BYTES_PER_BANK * 2 because we bTargetAddress += 2 + + //Send Read Byte vector and then get a byte from Target + SendVector(read_byte_v, 4); + // Set the drive here because SendByte() does not + SetSDATAStrong(); + SendByte(bTargetAddress, 7); + + RunClock(2); // Run two SCLK cycles between writing and reading + SetSDATAHiZ(); // Set to HiZ so Target can drive SDATA + bTargetDataIN = bReceiveByte(); + + RunClock(1); + SendVector(read_byte_v + 1, 1); // Send the ReadByte Vector End + + // Test the Byte that was read from the Target against the original + // value (already in the 128-Byte array "abTargetDataOUT[]"). If it + // matches, then bump the address & pointer,loop-back and continue. + // If it does NOT match abort the loop and return and error. + if (bTargetDataIN != abTargetDataOUT[bTargetDataPtr]) +// return(BLOCK_ERROR); + + // Increment the address by two to accomodate 7-Bit addressing + // (puts the 7-bit address into MSBit locations for "SendByte()"). + bTargetDataPtr++; + bTargetAddress += 2; + } + + SendVector(tsync_disable, num_bits_tsync_disable); //PTJ: 307, added for tsync testing + return (PASS); +} + +#endif //(PROJECT_REV_) +// end of file ISSP_Routines.c diff --git a/drivers/input/keyboard/cypressbln/issp_vectors.h b/drivers/input/keyboard/cypressbln/issp_vectors.h new file mode 100644 index 0000000..35d5ef1 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/issp_vectors.h @@ -0,0 +1,1146 @@ +// filename: ISSP_Vectors.h +#include "issp_revision.h" +#ifdef PROJECT_REV_304 +/* Copyright 2006-2007, Cypress Semiconductor Corporation. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONRTACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, + WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + Cypress reserves the right to make changes without further notice to the + materials described herein. Cypress does not assume any liability arising + out of the application or use of any product or circuit described herein. + Cypress does not authorize its products for use as critical components in + life-support systems where a malfunction or failure may reasonably be + expected to result in significant injury to the user. The inclusion of + Cypress� product in a life-support systems application implies that the + manufacturer assumes all risk of such use and in doing so indemnifies + Cypress against all charges. + + Use may be limited by and subject to the applicable Cypress software + license agreement. + +-----------------------------------------------------------------------------*/ +#ifndef INC_ISSP_VECTORS +#define INC_ISSP_VECTORS + +#include "issp_directives.h" + +#define HEX_DEFINE +// ------------------------- PSoC CY8C20x66 Devices --------------------------- + +#ifdef CY8C20236 /// 2009.03.26. kimc +unsigned char target_id_v[] = { 0x00, 0xb3, 0x52, 0x21 }; //ID for CY8C20236 +#endif +#ifdef CY8C20246 /// 2009.03.26. kimc +unsigned char target_id_v[] = { 0x00, 0xAA, 0x52, 0x21 }; //ID for CY8C20246 +#endif +#ifdef CY8C20266 +unsigned char target_id_v[] = { 0x00, 0x96, 0x52, 0x21 }; //ID for CY8C20266 +#endif +#ifdef CY8C20366 +unsigned char target_id_v[] = { 0x00, 0x97, 0x52, 0x21 }; //ID for CY8C20366 +#endif +#ifdef CY8C20466 +unsigned char target_id_v[] = { 0x00, 0x98, 0x52, 0x21 }; //ID for CY8C20466 +#endif +#ifdef CY8C20566 +unsigned char target_id_v[] = { 0x00, 0x99, 0x52, 0x21 }; //ID for CY8C20566 +#endif +#ifdef CY8C20666 +unsigned char target_id_v[] = { 0x00, 0x9c, 0x52, 0x21 }; //ID for CY8C20666 +#endif +#ifdef CY8C20066 +unsigned char target_id_v[] = { 0x00, 0x9a, 0x52, 0x21 }; //ID for CY8C20066 +#endif +#ifdef CY8C200661 +unsigned char target_id_v[] = { 0x00, 0x9b, 0x52, 0x21 }; //ID for CY8C200661 +#endif + +#ifdef CY8C20x66 +unsigned char target_status00_v = 0x00; //PTJ: Status = 00 means Success, the SROM function did what it was supposed to +unsigned char target_status01_v = 0x01; //PTJ: Status = 01 means that function is not allowed because of block level protection, for test with verify_setup (VERIFY-SETUP) +unsigned char target_status03_v = 0x03; //PTJ: Status = 03 is fatal error, SROM halted +unsigned char target_status04_v = 0x04; //PTJ: Status = 04 means that ___ for test with ___ (PROGRAM-AND-VERIFY) +unsigned char target_status06_v = 0x06; //PTJ: Status = 06 means that Calibrate1 failed, for test with id_setup_1 (ID-SETUP-1) +#endif + +/*************** CY8CTMA30x, CY8CTMG30x, CY8CTST30x series by KIMC, 2009.08.11 ***********************************/ +// ------------------------- PSoC CY8CTMA30x, CY8CTMG30x, CY8CTST30x Devices --------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8CTST300_36 // CY8CTST300_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x06, 0x71, 0x70, 0x11 }; +#endif +#ifdef CY8CTST300_48 // CY8CTST300_48LTXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x06, 0x72, 0x70, 0x11 }; +#endif +#ifdef CY8CTST300_49 // CY8CTST300_49FNXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x06, 0x73, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300_36 // CY8CTMA300_36LQXI // 2009.08.11, Test OK. +unsigned char target_id_v[] = { 0x05, 0x71, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300_48 // CY8CTMA300_48LTXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x72, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300_49 // CY8CTMA300_49FNXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x73, 0x70, 0x11 }; +#endif +#ifdef CY8CTMG300_36 // CY8CTMG300_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x07, 0x71, 0x70, 0x11 }; +#endif +#ifdef CY8CTMG300_48 // CY8CTMG300_48LTXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x07, 0x72, 0x70, 0x11 }; +#endif +#ifdef CY8CTMG300_49 // CY8CTMG300_49FNXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x07, 0x73, 0x70, 0x11 }; +#endif +#ifdef CY8CTMG300B_36 // CY8CTMG300B_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x07, 0x75, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300B_36 // CY8CTMA300B_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x74, 0x70, 0x11 }; +#endif +#ifdef CY8CTST300B_36 // CY8CTST300B_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x06, 0x74, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA301_36 // CY8CTMA301_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x75, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA301_48 // CY8CTMA301_48LTXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x76, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA301D_36 // CY8CTMA301D_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x77, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA301D_48 // CY8CTMA301D_48LTXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x78, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300D_36 // CY8CTMA300D_36LQXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x79, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300D_48 // CY8CTMA300D_48LTXI // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x80, 0x70, 0x11 }; +#endif +#ifdef CY8CTMA300D_49 // CY8CTMA300D_49FNXIT // 2009.08.11, not tested. +unsigned char target_id_v[] = { 0x05, 0x81, 0x70, 0x11 }; +#endif +/********************************************************************************************************/ + +/*************** CY8CTMG/TST series modified by KJHW, 2009.08.14 ***********************************/ +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8CTMG110 +unsigned char target_id_v[] = { 0x07, 0x38 }; //ID for CY8CTMG110 + +unsigned char target_status00_v = 0x00; //PTJ: Status = 00 means Success, the SROM function did what it was supposed to +unsigned char target_status01_v = 0x01; //PTJ: Status = 01 means that function is not allowed because of block level protection, for test with verify_setup (VERIFY-SETUP) +unsigned char target_status03_v = 0x03; //PTJ: Status = 03 is fatal error, SROM halted +unsigned char target_status04_v = 0x04; //PTJ: Status = 04 means that ___ for test with ___ (PROGRAM-AND-VERIFY) +unsigned char target_status06_v = 0x06; //PTJ: Status = 06 means that Calibrate1 failed, for test with id_setup_1 (ID-SETUP-1) +#endif + +#ifdef CY8CTST200_24PIN +unsigned char target_id_v[] = { 0x06, 0x6D, 0x52, 0x21 }; //ID for CY8CTST200 +#endif +#ifdef CY8CTST200_32PIN +unsigned char target_id_v[] = { 0x06, 0x6E, 0x52, 0x21 }; //ID for CY8CTST200 +#endif +#ifdef CY8CTMG200_24PIN +unsigned char target_id_v[] = { 0x07, 0x6D, 0x52, 0x21 }; //ID for CY8CTMG200 +#endif +#ifdef CY8CTMG200_32PIN +unsigned char target_id_v[] = { 0x07, 0x6E, 0x52, 0x21 }; //ID for CY8CTMG200 +#endif + +/********************************************************************************************************/ + +// ------------------------- PSoC CY8C21x23 Devices --------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8C21123 +unsigned char target_id_v[] = { 0x00, 0x17 }; //ID for CY8C21123 +#endif +#ifdef CY8C21223 +unsigned char target_id_v[] = { 0x00, 0x18 }; //ID for CY8C21223 +#endif +#ifdef CY8C21323 +unsigned char target_id_v[] = { 0x00, 0x19 }; //ID for CY8C2132 +#endif +#ifdef CY8C21002 +unsigned char target_id_v[] = { 0x00, 0x3F }; //ID for CY8C21x23 ICE pod +#endif + +// ------------------------- PSoC CY8C21x34 Devices --------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8C21234 +unsigned char target_id_v[] = { 0x00, 0x36 }; //ID for CY8C21234 +#endif +#ifdef CY8C21334 +unsigned char target_id_v[] = { 0x00, 0x37 }; //ID for CY8C21334 +#endif +#ifdef CY8C21434 +unsigned char target_id_v[] = { 0x00, 0x38 }; //ID for CY8C21434 +#endif +#ifdef CY8C21534 +unsigned char target_id_v[] = { 0x00, 0x40 }; //ID for CY8C21534 +#endif +#ifdef CY8C21634 +unsigned char target_id_v[] = { 0x00, 0x49 }; //ID for CY8C21634 +#endif +#ifdef CY8C21001 +unsigned char target_id_v[] = { 0x00, 0x39 }; //ID for CY8C21x34 ICE pod +#endif + +// ------------------------- PSoC CY8C24x23A Devices -------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8C24123A +unsigned char target_id_v[] = { 0x00, 0x32 }; //ID for CY8C24123A +#endif +#ifdef CY8C24223A +unsigned char target_id_v[] = { 0x00, 0x33 }; //ID for CY8C24223A +#endif +#ifdef CY8C24423A +unsigned char target_id_v[] = { 0x00, 0x34 }; //ID for CY8C24423A +#endif +#ifdef CY8C24000A +unsigned char target_id_v[] = { 0x00, 0x35 }; //ID for CY8C24x23A ICE pod +#endif + +// ------------------------- PSoC CY8C24x94 Devices --------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8C24794 +unsigned char target_id_v[] = { 0x00, 0x1D }; //ID for CY8C24794 +#endif +#ifdef CY8C24894 +unsigned char target_id_v[] = { 0x00, 0x1F }; //ID for CY8C24894 +#endif +#ifdef CY8C24994 +unsigned char target_id_v[] = { 0x00, 0x59 }; //ID for CY8C24994 +#endif +#ifdef CY8C24094 +unsigned char target_id_v[] = { 0x00, 0x1B }; //ID for CY8C24094 +#endif + +// ------------------------- PSoC CY8C27x43 Devices --------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8C27143 +unsigned char target_id_v[] = { 0x00, 0x09 }; //ID for CY8C27143 +#endif +#ifdef CY8C27243 +unsigned char target_id_v[] = { 0x00, 0x0A }; //ID for CY8C27243 +#endif +#ifdef CY8C27443 +unsigned char target_id_v[] = { 0x00, 0x0B }; //ID for CY8C27443 +#endif +#ifdef CY8C27543 +unsigned char target_id_v[] = { 0x00, 0x0C }; //ID for CY8C27543 +#endif +#ifdef CY8C27643 +unsigned char target_id_v[] = { 0x00, 0x0D }; //ID for CY8C27643 +#endif +#ifdef CY8C27002 +unsigned char target_id_v[] = { 0x00, 0x0E }; //ID for CY8C27x43 ICE pod +#endif + +// ------------------------- PSoC CY8C29x66 Devices --------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CY8C29466 +unsigned char target_id_v[] = { 0x00, 0x2A }; //ID for CY8C29466 +#endif +#ifdef CY8C29566 +unsigned char target_id_v[] = { 0x00, 0x2B }; //ID for CY8C29566 +#endif +#ifdef CY8C29666 +unsigned char target_id_v[] = { 0x00, 0x2C }; //ID for CY8C29666 +#endif +#ifdef CY8C29866 +unsigned char target_id_v[] = { 0x00, 0x2D }; //ID for CY8C29866 +#endif +#ifdef CY8C29002 +unsigned char target_id_v[] = { 0x00, 0x2E }; //ID for CY8C29002 +#endif + +// --------- CY8C20x66 Vectors ------------------------------------------------ +// ---------------------------------------------------------------------------- +#ifdef TSYNC +const unsigned int num_bits_tsync_enable = 110; +const unsigned char tsync_enable[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09, + 0xF7, 0x00, 0x1F, 0xDE, 0xE0, 0x1C +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b11011110, 0 b11100000, + 0 b00011100 +#endif +}; + +const unsigned int num_bits_tsync_disable = 110; +const unsigned char tsync_disable[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE2, 0x1F, 0x71, 0x00, 0x7D, 0xFC, 0x01, + 0xF7, 0x00, 0x1F, 0xDE, 0xE0, 0x1C +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01110001, 0 b00000000, + 0 b01111101, 0 b11111100, 0 b00000001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b11011110, 0 b11100000, + 0 b00011100 +#endif +}; +#endif + +#ifdef CY8CTMx30x +#ifdef ID_SETUP_1 +const unsigned int num_bits_id_setup_1 = 616; //KIMC, 2009.08.11, PTJ: id_setup_1 with TSYNC enabled for MW and disabled for IOW +const unsigned char id_setup_1[] = { + 0 b11001010, 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00001101, 0 b11101110, 0 b00100001, 0 b11110111, 0 b11110000, + 0 b00100111, 0 b11011100, 0 b01000000, + 0 b10011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00000001, 0 b11100111, 0 b11000001, + 0 b11010111, 0 b10011111, 0 b00100000, 0 b01111110, 0 b00111111, + 0 b10011101, 0 b01111000, 0 b11110110, + 0 b00100001, 0 b11110111, 0 b10111000, 0 b10000111, 0 b11011111, + 0 b11000000, 0 b00011111, 0 b01110001, + 0 b00000000, 0 b01111101, 0 b11000000, 0 b00000111, 0 b11110111, + 0 b10111000, 0 b00000111, 0 b11011110, + 0 b10000000, 0 b01111111, 0 b01111010, 0 b10000000, 0 b01111101, + 0 b11101100, 0 b00000001, 0 b11110111, + 0 b10000000, 0 b01001111, 0 b11011111, 0 b00000000, 0 b00011111, + 0 b01111100, 0 b10100000, 0 b01111101, + 0 b11110100, 0 b01100001, 0 b11110111, 0 b11111000, 0 b10010111 +}; +#endif +#else +#ifdef ID_SETUP_1 +const unsigned int num_bits_id_setup_1 = 594; //PTJ: id_setup_1 with TSYNC enabled for MW and disabled for IOW +const unsigned char id_setup_1[] = { +#ifdef HEX_DEFINE + 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0xEE, 0x21, 0xF7, 0xF0, 0x27, 0xDC, 0x40, + 0x9F, 0x70, 0x01, 0xFD, 0xEE, 0x01, /*0x21, */ 0xE7, 0xC1, + 0xD7, 0x9F, 0x20, 0x7E, 0x7D, 0x88, 0x7D, 0xEE, + 0x21, 0xF7, 0xF0, 0x07, 0xDC, 0x40, 0x1F, 0x70, + 0x01, 0xFD, 0xEE, 0x01, 0xF7, 0xA0, 0x1F, 0xDE, + 0xA0, 0x1F, 0x7B, 0x00, 0x7D, 0xE0, 0x13, 0xF7, + 0xC0, 0x07, 0xDF, 0x28, 0x1F, 0x7D, 0x18, 0x7D, + 0xFE, 0x25, 0xC0 +#else + 0 b11001010, 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00001101, 0 b11101110, 0 b00100001, 0 b11110111, 0 b11110000, + 0 b00100111, 0 b11011100, 0 b01000000, + 0 b10011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00100001, 0 b11100111, 0 b11000001, + 0 b11010111, 0 b10011111, 0 b00100000, 0 b01111110, 0 b01111101, + 0 b10001000, 0 b01111101, 0 b11101110, + 0 b00100001, 0 b11110111, 0 b11110000, 0 b00000111, 0 b11011100, + 0 b01000000, 0 b00011111, 0 b01110000, + 0 b00000001, 0 b11111101, 0 b11101110, 0 b00000001, 0 b11110111, + 0 b10100000, 0 b00011111, 0 b11011110, + 0 b10100000, 0 b00011111, 0 b01111011, 0 b00000000, 0 b01111101, + 0 b11100000, 0 b00010011, 0 b11110111, + 0 b11000000, 0 b00000111, 0 b11011111, 0 b00101000, 0 b00011111, + 0 b01111101, 0 b00011000, 0 b01111101, + 0 b11111110, 0 b00100101, 0 b11000000 +#endif +}; +#endif +#endif + +#ifdef SET_BLOCK_NUM +const unsigned int num_bits_set_block_num = 11; //PTJ: +const unsigned char set_block_num[] = { +#ifdef HEX_DEFINE + 0x9f, 0x40, 0x1c +#else + 0 b11011110, 0 b11100000, 0 b00011110, 0 b01111101, 0 b00000000, + 0 b01110000 +#endif +}; + +const unsigned int num_bits_set_block_num_end = 3; //PTJ: this selects the first three bits of set_block_num_end +const unsigned char set_block_num_end = 0xE0; +#endif + +#ifdef READ_WRITE_SETUP +const unsigned int num_bits_read_write_setup = 66; //PTJ: +const unsigned char read_write_setup[] = { +#ifdef HEX_DEFINE + 0xde, 0xf0, 0x1f, 0x78, 0x00, 0x7d, 0xa0, 0x03, + 0xc0 +#else + 0 b11011110, 0 b11110000, 0 b00011111, 0 b01111000, 0 b00000000, + 0 b01111101, 0 b10100000, 0 b00000011, + 0 b11000000 +#endif +}; +#endif + +#ifdef VERIFY_SETUP +const unsigned int num_bits_my_verify_setup = 440; +const unsigned char verify_setup[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x7f, 0x02, 0x7d, 0xc4, 0x09, + 0xf7, 0x00, 0x1f, 0x9f, 0x07, 0x5e, 0xfc, 0x81, + 0xf9, 0xf7, 0x01, 0xf7, 0xf0, 0x07, 0xdc, 0x40, + 0x1f, 0x70, 0x01, 0xfd, 0xee, 0x01, 0xf6, 0xa8, + 0x0f, 0xde, 0x80, 0x7f, 0x7a, 0x80, 0x7d, 0xec, + 0x01, 0xf7, 0x80, 0x0f, 0xdf, 0x00, 0x1f, 0x7c, + 0xa0, 0xfd, 0xf4, 0x61, 0xf7, 0xf8, 0x97 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11110000, + 0 b00000111, 0 b11011100, 0 b01000000, + 0 b00011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00000001, 0 b11110110, 0 b10101000, + 0 b00001111, 0 b11011110, 0 b10000000, 0 b01111111, 0 b01111010, + 0 b10000000, 0 b01111101, 0 b11101100, + 0 b00000001, 0 b11110111, 0 b10000000, 0 b00001111, 0 b11011111, + 0 b00000000, 0 b00011111, 0 b01111100, + 0 b10100000, 0 b01111101, 0 b11110100, 0 b01100001, 0 b11110111, + 0 b11111000, 0 b10010111 +#endif +}; +#endif + +#ifdef ERASE +const unsigned int num_bits_erase = 396; //PTJ: erase with TSYNC Enable and Disable +const unsigned char erase[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x7f, 0x02, 0x7d, 0xc4, 0x09, + 0xf7, 0x00, 0x1f, 0x9f, 0x07, 0x5e, 0x7c, 0x85, + 0xfd, 0xfc, 0x01, 0xf7, 0x10, 0x07, 0xdc, 0x00, + 0x7f, 0x7b, 0x80, 0x7d, 0xe0, 0x0b, 0xf7, 0xa0, + 0x1f, 0xd7, 0xa0, 0x1f, 0x7b, 0x04, 0x7d, 0xf0, + 0x01, 0xf7, 0xc9, 0x87, 0xdf, 0x48, 0x1f, 0x7f, + 0x89, 0x70 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000101, + 0 b11111101, 0 b11111100, 0 b00000001, 0 b11110111, 0 b00010000, + 0 b00000111, 0 b11011100, 0 b00000000, + 0 b01111111, 0 b01111011, 0 b10000000, 0 b01111101, 0 b11100000, + 0 b00001011, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b11011110, 0 b10100000, 0 b00011111, 0 b01111011, + 0 b00000100, 0 b01111101, 0 b11110000, + 0 b00000001, 0 b11110111, 0 b11001001, 0 b10000111, 0 b11011111, + 0 b01001000, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000 +#endif +}; +#endif + +#ifdef SECURE +const unsigned int num_bits_secure = 440; //PTJ: secure with TSYNC Enable and Disable +const unsigned char secure[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x7f, 0x02, 0x7d, 0xc4, 0x09, + 0xf7, 0x00, 0x1f, 0x9f, 0x07, 0x5e, 0x7c, 0x81, + 0xf9, 0xf7, 0x01, 0xf7, 0xf0, 0x07, 0xdc, 0x40, + 0x1f, 0x70, 0x01, 0xfd, 0xee, 0x01, 0xf6, 0xa0, + 0x0f, 0xde, 0x80, 0x7f, 0x7a, 0x80, 0x7d, 0xec, + 0x01, 0xf7, 0x80, 0x27, 0xdf, 0x00, 0x1f, 0x7c, + 0xa0, 0x7d, 0xf4, 0x61, 0xf7, 0xf8, 0x97 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11110000, + 0 b00000111, 0 b11011100, 0 b01000000, + 0 b00011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00000001, 0 b11110110, 0 b10100000, + 0 b00001111, 0 b11011110, 0 b10000000, 0 b01111111, 0 b01111010, + 0 b10000000, 0 b01111101, 0 b11101100, + 0 b00000001, 0 b11110111, 0 b10000000, 0 b00100111, 0 b11011111, + 0 b00000000, 0 b00011111, 0 b01111100, + 0 b10100000, 0 b01111101, 0 b11110100, 0 b01100001, 0 b11110111, + 0 b11111000, 0 b10010111 +#endif +}; +#endif + +#ifdef READ_SECURITY +const unsigned int num_bits_ReadSecuritySetup = 88; //PTJ: READ-SECURITY-SETUP +const unsigned char ReadSecuritySetup[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x60, 0x88, 0x7d, 0x84, 0x21, + 0xf7, 0xb8, 0x07 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01100000, 0 b10001000, + 0 b01111101, 0 b10000100, 0 b00100001, + 0 b11110111, 0 b10111000, 0 b00000111 +#endif +}; + +const unsigned int num_bits_read_security_pt1 = 78; //PTJ: This sends the beginning of the Read Supervisory command +const unsigned char read_security_pt1[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x72, 0x87, 0x7d, 0xca, 0x01, + 0xf7, 0x28 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01110010, 0 b10000111, + 0 b01111101, 0 b11001010, 0 b00000001, + 0 b11110111, 0 b00101000 +#endif +}; + +const unsigned int num_bits_read_security_pt1_end = 25; //PTJ: this finishes the Address Low command and sends the Address High command +const unsigned char read_security_pt1_end[] = { +#ifdef HEX_DEFINE + 0xfb, 0x94, 0x03, 0x80 +#else + 0 b11111011, 0 b10010100, 0 b00000011, 0 b10000000 +#endif +}; + +const unsigned int num_bits_read_security_pt2 = 198; //PTJ: load the test queue with the op code for MOV 1,E5h register into Accumulator A +const unsigned char read_security_pt2[] = { +#ifdef HEX_DEFINE + 0xde, 0xe0, 0x1f, 0x7a, 0x01, 0xfd, 0xea, 0x01, + 0xf7, 0xb0, 0x07, 0xdf, 0x0b, 0xbf, 0x7c, 0xf2, + 0xfd, 0xf4, 0x61, 0xf7, 0xb8, 0x87, 0xdf, 0xe2, + 0x5c +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111101, 0 b11101010, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b11011111, 0 b00001011, + 0 b10111111, 0 b01111100, 0 b11110010, + 0 b11111101, 0 b11110100, 0 b01100001, 0 b11110111, 0 b10111000, + 0 b10000111, 0 b11011111, 0 b11100010, + 0 b01011100 +#endif +}; + +const unsigned int num_bits_read_security_pt3 = 122; //PTJ: +const unsigned char read_security_pt3[] = { +#ifdef HEX_DEFINE + 0xde, 0xe0, 0x1f, 0x7a, 0x01, 0xfd, 0xea, 0x01, + 0xf7, 0xb0, 0x07, 0xdf, 0x0a, 0x7f, 0x7c, 0xc0 +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111101, 0 b11101010, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b11011111, 0 b00001010, + 0 b01111111, 0 b01111100, 0 b11000000 +#endif +}; + +const unsigned int num_bits_read_security_pt3_end = 47; //PTJ: +const unsigned char read_security_pt3_end[] = { +#ifdef HEX_DEFINE + 0xfb, 0xe8, 0xc3, 0xef, 0xf1, 0x2e +#else + 0 b11111011, 0 b11101000, 0 b11000011, 0 b11101111, 0 b11110001, + 0 b00101110 +#endif +}; + +#endif + +// --------- CY8C20x66 Checksum Setup Vector ---------------------------------- +// ---------------------------------------------------------------------------- +#ifdef CHECKSUM_SETUP +const unsigned int num_bits_checksum_setup = 418; //PTJ: Checksum with TSYNC Enable and Disable +const unsigned char checksum_setup[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x7f, 0x02, 0x7d, 0xc4, 0x09, + 0xf7, 0x00, 0x1f, 0x9f, 0x07, 0x5e, 0x7c, 0x81, + 0xf9, 0xf4, 0x01, 0xf7, 0xf0, 0x07, 0xdc, 0x40, + 0x1f, 0x70, 0x01, 0xfd, 0xee, 0x01, 0xf7, 0xa0, + 0x1f, 0xde, 0xa0, 0x1f, 0x7b, 0x00, 0x7d, 0xe0, + 0x0f, 0xf7, 0xc0, 0x07, 0xdf, 0x28, 0x1f, 0x7d, + 0x18, 0x7d, 0xfe, 0x25, 0xc0 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111001, 0 b11110100, 0 b00000001, 0 b11110111, 0 b11110000, + 0 b00000111, 0 b11011100, 0 b01000000, + 0 b00011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b11011110, 0 b10100000, 0 b00011111, 0 b01111011, + 0 b00000000, 0 b01111101, 0 b11100000, + 0 b00001111, 0 b11110111, 0 b11000000, 0 b00000111, 0 b11011111, + 0 b00101000, 0 b00011111, 0 b01111101, + 0 b00011000, 0 b01111101, 0 b11111110, 0 b00100101, 0 b11000000 +#endif +}; +#endif + +// --------- CY8C21x23, CY8C21x34 & CY8C27x43 Checksum Setup Vectors ---------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CHECKSUM_SETUP_21_27 +const unsigned int num_bits_checksum = 286; +const unsigned char checksum_v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7B, 0x00, 0x79, 0xF0, 0x75, + 0xE7, 0xC8, 0x1F, 0xDE, 0xA0, 0x1F, 0x7A, 0x01, + 0xF9, 0xF7, 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, + 0x1E, 0x7D, 0x00, 0x7D, 0xE0, 0x0F, 0xF7, 0xC0, + 0x07, 0xDF, 0xE2, 0x5C +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111011, 0 b00000000, + 0 b01111001, 0 b11110000, 0 b01110101, + 0 b11100111, 0 b11001000, 0 b00011111, 0 b11011110, 0 b10100000, + 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11001001, + 0 b10000111, 0 b11011111, 0 b01001000, + 0 b00011110, 0 b01111101, 0 b00000000, 0 b01111101, 0 b11100000, + 0 b00001111, 0 b11110111, 0 b11000000, + 0 b00000111, 0 b11011111, 0 b11100010, 0 b01011100 +#endif +}; +#endif + +// -------------- CY8C24x23 & CY8C24x23A Checksum Setup Vectors --------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CHECKSUM_SETUP_24_24A +const unsigned int num_bits_checksum = 286; +const unsigned char checksum_v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7B, 0x00, 0x79, 0xF0, 0x75, + 0xE7, 0xC8, 0x1F, 0xDE, 0xA0, 0x1F, 0x7A, 0x01, + 0xF9, 0xF7, 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, + 0x1E, 0x7D, 0x20, 0x7D, 0xE0, 0x0F, 0xF7, 0xC0, + 0x07, 0xDF, 0xE2, 0x5C +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111011, 0 b00000000, + 0 b01111001, 0 b11110000, 0 b01110101, + 0 b11100111, 0 b11001000, 0 b00011111, 0 b11011110, 0 b10100000, + 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11001001, + 0 b10000111, 0 b11011111, 0 b01001000, + 0 b00011110, 0 b01111101, 0 b00100000, 0 b01111101, 0 b11100000, + 0 b00001111, 0 b11110111, 0 b11000000, + 0 b00000111, 0 b11011111, 0 b11100010, 0 b01011100 +#endif +}; +#endif + +// -------------- CY8C24x94 & CY8C29x66 Checksum Setup Vectors ---------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef CHECKSUM_SETUP_24_29 +const unsigned int num_bits_checksum = 286; +const unsigned char checksum_v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7B, 0x00, 0x79, 0xF0, 0x75, + 0xE7, 0xC8, 0x1F, 0xDE, 0xA0, 0x1F, 0x7A, 0x01, + 0xF9, 0xF6, 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, + 0x1E, 0x7D, 0x40, 0x7D, 0xE0, 0x0F, 0xF7, 0xC0, + 0x07, 0xDF, 0xE2, 0x5C +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111011, 0 b00000000, + 0 b01111001, 0 b11110000, 0 b01110101, + 0 b11100111, 0 b11001000, 0 b00011111, 0 b11011110, 0 b10100000, + 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111001, 0 b11110110, 0 b00000001, 0 b11110111, 0 b11001001, + 0 b10000111, 0 b11011111, 0 b01001000, + 0 b00011110, 0 b01111101, 0 b00100000, 0 b01111101, 0 b11100000, + 0 b00001111, 0 b11110111, 0 b11000000, + 0 b00000111, 0 b11011111, 0 b11100010, 0 b01011100 +#endif +}; +#endif + +// ---- CY8C20x66 Program Block Vector ---------------------------------------- +// +// ---------------------------------------------------------------------------- +#ifdef PROGRAM_AND_VERIFY +const unsigned int num_bits_program_and_verify = 440; //KIMC, PTJ: length of program_block[], not including zero padding at end +const unsigned char program_and_verify[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x7f, 0x02, 0x7d, 0xc4, 0x09, + 0xf7, 0x00, 0x1f, 0x9f, 0x07, 0x5e, 0x7c, 0x81, + 0xf9, 0xf7, 0x01, 0xf7, 0xf0, 0x07, 0xdc, 0x40, + 0x1f, 0x70, 0x01, 0xfd, 0xee, 0x01, 0xf6, 0xa0, + 0x0f, 0xde, 0x80, 0x7f, 0x7a, 0x80, 0x7d, 0xec, + 0x01, 0xf7, 0x80, 0x57, 0xdf, 0x00, 0x1f, 0x7c, + 0xa0, 0x7d, 0xf4, 0x61, 0xf7, 0xf8, 0x97 +#else + 0 b00011011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b00011110111, 0 b00000000, 0 b00011111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b00011111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11110000, + 0 b00000111, 0 b11011100, 0 b01000000, + 0 b00000011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00000001, 0 b11110110, 0 b10100000, + 0 b00000001111, 0 b11011110, 0 b10000000, 0 b01111111, 0 b01111010, + 0 b10000000, 0 b01111101, 0 b11101100, + 0 b00000000001, 0 b11110111, 0 b10000000, 0 b01010111, 0 b11011111, + 0 b00000000, 0 b00011111, 0 b01111100, + 0 b00010100000, 0 b01111101, 0 b11110100, 0 b01100001, 0 b11110111, + 0 b11111000, 0 b10010111 +#endif +}; +#endif + +// ---- CY8C21xxx, CY8C24x23A, CY8C24x94 & CY8C29x66 Program Block Vectors ---- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef PROGRAM_BLOCK_21_24_29 +const unsigned int num_bits_program_block = 308; +const unsigned char program_block[] = { +#ifdef HEX_DEFINE + 0x9F, 0x8A, 0x9E, 0x7F, 0x2B, 0x7D, 0xEE, 0x01, + 0xF7, 0xB0, 0x07, 0x9F, 0x07, 0x5E, 0x7C, 0x81, + 0xFD, 0xEA, 0x01, 0xF7, 0xA0, 0x1F, 0x9F, 0x70, + 0x1F, 0x7C, 0x98, 0x7D, 0xF4, 0x81, 0xF7, 0x80, + 0x17, 0xDF, 0x00, 0x1F, 0x7F, 0x89, 0x70 +#else + 0 b10011111, 0 b10001010, 0 b10011110, 0 b01111111, 0 b00101011, + 0 b01111101, 0 b11101110, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111101, 0 b11101010, 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b10011111, 0 b01110000, + 0 b00011111, 0 b01111100, 0 b10011000, 0 b01111101, 0 b11110100, + 0 b10000001, 0 b11110111, 0 b10000000, + 0 b00010111, 0 b11011111, 0 b00000000, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000 +#endif +}; +#endif + +// --------------------- CY8C27x43 Program Block Vectors----------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +#ifdef PROGRAM_BLOCK_27 +const unsigned int num_bits_program_block = 308; + +const unsigned char program_block[] = { +#ifdef HEX_DEFINE + 0x9F, 0x82, 0xBE, 0x7F, 0x2B, 0x7D, 0xEE, 0x01, + 0xF7, 0xB0, 0x07, 0x9F, 0x07, 0x5E, 0x7C, 0x81, + 0xFD, 0xEA, 0x01, 0xF7, 0xA0, 0x1F, 0x9F, 0x70, + 0x1F, 0x7C, 0x98, 0x7D, 0xF4, 0x81, 0xF7, 0x80, + 0x17, 0xDF, 0x00, 0x1F, 0x7F, 0x89, 0x70 +#else + 0 b10011111, 0 b10000010, 0 b10111110, 0 b01111111, 0 b00101011, + 0 b01111101, 0 b11101110, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111101, 0 b11101010, 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b10011111, 0 b01110000, + 0 b00011111, 0 b01111100, 0 b10011000, 0 b01111101, 0 b11110100, + 0 b10000001, 0 b11110111, 0 b10000000, + 0 b00010111, 0 b11011111, 0 b00000000, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000 +#endif +}; +#endif + +// ----------------------------- General PSoC Vectors-------------------------- +// Modifying these tables is NOT recommendended. Doing so will all but +// guarantee an ISSP error, unless updated vectors have been recommended or +// provided by Cypress Semiconductor. +// ---------------------------------------------------------------------------- +const unsigned int num_bits_init1 = 396; +const unsigned char init1_v[] = { +#ifdef HEX_DEFINE + 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0xEE, 0x01, 0xF7, 0xB0, 0x07, 0x9F, 0x07, + 0x5E, 0x7C, 0x81, 0xFD, 0xEA, 0x01, 0xF7, 0xA0, + 0x1F, 0x9F, 0x70, 0x1F, 0x7C, 0x98, 0x7D, 0xF4, + 0x81, 0xF7, 0x80, 0x4F, 0xDF, 0x00, 0x1F, 0x7F, + 0x89, 0x70 +#else + 0 b11001010, 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00000000, 0 b00000000, 0 b00000000, + 0 b00001101, 0 b11101110, 0 b00000001, 0 b11110111, 0 b10110000, + 0 b00000111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, 0 b11111101, 0 b11101010, + 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b10011111, 0 b01110000, 0 b00011111, 0 b01111100, + 0 b10011000, 0 b01111101, 0 b11110100, + 0 b10000001, 0 b11110111, 0 b10000000, 0 b01001111, 0 b11011111, + 0 b00000000, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000 +#endif +}; + +const unsigned int num_bits_init2 = 286; +const unsigned char init2_v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7B, 0x00, 0x79, 0xF0, 0x75, + 0xE7, 0xC8, 0x1F, 0xDE, 0xA0, 0x1F, 0x7A, 0x01, + 0xF9, 0xF7, 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, + 0x1E, 0x7D, 0x00, 0xFD, 0xE0, 0x0D, 0xF7, 0xC0, + 0x07, 0xDF, 0xE2, 0x5C +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111011, 0 b00000000, + 0 b01111001, 0 b11110000, 0 b01110101, + 0 b11100111, 0 b11001000, 0 b00011111, 0 b11011110, 0 b10100000, + 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11001001, + 0 b10000111, 0 b11011111, 0 b01001000, + 0 b00011110, 0 b01111101, 0 b00000000, 0 b11111101, 0 b11100000, + 0 b00001101, 0 b11110111, 0 b11000000, + 0 b00000111, 0 b11011111, 0 b11100010, 0 b01011100 +#endif +}; + +const unsigned int num_bits_init3_5v = 836; +const unsigned char init3_5v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7A, 0x01, 0xFD, 0xEA, 0x01, + 0xF7, 0xB0, 0x47, 0xDF, 0x0A, 0x3F, 0x7C, 0xFE, + 0x7D, 0xF4, 0x61, 0xF7, 0xF8, 0x97, 0x00, 0x00, + 0x03, 0x7B, 0x80, 0x7D, 0xE8, 0x07, 0xF7, 0xA8, + 0x07, 0xDE, 0xC1, 0x1F, 0x7C, 0x30, 0x7D, 0xF3, + 0xD5, 0xF7, 0xD1, 0x87, 0xDE, 0xE2, 0x1F, 0x7F, + 0x89, 0x70, 0x00, 0x00, 0x37, 0xB8, 0x07, 0xDE, + 0x80, 0x7F, 0x7A, 0x80, 0x7D, 0xEC, 0x11, 0xF7, + 0xC2, 0x8F, 0xDF, 0x3F, 0xBF, 0x7D, 0x18, 0x7D, + 0xFE, 0x25, 0xC0, 0x00, 0x00, 0xDE, 0xE0, 0x1F, + 0x7A, 0x01, 0xFD, 0xEA, 0x01, 0xF7, 0xB0, 0x47, + 0xDF, 0x0C, 0x1F, 0x7C, 0xF4, 0x7D, 0xF4, 0x61, + 0xF7, 0xB8, 0x87, 0xDF, 0xE2, 0x5C, 0x00, 0x00, + 0x00 +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111101, 0 b11101010, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b01000111, 0 b11011111, 0 b00001010, + 0 b00111111, 0 b01111100, 0 b11111110, + 0 b01111101, 0 b11110100, 0 b01100001, 0 b11110111, 0 b11111000, + 0 b10010111, 0 b00000000, 0 b00000000, + 0 b00000011, 0 b01111011, 0 b10000000, 0 b01111101, 0 b11101000, + 0 b00000111, 0 b11110111, 0 b10101000, + 0 b00000111, 0 b11011110, 0 b11000001, 0 b00011111, 0 b01111100, + 0 b00110000, 0 b01111101, 0 b11110011, + 0 b11010101, 0 b11110111, 0 b11010001, 0 b10000111, 0 b11011110, + 0 b11100010, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000, 0 b00000000, 0 b00000000, 0 b00110111, + 0 b10111000, 0 b00000111, 0 b11011110, + 0 b10000000, 0 b01111111, 0 b01111010, 0 b10000000, 0 b01111101, + 0 b11101100, 0 b00010001, 0 b11110111, + 0 b11000010, 0 b10001111, 0 b11011111, 0 b00111111, 0 b10111111, + 0 b01111101, 0 b00011000, 0 b01111101, + 0 b11111110, 0 b00100101, 0 b11000000, 0 b00000000, 0 b00000000, + 0 b11011110, 0 b11100000, 0 b00011111, + 0 b01111010, 0 b00000001, 0 b11111101, 0 b11101010, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b01000111, + 0 b11011111, 0 b00001100, 0 b00011111, 0 b01111100, 0 b11110100, + 0 b01111101, 0 b11110100, 0 b01100001, + 0 b11110111, 0 b10111000, 0 b10000111, 0 b11011111, 0 b11100010, + 0 b01011100, 0 b00000000, 0 b00000000, + 0 b00000000 +#endif +}; + +const unsigned int num_bits_init3_3v = 836; +const unsigned char init3_3v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7A, 0x01, 0xFD, 0xEA, 0x01, + 0xF7, 0xB0, 0x47, 0xDF, 0x0A, 0x3F, 0x7C, 0xFC, + 0x7D, 0xF4, 0x61, 0xF7, 0xF8, 0x97, 0x00, 0x00, + 0x03, 0x7B, 0x80, 0x7D, 0xE8, 0x07, 0xF7, 0xA8, + 0x07, 0xDE, 0xC1, 0x1F, 0x7C, 0x30, 0x7D, 0xF3, + 0xD5, 0xF7, 0xD1, 0x87, 0xDE, 0xE2, 0x1F, 0x7F, + 0x89, 0x70, 0x00, 0x00, 0x37, 0xB8, 0x07, 0xDE, + 0x80, 0x7F, 0x7A, 0x80, 0x7D, 0xEC, 0x11, 0xF7, + 0xC2, 0x8F, 0xDF, 0x3F, 0x3F, 0x7D, 0x18, 0x7D, + 0xFE, 0x25, 0xC0, 0x00, 0x00, 0xDE, 0xE0, 0x1F, + 0x7A, 0x01, 0xFD, 0xEA, 0x01, 0xF7, 0xB0, 0x47, + 0xDF, 0x0C, 0x1F, 0x7C, 0xF4, 0x7D, 0xF4, 0x61, + 0xF7, 0xB8, 0x87, 0xDF, 0xE2, 0x5C, 0x00, 0x00, + 0x00 +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111101, 0 b11101010, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b01000111, 0 b11011111, 0 b00001010, + 0 b00111111, 0 b01111100, 0 b11111100, + 0 b01111101, 0 b11110100, 0 b01100001, 0 b11110111, 0 b11111000, + 0 b10010111, 0 b00000000, 0 b00000000, + 0 b00000011, 0 b01111011, 0 b10000000, 0 b01111101, 0 b11101000, + 0 b00000111, 0 b11110111, 0 b10101000, + 0 b00000111, 0 b11011110, 0 b11000001, 0 b00011111, 0 b01111100, + 0 b00110000, 0 b01111101, 0 b11110011, + 0 b11010101, 0 b11110111, 0 b11010001, 0 b10000111, 0 b11011110, + 0 b11100010, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000, 0 b00000000, 0 b00000000, 0 b00110111, + 0 b10111000, 0 b00000111, 0 b11011110, + 0 b10000000, 0 b01111111, 0 b01111010, 0 b10000000, 0 b01111101, + 0 b11101100, 0 b00010001, 0 b11110111, + 0 b11000010, 0 b10001111, 0 b11011111, 0 b00111111, 0 b00111111, + 0 b01111101, 0 b00011000, 0 b01111101, + 0 b11111110, 0 b00100101, 0 b11000000, 0 b00000000, 0 b00000000, + 0 b11011110, 0 b11100000, 0 b00011111, + 0 b01111010, 0 b00000001, 0 b11111101, 0 b11101010, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b01000111, + 0 b11011111, 0 b00001100, 0 b00011111, 0 b01111100, 0 b11110100, + 0 b01111101, 0 b11110100, 0 b01100001, + 0 b11110111, 0 b10111000, 0 b10000111, 0 b11011111, 0 b11100010, + 0 b01011100, 0 b00000000, 0 b00000000, + 0 b00000000 +#endif +}; + +#if 0 // +const unsigned int num_bits_id_setup = 330; +const unsigned char id_setup_v[] = { + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01110000, 0 b00000001, + 0 b01111101, 0 b11101110, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111101, 0 b11101010, 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b10011111, 0 b01110000, + 0 b00011111, 0 b01111100, 0 b10011000, 0 b01111101, 0 b11110100, + 0 b10000001, 0 b11100111, 0 b11010000, + 0 b00000111, 0 b11011110, 0 b00000000, 0 b11011111, 0 b01111100, + 0 b00000000, 0 b01111101, 0 b11111110, + 0 b00100101, 0 b11000000 +}; +#endif +#ifdef ID_SETUP_2 +const unsigned int num_bits_id_setup_2 = 418; //PTJ: id_setup_2 with TSYNC Disable (TSYNC enabled before with SendVector(tsync_enable....) +const unsigned char id_setup_2[] = { +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1f, 0x7f, 0x02, 0x7d, 0xc4, 0x09, + 0xf7, 0x00, 0x1f, 0x9f, 0x07, 0x5e, 0x7c, 0x81, + 0xf9, 0xf4, 0x01, 0xf7, 0xf0, 0x07, 0xdc, 0x40, + 0x1f, 0x70, 0x01, 0xfd, 0xee, 0x01, 0xf7, 0xa0, + 0x1f, 0xde, 0xa0, 0x1f, 0x7b, 0x00, 0x7d, 0xe0, + 0x0d, 0xf7, 0xc0, 0x07, 0xdf, 0x28, 0x1f, 0x7d, + 0x18, 0x7d, 0xfe, 0x25, 0xc0 +#else + 0 b11011110, 0 b11100010, 0 b00011111, 0 b01111111, 0 b00000010, + 0 b01111101, 0 b11000100, 0 b00001001, + 0 b11110111, 0 b00000000, 0 b00011111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111001, 0 b11110100, 0 b00000001, 0 b11110111, 0 b11110000, + 0 b00000111, 0 b11011100, 0 b01000000, + 0 b00011111, 0 b01110000, 0 b00000001, 0 b11111101, 0 b11101110, + 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b11011110, 0 b10100000, 0 b00011111, 0 b01111011, + 0 b00000000, 0 b01111101, 0 b11100000, + 0 b00001101, 0 b11110111, 0 b11000000, 0 b00000111, 0 b11011111, + 0 b00101000, 0 b00011111, 0 b01111101, + 0 b00011000, 0 b01111101, 0 b11111110, 0 b00100101, 0 b11000000 +#endif +}; +#endif + +const unsigned int num_bits_erase_all = 308; +const unsigned char erase_all_v[] = { +#ifdef HEX_DEFINE + 0x9F, 0x82, 0xBE, 0x7F, 0x2B, 0x7D, 0xEE, 0x01, + 0xF7, 0xB0, 0x07, 0x9F, 0x07, 0x5E, 0x7C, 0x81, + 0xFD, 0xEA, 0x01, 0xF7, 0xA0, 0x1F, 0x9F, 0x70, + 0x1F, 0x7C, 0x98, 0x7D, 0xF4, 0x81, 0xF7, 0x80, + 0x2F, 0xDF, 0x00, 0x1F, 0x7F, 0x89, 0x70 +#else + 0 b10011111, 0 b10000010, 0 b10111110, 0 b01111111, 0 b00101011, + 0 b01111101, 0 b11101110, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111101, 0 b11101010, 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b10011111, 0 b01110000, + 0 b00011111, 0 b01111100, 0 b10011000, 0 b01111101, 0 b11110100, + 0 b10000001, 0 b11110111, 0 b10000000, + 0 b00101111, 0 b11011111, 0 b00000000, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000 +#endif +}; + +const unsigned char read_id_v[] = { +#ifdef HEX_DEFINE + 0xBF, 0x00, 0xDF, 0x90, 0x00, 0xFE, 0x60, 0xFF, 0x00 +#else + 0 b10111111, 0 b00000000, 0 b11011111, 0 b10010000, 0 b00000000, + 0 b11111110, 0 b0110000, 0 b11111111, 0 b00000000 +#endif +}; + +const unsigned char Switch_Bank1[] = //PTJ: use this to switch between register banks +{ +#ifdef HEX_DEFINE + 0xde, 0xe2, 0x1c +#else + 0 b11011110, 0 b11100010, 0 b00011100 +#endif +}; + +const unsigned char Switch_Bank0[] = //PTJ: use this to switch between register banks +{ +#ifdef HEX_DEFINE + 0xde, 0xe0, 0x1c +#else + 0 b11011110, 0 b11100000, 0 b00011100 +#endif +}; + +const unsigned char read_IMOtrim[] = //PTJ: read the 1,E8h register after id__setup_1 to see if the cal data was loaded properly. +{ +#ifdef HEX_DEFINE + 0xfd, 0x00, 0x10 +#else + 0 b11111101, 0 b00000000, 0 b00010000 +#endif +}; + +const unsigned char read_SPCtrim[] = //PTJ: read the 1,E7h register after id__setup_1 to see if the cal data was loaded properly. +{ +#ifdef HEX_DEFINE + 0xfc, 0xe0, 0x10 +#else + 0 b11111100, 0 b11100000, 0 b00010000 +#endif +}; + +const unsigned char read_VBGfinetrim[] = //PTJ: read the 1,D7h register after id__setup_1 to see if the cal data was loaded properly. +{ +#ifdef HEX_DEFINE + 0xfa, 0xe0, 0x08 +#else + 0 b11111010, 0 b11100000, 0 b0001000 +#endif +}; + +const unsigned char read_reg_end = 0x80; //PTJ: this is the final '1' after a MR command + +const unsigned char write_byte_start = 0x90; //PTJ: this is set to SRAM 0x80 +const unsigned char write_byte_end = 0xE0; + +const unsigned char set_block_number[] = { 0x9F, 0x40, 0xE0 }; + +const unsigned char set_block_number_end = 0xE0; +#ifdef MULTI_BANK +const unsigned char set_bank_number[] = { 0xDE, 0xE2, 0x1F, 0x7D, 0x00 }; +const unsigned char set_bank_number_end[] = { 0xFB, 0xDC, 0x03, 0x80 }; +#endif + +// const unsigned char num_bits_wait_and_poll_end = 40; //PTJ 308: commented out +const unsigned char num_bits_wait_and_poll_end = 30; //PTJ 308: added to match spec +const unsigned char wait_and_poll_end[] = { +// 0x00, 0x00, 0x00, 0x00, 0x00 //PTJ 308: commented out + 0x00, 0x00, 0x00, 0x00 //PTJ 308: added to match spec +}; // forty '0's per the spec + +const unsigned char read_checksum_v[] = { +#ifdef HEX_DEFINE + 0xBF, 0x20, 0xDF, 0x80, 0x80 +#else + 0 b10111111, 0 b00100000, 0 b11011111, 0 b10000000, 0 b10000000 +#endif +}; + +const unsigned char read_byte_v[] = { +#ifdef HEX_DEFINE + 0xB0, 0x80 +#else + 0 b10110000, 0 b10000000 +#endif +}; + +const unsigned int num_bits_verify_setup = 264; +const unsigned char verify_setup_v[] = { +#ifdef HEX_DEFINE + 0xDE, 0xE0, 0x1F, 0x7B, 0x00, 0x79, 0xF0, 0x75, + 0xE7, 0xC8, 0x1F, 0xDE, 0xA0, 0x1F, 0x7A, 0x01, + 0xF9, 0xF7, 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, + 0x1F, 0x78, 0x00, 0xFD, 0xF0, 0x01, 0xF7, 0xF8, + 0x97 +#else + 0 b11011110, 0 b11100000, 0 b00011111, 0 b01111011, 0 b00000000, + 0 b01111001, 0 b11110000, 0 b01110101, + 0 b11100111, 0 b11001000, 0 b00011111, 0 b11011110, 0 b10100000, + 0 b00011111, 0 b01111010, 0 b00000001, + 0 b11111001, 0 b11110111, 0 b00000001, 0 b11110111, 0 b11001001, + 0 b10000111, 0 b11011111, 0 b01001000, + 0 b00011111, 0 b01111000, 0 b00000000, 0 b11111101, 0 b11110000, + 0 b00000001, 0 b11110111, 0 b11111000, + 0 b10010111 +#endif +}; + +const unsigned int num_bits_security = 308; +const unsigned char security_v[] = { +#ifdef HEX_DEFINE + 0x9F, 0x8A, 0x9E, 0x7F, 0x2B, 0x7D, 0xEE, 0x01, + 0xF7, 0xB0, 0x07, 0x9F, 0x07, 0x5E, 0x7C, 0x81, + 0xFD, 0xEA, 0x01, 0xF7, 0xA0, 0x1F, 0x9F, 0x70, + 0x1F, 0x7C, 0x98, 0x7D, 0xF4, 0x81, 0xF7, 0x80, + 0x27, 0xDF, 0x00, 0x1F, 0x7F, 0x89, 0x70 +#else + 0 b10011111, 0 b10001010, 0 b10011110, 0 b01111111, 0 b00101011, + 0 b01111101, 0 b11101110, 0 b00000001, + 0 b11110111, 0 b10110000, 0 b00000111, 0 b10011111, 0 b00000111, + 0 b01011110, 0 b01111100, 0 b10000001, + 0 b11111101, 0 b11101010, 0 b00000001, 0 b11110111, 0 b10100000, + 0 b00011111, 0 b10011111, 0 b01110000, + 0 b00011111, 0 b01111100, 0 b10011000, 0 b01111101, 0 b11110100, + 0 b10000001, 0 b11110111, 0 b10000000, + 0 b00100111, 0 b11011111, 0 b00000000, 0 b00011111, 0 b01111111, + 0 b10001001, 0 b01110000 +#endif +}; + +#endif //(INC_ISSP_VECTORS) +#endif //(PROJECT_REV_) +//end of file ISSP_Vectors.h diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_M0.h b/drivers/input/keyboard/cypressbln/touchkey_fw_M0.h new file mode 100644 index 0000000..c712b5e --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_M0.h @@ -0,0 +1,747 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x00, 0x68, + 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x04, 0xce, 0x7e, 0x7e, 0x30, + 0x30, 0x30, 0x7d, 0x06, 0x1b, 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, + 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x40, 0x71, 0x10, 0x62, 0xe3, 0x06, 0x70, + 0xef, 0x62, 0xe3, 0x38, 0x50, 0x80, 0x4e, 0x62, 0xe3, 0x38, 0x5d, + 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, 0xfa, 0x01, 0x40, 0x4f, 0x5b, + 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, 0x50, 0x06, 0x00, 0x40, + 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, 0x70, 0xef, 0x18, 0x60, + 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, 0x71, 0x10, 0x62, 0xe0, + 0x1a, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x71, 0x10, 0x41, 0xe1, 0xfe, + 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, 0x03, 0x50, 0x80, 0x4e, + 0x62, 0xd3, 0x03, 0x62, 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, + 0x00, 0x71, 0xc0, 0x7c, 0x03, 0x3b, 0x62, 0xd0, 0x00, 0x50, 0x01, + 0x57, 0xe2, 0x08, 0x28, 0x53, 0x58, 0x18, 0x75, 0x09, 0x00, 0x28, + 0x4b, 0x51, 0x58, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, + 0x08, 0x28, 0x60, 0xd5, 0x74, 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, + 0x08, 0x28, 0x53, 0x58, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xa0, + 0x1c, 0x53, 0x57, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x3f, 0x58, + 0x47, 0x58, 0xff, 0xb0, 0x06, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x18, + 0x7a, 0x57, 0xbf, 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, 0x00, 0x08, + 0x28, 0x53, 0x57, 0x50, 0x00, 0x3f, 0x58, 0x47, 0x58, 0xff, 0xb0, + 0x08, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x57, 0xbf, + 0xef, 0x18, 0x8f, 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, + 0x10, 0x62, 0xe0, 0x1a, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x18, + 0x88, 0x8f, 0xff, 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x01, 0x99, 0x03, 0x33, 0x06, + 0x66, 0x0c, 0xcc, 0x19, 0x99, 0x33, 0x33, 0x66, 0x66, 0xcc, 0xcc, + 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0b, 0xff, 0x18, 0x00, 0x2f, + 0xff, 0x5f, 0xff, 0xbf, 0xff, 0x01, 0x66, 0x02, 0xcc, 0x05, 0x99, + 0x0b, 0x32, 0x16, 0x66, 0x2c, 0xcc, 0x59, 0x98, 0xb3, 0x32, 0x01, + 0x4d, 0x02, 0x9a, 0x05, 0x33, 0x0a, 0x66, 0x14, 0xcd, 0x29, 0x99, + 0x53, 0x33, 0xa6, 0x66, 0x01, 0x33, 0x02, 0x66, 0x04, 0xcc, 0x09, + 0x99, 0x13, 0x33, 0x26, 0x65, 0x4c, 0xcc, 0x99, 0x99, 0x01, 0x19, + 0x02, 0x33, 0x04, 0x66, 0x08, 0xcc, 0x11, 0x99, 0x25, 0x26, 0x46, + 0x65, 0x8c, 0xcc, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x0f, 0xff, 0x20, 0x00, 0x3f, 0xff, 0x7f, 0xff, 0x1a, 0xf5, 0x70, + 0xef, 0x62, 0x61, 0x00, 0x62, 0xfd, 0x00, 0x62, 0xcd, 0x00, 0x62, + 0xce, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x62, 0xa0, 0x00, + 0x62, 0xa1, 0x80, 0x62, 0xa2, 0xc0, 0x62, 0xa3, 0x0c, 0x62, 0xa8, + 0x00, 0x62, 0xa6, 0x00, 0x62, 0xa7, 0x00, 0x62, 0x7c, 0x33, 0x62, + 0x7a, 0x00, 0x62, 0x7b, 0x00, 0x62, 0x79, 0x00, 0x62, 0x36, 0x00, + 0x62, 0x37, 0x00, 0x62, 0x38, 0x00, 0x62, 0x39, 0x00, 0x62, 0x3a, + 0x00, 0x62, 0x3b, 0x00, 0x62, 0x3c, 0x00, 0x62, 0x3d, 0x00, 0x62, + 0x3e, 0x00, 0x62, 0x3f, 0x00, 0x62, 0x40, 0x00, 0x62, 0x41, 0x00, + 0x62, 0x42, 0x00, 0x62, 0x43, 0x00, 0x62, 0x44, 0x00, 0x62, 0x45, + 0x00, 0x62, 0x46, 0x00, 0x62, 0x47, 0x00, 0x62, 0x48, 0x00, 0x62, + 0x49, 0x00, 0x62, 0x4a, 0x00, 0x62, 0x4b, 0x00, 0x62, 0x4c, 0x00, + 0x62, 0x4d, 0x00, 0x62, 0x4e, 0x00, 0x62, 0x4f, 0x00, 0x62, 0xca, + 0x20, 0x62, 0xd6, 0x44, 0x62, 0xcf, 0x00, 0x62, 0xcb, 0x00, 0x62, + 0xc8, 0x00, 0x62, 0xcc, 0x00, 0x62, 0xc9, 0x00, 0x62, 0xd7, 0x00, + 0x62, 0xa9, 0x00, 0x62, 0x2b, 0x00, 0x62, 0xb0, 0x00, 0x62, 0xb3, + 0x02, 0x62, 0xb6, 0x00, 0x62, 0xb2, 0x00, 0x62, 0xb5, 0x00, 0x62, + 0xb8, 0x00, 0x62, 0xb1, 0x00, 0x62, 0xb4, 0x00, 0x62, 0xb7, 0x00, + 0x62, 0x33, 0x00, 0x62, 0x34, 0x00, 0x62, 0x35, 0x00, 0x71, 0x10, + 0x62, 0x54, 0x00, 0x62, 0x55, 0x00, 0x62, 0x56, 0x00, 0x62, 0x57, + 0x00, 0x62, 0x58, 0x00, 0x62, 0x59, 0x00, 0x62, 0x5a, 0x00, 0x62, + 0x5b, 0x00, 0x62, 0xdc, 0x00, 0x62, 0xe2, 0x00, 0x62, 0xdd, 0x00, + 0x62, 0xd8, 0x02, 0x62, 0xd9, 0x00, 0x62, 0xda, 0x00, 0x62, 0xdb, + 0x00, 0x62, 0xdf, 0x00, 0x62, 0x29, 0x00, 0x62, 0x30, 0x00, 0x62, + 0xbd, 0x00, 0x70, 0xef, 0x70, 0xef, 0x62, 0x00, 0x08, 0x71, 0x10, + 0x62, 0x00, 0x08, 0x62, 0x01, 0x92, 0x70, 0xef, 0x62, 0x04, 0x17, + 0x71, 0x10, 0x62, 0x04, 0x14, 0x62, 0x05, 0xbc, 0x70, 0xef, 0x62, + 0x08, 0x00, 0x71, 0x10, 0x62, 0x08, 0x00, 0x62, 0x09, 0x28, 0x70, + 0xef, 0x62, 0x0c, 0x00, 0x71, 0x10, 0x62, 0x0c, 0x00, 0x62, 0x0d, + 0x00, 0x70, 0xef, 0x62, 0x10, 0x00, 0x71, 0x10, 0x62, 0x10, 0x00, + 0x62, 0x11, 0x00, 0x70, 0xef, 0x62, 0x01, 0x00, 0x62, 0x05, 0x00, + 0x62, 0x09, 0x00, 0x62, 0x0d, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, + 0x7f, 0x55, 0x02, 0x08, 0x55, 0x03, 0x17, 0x55, 0x04, 0x00, 0x7c, + 0x03, 0x48, 0x7f, 0x7c, 0x01, 0xe4, 0x70, 0xef, 0x7f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x62, 0xd0, 0x00, 0x53, 0x00, 0x71, 0x10, 0x5d, + 0xe0, 0x08, 0x21, 0xf8, 0x29, 0x00, 0x70, 0xfe, 0x60, 0xe0, 0x70, + 0xef, 0x4b, 0x4b, 0x4b, 0x4b, 0x51, 0x02, 0x21, 0xf7, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, + 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, + 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, + 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, + 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, + 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, + 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, + 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, + 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, + 0x60, 0x00, 0x47, 0x00, 0x00, 0x49, 0x01, 0x00, 0x29, 0x08, 0x60, + 0x00, 0x57, 0x01, 0x79, 0xbf, 0xfe, 0x18, 0x71, 0x10, 0x60, 0xe0, + 0x70, 0xef, 0x71, 0x01, 0x7f, 0x08, 0x67, 0x67, 0x67, 0x67, 0x21, + 0x0f, 0xff, 0x40, 0x9f, 0x4e, 0x18, 0x21, 0x0f, 0xff, 0x39, 0x9f, + 0x47, 0x7f, 0x08, 0x10, 0x28, 0xa0, 0x0b, 0x9f, 0x3f, 0x20, 0x18, + 0x75, 0xdf, 0xf5, 0x74, 0x8f, 0xf2, 0x38, 0xfe, 0x7f, 0x52, 0x00, + 0xa0, 0x08, 0x10, 0x9f, 0x2d, 0x20, 0x75, 0x8f, 0xf6, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x50, 0x0d, 0x9f, 0x20, 0x50, 0x0a, 0x9f, 0x1c, + 0x7f, 0x70, 0xbf, 0x62, 0xd3, 0x03, 0x4f, 0x52, 0xfb, 0xa0, 0x15, + 0x7b, 0xfb, 0x52, 0xfc, 0x59, 0xfd, 0x60, 0xd3, 0x52, 0x00, 0x9f, + 0x05, 0x4f, 0x62, 0xd3, 0x03, 0x77, 0xfd, 0x8f, 0xe9, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x3d, 0xfa, 0x00, 0xb0, 0x06, 0x3d, 0xfb, 0x00, + 0xa0, 0x18, 0x10, 0x52, 0xfc, 0x59, 0xfd, 0x28, 0x9e, 0xe6, 0x20, + 0x07, 0xfd, 0x01, 0x0f, 0xfc, 0x00, 0x17, 0xfb, 0x01, 0x1f, 0xfa, + 0x00, 0x8f, 0xe0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, + 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xfb, 0x80, 0x04, + 0x2e, 0x03, 0x04, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, 0x71, 0xc0, + 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, + 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xef, + 0x80, 0x04, 0x2e, 0x03, 0x10, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x10, 0x70, + 0x3f, 0x71, 0x80, 0x5d, 0xd3, 0x08, 0x5d, 0xd0, 0x08, 0x62, 0xd0, + 0x00, 0x51, 0x08, 0x60, 0xd3, 0x2e, 0x05, 0x80, 0x49, 0xd7, 0x08, + 0xa0, 0x09, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x00, 0x80, 0x08, 0x49, + 0xd7, 0x20, 0xa0, 0x03, 0x80, 0xa6, 0x51, 0x05, 0x21, 0x0e, 0xe0, + 0x01, 0x80, 0x11, 0x80, 0x67, 0x80, 0x79, 0x80, 0x47, 0x80, 0x96, + 0x80, 0x94, 0x80, 0x92, 0x80, 0x90, 0x80, 0x97, 0x5d, 0xd8, 0x21, + 0xfe, 0x39, 0x40, 0xa0, 0x06, 0x62, 0xd7, 0x00, 0x80, 0x8a, 0x49, + 0xd8, 0x01, 0xb0, 0x0f, 0x55, 0x0c, 0x02, 0x26, 0x05, 0xf0, 0x2e, + 0x05, 0x02, 0x62, 0xd7, 0x10, 0x80, 0x77, 0x55, 0x0c, 0x01, 0x26, + 0x05, 0xf0, 0x2e, 0x05, 0x06, 0x5f, 0x07, 0x06, 0x51, 0x09, 0x02, + 0x07, 0x5c, 0x52, 0x00, 0x60, 0xd8, 0x76, 0x07, 0x62, 0xd7, 0x14, + 0x80, 0x5b, 0x51, 0x0a, 0x78, 0x3a, 0x07, 0xc0, 0x0f, 0x51, 0x09, + 0x02, 0x07, 0x5c, 0x52, 0x00, 0x60, 0xd8, 0x76, 0x07, 0x2e, 0x05, + 0x20, 0x60, 0xd8, 0x62, 0xd7, 0x04, 0x80, 0x3f, 0x5d, 0xd8, 0x3a, + 0x0a, 0xd0, 0x2b, 0xa0, 0x29, 0x53, 0x07, 0x53, 0x06, 0x26, 0x05, + 0xf0, 0x2e, 0x05, 0x04, 0x80, 0x18, 0x51, 0x0b, 0x78, 0x3a, 0x07, + 0xc0, 0x16, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x5d, 0xd8, 0x54, 0x00, + 0x2e, 0x05, 0x10, 0x76, 0x07, 0x80, 0x01, 0x62, 0xd7, 0x10, 0x80, + 0x0f, 0x62, 0xd7, 0x00, 0x80, 0x0a, 0x26, 0x05, 0xf0, 0x2e, 0x05, + 0x00, 0x55, 0x0c, 0x00, 0x18, 0x60, 0xd0, 0x18, 0x60, 0xd3, 0x20, + 0x18, 0x7e, 0x62, 0xd0, 0x00, 0x71, 0x10, 0x41, 0x04, 0xfc, 0x43, + 0x05, 0x03, 0x70, 0xef, 0x26, 0x03, 0xfc, 0x51, 0x03, 0x60, 0x04, + 0x55, 0x0c, 0x00, 0x90, 0x28, 0x90, 0x2d, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x50, 0x00, 0x53, 0x06, 0x71, 0x10, 0x43, 0x04, 0x03, 0x43, + 0x05, 0x03, 0x70, 0xef, 0x2e, 0x03, 0x03, 0x51, 0x03, 0x60, 0x04, + 0x7f, 0x62, 0xd0, 0x00, 0x51, 0x05, 0x21, 0xb0, 0x26, 0x05, 0x4f, + 0x7f, 0x41, 0xe0, 0x7f, 0x43, 0xe0, 0x80, 0x7f, 0x43, 0xd6, 0x31, + 0x7f, 0x41, 0xe0, 0x7f, 0x41, 0xd6, 0xfe, 0x7f, 0x62, 0xd0, 0x00, + 0x4f, 0x52, 0xfd, 0x53, 0x0a, 0x52, 0xfc, 0x53, 0x0b, 0x52, 0xfb, + 0x53, 0x09, 0x52, 0xfa, 0x53, 0x08, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x08, 0x5d, 0xa4, 0x04, 0x1b, 0x5d, 0xa5, 0x0c, 0x1a, 0x55, 0x1c, + 0x01, 0x18, 0x7e, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x53, + 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x8c, 0x62, 0xd3, 0x00, + 0x13, 0x7a, 0x62, 0xd3, 0x00, 0x54, 0x7e, 0x62, 0xd3, 0x00, 0x52, + 0x8b, 0x62, 0xd3, 0x00, 0x1b, 0x79, 0x62, 0xd3, 0x00, 0x54, 0x7d, + 0x48, 0x7d, 0x80, 0xb0, 0x33, 0x3d, 0x7d, 0x00, 0xb0, 0x7b, 0x51, + 0x0d, 0x3b, 0x7e, 0xc0, 0x75, 0x52, 0x7e, 0x58, 0x1e, 0x01, 0x00, + 0x6d, 0x62, 0xd3, 0x00, 0x05, 0x40, 0xc0, 0x09, 0x51, 0x0f, 0x3b, + 0x40, 0xd0, 0x12, 0xa0, 0x10, 0x56, 0x40, 0x00, 0x5b, 0x64, 0x5c, + 0x62, 0xd3, 0x00, 0x07, 0x7a, 0x01, 0x0f, 0x79, 0x00, 0x80, 0x41, + 0x3d, 0x7d, 0xff, 0xb0, 0x09, 0x50, 0xff, 0x12, 0x0e, 0x3b, 0x7e, + 0xc0, 0x20, 0x62, 0xd3, 0x00, 0x56, 0x7e, 0x00, 0x56, 0x7d, 0x00, + 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x47, 0x78, 0xd0, 0x03, + 0x50, 0x00, 0x54, 0x47, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, + 0x62, 0xd3, 0x00, 0x52, 0x8c, 0x62, 0xd3, 0x00, 0x54, 0x7a, 0x62, + 0xd3, 0x00, 0x52, 0x8b, 0x62, 0xd3, 0x00, 0x54, 0x79, 0x51, 0x1e, + 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x56, 0x7e, 0x00, 0x56, 0x7d, 0x00, + 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x12, 0x54, 0x47, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, + 0x08, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x42, 0x53, 0x19, 0x55, 0x18, + 0x00, 0x18, 0x08, 0x90, 0x7e, 0x62, 0xd3, 0x00, 0x23, 0x44, 0xb0, + 0x2c, 0x51, 0x10, 0x04, 0x19, 0x0e, 0x18, 0x00, 0x18, 0x64, 0x5c, + 0x62, 0xd3, 0x00, 0x52, 0x7e, 0x12, 0x19, 0x52, 0x7d, 0x1a, 0x18, + 0xc0, 0x39, 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x45, 0x78, + 0x54, 0x45, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x3e, 0x80, 0x18, + 0x51, 0x10, 0x14, 0x19, 0x1e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x52, 0x7e, 0x12, 0x19, 0x52, 0x7d, 0x1a, 0x18, 0xc0, + 0x0e, 0x5b, 0x67, 0x90, 0x31, 0x62, 0xd3, 0x00, 0x2d, 0x44, 0x50, + 0x01, 0x80, 0x24, 0x5b, 0x67, 0x08, 0x90, 0x23, 0x73, 0x62, 0xd3, + 0x00, 0x25, 0x44, 0x62, 0xd3, 0x00, 0x20, 0x51, 0x11, 0x54, 0x45, + 0x50, 0x00, 0x80, 0x0d, 0x5b, 0x67, 0x90, 0x0d, 0x73, 0x62, 0xd3, + 0x00, 0x25, 0x44, 0x50, 0x00, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, + 0x67, 0x67, 0x67, 0x5c, 0x18, 0x21, 0x07, 0xf0, 0x01, 0x7f, 0x01, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x70, 0xbf, 0x70, 0xbf, + 0x62, 0xd3, 0x00, 0x50, 0x02, 0x78, 0x08, 0x5c, 0x56, 0x42, 0x19, + 0x18, 0x78, 0xdf, 0xf8, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x91, + 0xb2, 0x70, 0xbf, 0x18, 0x08, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, + 0x8c, 0x62, 0xd3, 0x00, 0x54, 0x7a, 0x62, 0xd3, 0x00, 0x52, 0x8b, + 0x62, 0xd3, 0x00, 0x54, 0x79, 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x14, 0x00, 0x50, 0x02, + 0x78, 0x08, 0x9f, 0x0e, 0x39, 0x01, 0xb0, 0x04, 0x55, 0x14, 0x01, + 0x18, 0x78, 0xdf, 0xf3, 0x51, 0x14, 0x7f, 0x50, 0x02, 0x78, 0x08, + 0x9e, 0x3e, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x98, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0xd8, 0xd9, 0xda, 0xdb, 0xdf, 0x00, + 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x70, 0xbf, 0x62, + 0xd0, 0x00, 0x62, 0xd3, 0x00, 0x57, 0x00, 0x56, 0x44, 0x00, 0x79, + 0xdf, 0xfb, 0x62, 0xd3, 0x00, 0x57, 0x01, 0x50, 0x03, 0x54, 0x45, + 0x79, 0xdf, 0xfc, 0x62, 0xd3, 0x00, 0x50, 0x14, 0x57, 0x01, 0x54, + 0x47, 0x79, 0xdf, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x55, 0x0d, 0x14, + 0x55, 0x0e, 0x05, 0x55, 0x0f, 0x14, 0x55, 0x10, 0x01, 0x55, 0x11, + 0x03, 0x55, 0x12, 0x14, 0x55, 0x22, 0x04, 0x55, 0x1f, 0x14, 0x43, + 0x61, 0x0d, 0x57, 0x00, 0x50, 0x02, 0x90, 0xae, 0x50, 0x04, 0xff, + 0x98, 0x29, 0x00, 0x60, 0xa9, 0x62, 0xa0, 0x08, 0x43, 0xa2, 0x04, + 0x62, 0xa3, 0x70, 0x43, 0x7a, 0x01, 0x43, 0xaa, 0x02, 0x43, 0xdf, + 0x01, 0x50, 0x01, 0x57, 0x09, 0x90, 0x20, 0x90, 0x55, 0x57, 0x01, + 0x50, 0xb3, 0x91, 0x5d, 0x50, 0x01, 0x57, 0x0e, 0x90, 0x12, 0x90, + 0x47, 0x7f, 0x53, 0x22, 0xff, 0x67, 0x29, 0x00, 0x60, 0xa9, 0x51, + 0x21, 0x58, 0x20, 0x90, 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, 0x03, + 0x53, 0x21, 0x64, 0x64, 0x64, 0x64, 0x64, 0x29, 0x80, 0x60, 0xa1, + 0x5b, 0x78, 0x21, 0x0f, 0x29, 0x08, 0x74, 0x53, 0x20, 0x12, 0x22, + 0x02, 0x21, 0x5c, 0x50, 0x00, 0x53, 0x1d, 0x53, 0x23, 0x29, 0x01, + 0x79, 0xa0, 0x08, 0x64, 0x6b, 0x1d, 0x6b, 0x23, 0x8f, 0xf5, 0x60, + 0xb5, 0x51, 0x1d, 0x60, 0xb4, 0x7f, 0x50, 0x02, 0x78, 0x08, 0x90, + 0x28, 0x90, 0x5a, 0x18, 0x78, 0xdf, 0xf8, 0x7f, 0x41, 0xdf, 0xfe, + 0x71, 0x10, 0x41, 0xd8, 0xfd, 0x70, 0xef, 0x41, 0x61, 0xf3, 0x41, + 0xa2, 0xfb, 0x41, 0xa0, 0xf7, 0x62, 0xa3, 0x00, 0x62, 0xa9, 0x00, + 0x41, 0xaa, 0xfd, 0x7f, 0x02, 0x20, 0x02, 0x08, 0x64, 0x5c, 0xff, + 0xf8, 0x4b, 0x74, 0xff, 0xf4, 0x7f, 0x62, 0xd0, 0x00, 0x53, 0x1d, + 0x10, 0x5b, 0x64, 0x64, 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x2a, 0x1d, + 0x61, 0x01, 0x36, 0x1d, 0xff, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, + 0x36, 0x1d, 0xff, 0x18, 0xfe, 0xd6, 0x5c, 0x5e, 0x00, 0x2a, 0x1d, + 0x61, 0x00, 0x70, 0xef, 0x7f, 0x62, 0xd0, 0x00, 0x10, 0x73, 0x53, + 0x1d, 0x71, 0x10, 0x5b, 0xfe, 0xc0, 0x5c, 0x5e, 0x00, 0x22, 0x1d, + 0x61, 0x00, 0x70, 0xef, 0x18, 0x64, 0x64, 0x5c, 0x71, 0x10, 0x5e, + 0x01, 0x22, 0x1d, 0x61, 0x01, 0x36, 0x1d, 0xff, 0x5e, 0x00, 0x2a, + 0x1d, 0x61, 0x00, 0x70, 0xef, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, + 0x53, 0x1e, 0x50, 0x00, 0x53, 0x1a, 0x53, 0x1b, 0x51, 0x1e, 0x5c, + 0x62, 0xd3, 0x00, 0x52, 0x24, 0x53, 0x1f, 0x43, 0xa0, 0x01, 0x51, + 0x1f, 0x60, 0xfd, 0x41, 0xa3, 0xdf, 0x51, 0x1e, 0x9f, 0x7a, 0x9f, + 0x81, 0x58, 0x23, 0x55, 0x1c, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, + 0x00, 0x43, 0xb3, 0x01, 0x51, 0x1c, 0xaf, 0xfd, 0x79, 0xdf, 0xee, + 0x51, 0x1e, 0x9f, 0x5f, 0x9f, 0x91, 0x43, 0xa3, 0x20, 0x41, 0xa0, + 0xfe, 0x62, 0xfd, 0x00, 0x50, 0xff, 0x4c, 0x1b, 0x14, 0x1b, 0x51, + 0x20, 0x11, 0x08, 0xfe, 0x4d, 0x4c, 0x1a, 0x1c, 0x1a, 0xd0, 0x07, + 0x55, 0x1a, 0x00, 0x55, 0x1b, 0x00, 0x51, 0x1e, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x51, 0x1b, 0x54, 0x8c, 0x51, 0x1a, 0x54, 0x8b, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x9f, 0x86, 0x18, 0x78, 0xdf, 0xfa, + 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, 0x27, 0x5a, 0x26, 0x55, + 0x1e, 0x01, 0x62, 0xd3, 0x00, 0x58, 0x1e, 0x56, 0x24, 0x80, 0x55, + 0x29, 0x08, 0x55, 0x28, 0x80, 0x51, 0x1e, 0x9f, 0x63, 0x51, 0x1e, + 0x9f, 0x5f, 0x70, 0xbf, 0x58, 0x1e, 0x62, 0xd3, 0x00, 0x51, 0x1b, + 0x3a, 0x27, 0x51, 0x1a, 0x1a, 0x26, 0xd0, 0x06, 0x51, 0x28, 0x73, + 0x25, 0x24, 0x68, 0x28, 0x26, 0x28, 0x7f, 0x51, 0x28, 0x2d, 0x24, + 0x7a, 0x29, 0xbf, 0xd6, 0x7a, 0x1e, 0xdf, 0xc4, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0xa0, 0x11, 0x10, 0x51, 0x9f, + 0x19, 0x0e, 0xd0, 0x12, 0x7c, 0x14, 0x1d, 0x39, 0x0f, 0xa0, 0x16, + 0x62, 0xd0, 0x00, 0x76, 0xa0, 0x0e, 0x9f, 0x00, 0x80, 0x0c, 0x62, + 0xd0, 0x00, 0x55, 0xa0, 0x00, 0x55, 0x9f, 0x00, 0x90, 0xa9, 0x7f, + 0x62, 0xd0, 0x00, 0x3c, 0xa7, 0xf0, 0xd0, 0x03, 0x76, 0xa7, 0x62, + 0xd0, 0x00, 0x51, 0x2f, 0x21, 0x7f, 0x53, 0x58, 0x51, 0xa7, 0x3a, + 0x58, 0xb0, 0x50, 0x7c, 0x14, 0x1d, 0x62, 0xd0, 0x00, 0x53, 0xad, + 0x3c, 0xad, 0x0f, 0xa0, 0x3d, 0x3c, 0xa9, 0x00, 0xb0, 0x1c, 0x55, + 0x93, 0x00, 0x55, 0x94, 0x00, 0x51, 0xad, 0x53, 0x57, 0x55, 0x58, + 0x00, 0x06, 0x57, 0x93, 0x0e, 0x58, 0x00, 0x51, 0x58, 0x60, 0xd5, + 0x50, 0x08, 0x3f, 0x57, 0x62, 0xd0, 0x00, 0x55, 0xa5, 0x00, 0x3c, + 0xac, 0x00, 0xb0, 0x0a, 0x7c, 0x14, 0xb2, 0x62, 0xd0, 0x00, 0x55, + 0xac, 0x01, 0x62, 0xd0, 0x00, 0x55, 0xb0, 0x03, 0x80, 0x07, 0x62, + 0xd0, 0x00, 0x55, 0xa7, 0x00, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0xa0, + 0x00, 0x55, 0x9f, 0x00, 0x3c, 0xac, 0x01, 0xb0, 0x31, 0x7a, 0xb0, + 0x3c, 0xb0, 0x00, 0xb0, 0x2a, 0x3c, 0xac, 0x01, 0xb0, 0x0a, 0x7c, + 0x15, 0x45, 0x62, 0xd0, 0x00, 0x55, 0xac, 0x00, 0x62, 0xd0, 0x00, + 0x3c, 0xa9, 0x00, 0xb0, 0x0e, 0x51, 0xad, 0x53, 0x57, 0x55, 0x58, + 0x00, 0x06, 0x57, 0x93, 0x7c, 0x1a, 0xcf, 0x62, 0xd0, 0x00, 0x55, + 0xa7, 0x00, 0x7f, 0x10, 0x4f, 0x38, 0x16, 0x62, 0xd0, 0x00, 0x3c, + 0xa8, 0x00, 0xb0, 0x05, 0x51, 0x9b, 0x53, 0x24, 0x56, 0x0d, 0x00, + 0x80, 0x67, 0x56, 0x00, 0x00, 0x80, 0x5b, 0x62, 0xd0, 0x00, 0x3c, + 0xa8, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, + 0x06, 0x57, 0x9b, 0x7c, 0x19, 0xf9, 0x52, 0x00, 0x53, 0x55, 0x55, + 0x56, 0x00, 0x06, 0x55, 0x24, 0x7c, 0x1a, 0xc3, 0x10, 0x52, 0x00, + 0x7c, 0x09, 0x5a, 0x20, 0x10, 0x7c, 0x05, 0xe5, 0x62, 0xd0, 0x00, + 0x20, 0x39, 0x00, 0xbf, 0xee, 0x7c, 0x19, 0x52, 0x52, 0x0d, 0x7c, + 0x1a, 0xb7, 0x02, 0x57, 0x53, 0x57, 0x51, 0x56, 0x0a, 0x58, 0x53, + 0x58, 0x7c, 0x1a, 0x85, 0x06, 0x55, 0x8b, 0x0e, 0x56, 0x00, 0x51, + 0x56, 0x7c, 0x19, 0xe3, 0x7c, 0x1a, 0x05, 0x77, 0x00, 0x3d, 0x00, + 0x02, 0xcf, 0xa2, 0x77, 0x0d, 0x3d, 0x0d, 0x03, 0xcf, 0x96, 0x56, + 0x00, 0x00, 0x81, 0x06, 0x7c, 0x19, 0x47, 0x7c, 0x19, 0x9e, 0x51, + 0x58, 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x0e, 0x3e, 0x57, 0x54, 0x0f, + 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, 0x55, 0x55, 0x06, 0x55, + 0x56, 0x00, 0x55, 0x52, 0x00, 0x55, 0x51, 0x00, 0x3c, 0x56, 0x00, + 0xb0, 0x06, 0x3c, 0x55, 0x00, 0xa0, 0x1a, 0x70, 0xfb, 0x6e, 0x56, + 0x6e, 0x55, 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x57, 0x04, 0x52, + 0x51, 0x58, 0x0c, 0x51, 0x65, 0x57, 0x6b, 0x58, 0x8f, 0xde, 0x5f, + 0x57, 0x52, 0x5f, 0x58, 0x51, 0x62, 0xd0, 0x00, 0x5a, 0x55, 0x06, + 0x55, 0x03, 0x51, 0x55, 0x04, 0x57, 0x0e, 0x58, 0x03, 0x51, 0x58, + 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x10, 0x3e, 0x57, 0x54, 0x11, 0x52, + 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, 0x55, 0x55, 0x06, 0x55, 0x56, + 0x00, 0x55, 0x52, 0x00, 0x55, 0x51, 0x00, 0x3c, 0x56, 0x00, 0xb0, + 0x06, 0x3c, 0x55, 0x00, 0xa0, 0x1a, 0x70, 0xfb, 0x6e, 0x56, 0x6e, + 0x55, 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x57, 0x04, 0x52, 0x51, + 0x58, 0x0c, 0x51, 0x65, 0x57, 0x6b, 0x58, 0x8f, 0xde, 0x5f, 0x57, + 0x52, 0x5f, 0x58, 0x51, 0x62, 0xd0, 0x00, 0x5a, 0x55, 0x06, 0x55, + 0x05, 0x51, 0x55, 0x04, 0x57, 0x0e, 0x58, 0x03, 0x51, 0x58, 0x60, + 0xd4, 0x3e, 0x57, 0x54, 0x12, 0x3e, 0x57, 0x54, 0x13, 0x50, 0x03, + 0x08, 0x5a, 0x57, 0x06, 0x57, 0x0e, 0x08, 0x51, 0x57, 0x08, 0x7c, + 0x17, 0xdc, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, 0x57, 0x54, 0x15, + 0x51, 0x58, 0x54, 0x14, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x8b, + 0x7c, 0x1a, 0x33, 0x06, 0x57, 0x79, 0x7c, 0x1a, 0x6a, 0x7c, 0x19, + 0x2a, 0x51, 0x57, 0x01, 0x61, 0x7c, 0x1a, 0x33, 0x51, 0x57, 0x01, + 0x69, 0x7c, 0x1a, 0x33, 0x06, 0x57, 0x71, 0x7c, 0x1a, 0x6a, 0x77, + 0x00, 0x3d, 0x00, 0x02, 0xce, 0xf7, 0x38, 0xea, 0x20, 0x7f, 0x10, + 0x4f, 0x38, 0x16, 0x10, 0x57, 0x09, 0x50, 0x01, 0x7c, 0x08, 0x94, + 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x10, 0x08, 0x57, 0xc2, 0x28, + 0x53, 0x58, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0x57, 0x20, 0x10, + 0x51, 0x58, 0x08, 0x51, 0x57, 0x20, 0x7c, 0x09, 0xd9, 0x20, 0x10, + 0x57, 0x0e, 0x50, 0x01, 0x7c, 0x08, 0x94, 0x20, 0x62, 0xd0, 0x00, + 0x3c, 0xa8, 0x01, 0xb0, 0x0b, 0x51, 0x24, 0x53, 0x30, 0x51, 0x25, + 0x53, 0x31, 0x80, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x9b, 0x53, 0x24, + 0x51, 0x9c, 0x53, 0x25, 0x10, 0x50, 0x00, 0x7c, 0x09, 0x5a, 0x20, + 0x56, 0x0d, 0x00, 0x80, 0x67, 0x56, 0x00, 0x00, 0x80, 0x5b, 0x62, + 0xd0, 0x00, 0x3c, 0xa8, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x57, + 0x55, 0x58, 0x00, 0x06, 0x57, 0x9b, 0x7c, 0x19, 0xf9, 0x52, 0x00, + 0x53, 0x55, 0x55, 0x56, 0x00, 0x06, 0x55, 0x24, 0x7c, 0x1a, 0xc3, + 0x10, 0x52, 0x00, 0x7c, 0x09, 0x5a, 0x20, 0x10, 0x7c, 0x05, 0xe5, + 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x7c, 0x19, 0x52, + 0x52, 0x0d, 0x7c, 0x1a, 0xb7, 0x02, 0x57, 0x53, 0x57, 0x51, 0x56, + 0x0a, 0x58, 0x53, 0x58, 0x7c, 0x1a, 0x85, 0x06, 0x55, 0x8b, 0x0e, + 0x56, 0x00, 0x51, 0x56, 0x7c, 0x19, 0xe3, 0x7c, 0x1a, 0x05, 0x77, + 0x00, 0x3d, 0x00, 0x02, 0xcf, 0xa2, 0x77, 0x0d, 0x3d, 0x0d, 0x03, + 0xcf, 0x96, 0x56, 0x00, 0x00, 0x81, 0x06, 0x7c, 0x19, 0x47, 0x7c, + 0x19, 0x9e, 0x51, 0x58, 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x0e, 0x3e, + 0x57, 0x54, 0x0f, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, 0x55, + 0x55, 0x06, 0x55, 0x56, 0x00, 0x55, 0x52, 0x00, 0x55, 0x51, 0x00, + 0x3c, 0x56, 0x00, 0xb0, 0x06, 0x3c, 0x55, 0x00, 0xa0, 0x1a, 0x70, + 0xfb, 0x6e, 0x56, 0x6e, 0x55, 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, + 0x57, 0x04, 0x52, 0x51, 0x58, 0x0c, 0x51, 0x65, 0x57, 0x6b, 0x58, + 0x8f, 0xde, 0x5f, 0x57, 0x52, 0x5f, 0x58, 0x51, 0x62, 0xd0, 0x00, + 0x5a, 0x55, 0x06, 0x55, 0x03, 0x51, 0x55, 0x04, 0x57, 0x0e, 0x58, + 0x03, 0x51, 0x58, 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x10, 0x3e, 0x57, + 0x54, 0x11, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, 0x55, 0x55, + 0x06, 0x55, 0x56, 0x00, 0x55, 0x52, 0x00, 0x55, 0x51, 0x00, 0x3c, + 0x56, 0x00, 0xb0, 0x06, 0x3c, 0x55, 0x00, 0xa0, 0x1a, 0x70, 0xfb, + 0x6e, 0x56, 0x6e, 0x55, 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x57, + 0x04, 0x52, 0x51, 0x58, 0x0c, 0x51, 0x65, 0x57, 0x6b, 0x58, 0x8f, + 0xde, 0x5f, 0x57, 0x52, 0x5f, 0x58, 0x51, 0x62, 0xd0, 0x00, 0x5a, + 0x55, 0x06, 0x55, 0x05, 0x51, 0x55, 0x04, 0x57, 0x0e, 0x58, 0x03, + 0x51, 0x58, 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x12, 0x3e, 0x57, 0x54, + 0x13, 0x50, 0x03, 0x08, 0x5a, 0x57, 0x06, 0x57, 0x0e, 0x08, 0x51, + 0x57, 0x08, 0x7c, 0x17, 0xdc, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, + 0x57, 0x54, 0x15, 0x51, 0x58, 0x54, 0x14, 0x7c, 0x19, 0x2a, 0x51, + 0x57, 0x01, 0x8b, 0x7c, 0x1a, 0x33, 0x06, 0x57, 0x79, 0x7c, 0x1a, + 0x6a, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x61, 0x7c, 0x1a, 0x33, + 0x51, 0x57, 0x01, 0x69, 0x7c, 0x1a, 0x33, 0x06, 0x57, 0x71, 0x7c, + 0x1a, 0x6a, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0xf7, 0x56, 0x00, + 0x00, 0x80, 0x19, 0x7c, 0x19, 0x47, 0x06, 0x57, 0x24, 0x7c, 0x19, + 0xf9, 0x52, 0x00, 0x53, 0x55, 0x55, 0x56, 0x00, 0x06, 0x55, 0x30, + 0x7c, 0x1a, 0xc3, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0xe4, 0x38, + 0xea, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x62, 0xd0, 0x00, 0x52, + 0xfc, 0x01, 0x02, 0x53, 0x57, 0x52, 0xfb, 0x09, 0x00, 0x7c, 0x1a, + 0x12, 0x52, 0xfc, 0x01, 0x04, 0x53, 0x55, 0x52, 0xfb, 0x7c, 0x19, + 0xee, 0x12, 0x57, 0x51, 0x56, 0x1a, 0x58, 0xc0, 0x6f, 0x52, 0xfc, + 0x53, 0x57, 0x52, 0xfb, 0x7c, 0x1a, 0x12, 0x52, 0xfc, 0x01, 0x02, + 0x53, 0x55, 0x52, 0xfb, 0x7c, 0x19, 0xee, 0x12, 0x57, 0x51, 0x56, + 0x1a, 0x58, 0xc0, 0x10, 0x52, 0xfc, 0x01, 0x02, 0x7c, 0x1a, 0x51, + 0x54, 0x00, 0x3e, 0x57, 0x54, 0x01, 0x80, 0xb3, 0x62, 0xd0, 0x00, + 0x52, 0xfc, 0x01, 0x04, 0x53, 0x57, 0x52, 0xfb, 0x09, 0x00, 0x7c, + 0x1a, 0x12, 0x52, 0xfc, 0x53, 0x55, 0x52, 0xfb, 0x60, 0xd4, 0x3e, + 0x55, 0x53, 0x56, 0x3e, 0x55, 0x12, 0x57, 0x51, 0x56, 0x1a, 0x58, + 0xc0, 0x10, 0x52, 0xfc, 0x01, 0x04, 0x7c, 0x1a, 0x51, 0x54, 0x00, + 0x3e, 0x57, 0x54, 0x01, 0x80, 0x7e, 0x62, 0xd0, 0x00, 0x52, 0xfc, + 0x53, 0x57, 0x52, 0xfb, 0x7c, 0x1a, 0x7a, 0x80, 0x70, 0x62, 0xd0, + 0x00, 0x52, 0xfc, 0x53, 0x57, 0x52, 0xfb, 0x7c, 0x1a, 0x12, 0x52, + 0xfc, 0x01, 0x04, 0x53, 0x55, 0x52, 0xfb, 0x7c, 0x19, 0xee, 0x12, + 0x57, 0x51, 0x56, 0x1a, 0x58, 0xc0, 0x10, 0x52, 0xfc, 0x01, 0x04, + 0x7c, 0x1a, 0x51, 0x54, 0x00, 0x3e, 0x57, 0x54, 0x01, 0x80, 0x42, + 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x01, 0x02, 0x53, 0x57, 0x52, 0xfb, + 0x09, 0x00, 0x7c, 0x1a, 0x12, 0x52, 0xfc, 0x53, 0x55, 0x52, 0xfb, + 0x60, 0xd4, 0x3e, 0x55, 0x53, 0x56, 0x3e, 0x55, 0x12, 0x57, 0x51, + 0x56, 0x1a, 0x58, 0xc0, 0x10, 0x52, 0xfc, 0x01, 0x02, 0x7c, 0x1a, + 0x51, 0x54, 0x00, 0x3e, 0x57, 0x54, 0x01, 0x80, 0x0d, 0x62, 0xd0, + 0x00, 0x52, 0xfc, 0x53, 0x57, 0x52, 0xfb, 0x7c, 0x1a, 0x7a, 0x62, + 0xd0, 0x00, 0x52, 0x01, 0x53, 0x57, 0x52, 0x00, 0x53, 0x58, 0x38, + 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x05, 0x62, 0xd0, 0x00, 0x55, + 0xaf, 0x00, 0x56, 0x00, 0x00, 0x80, 0x38, 0x62, 0xd0, 0x00, 0x3c, + 0xa8, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, + 0x06, 0x57, 0x9b, 0x7c, 0x19, 0xf9, 0x52, 0x00, 0x53, 0x55, 0x55, + 0x56, 0x00, 0x06, 0x55, 0x24, 0x7c, 0x1a, 0xc3, 0x10, 0x52, 0x00, + 0x7c, 0x09, 0x5a, 0x20, 0x10, 0x7c, 0x05, 0xe5, 0x62, 0xd0, 0x00, + 0x20, 0x39, 0x00, 0xbf, 0xee, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, + 0xc5, 0x56, 0x00, 0x00, 0x82, 0x86, 0x62, 0xd0, 0x00, 0x3c, 0xae, + 0x02, 0xa0, 0x9f, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x79, 0x7c, + 0x19, 0x36, 0x06, 0x57, 0x8b, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, 0x44, + 0xd0, 0x16, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x79, 0x7c, 0x19, + 0x36, 0x06, 0x57, 0x8b, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, 0xdb, 0x80, + 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x8b, + 0x7c, 0x19, 0x36, 0x06, 0x57, 0x79, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, + 0xdb, 0x50, 0x5e, 0x13, 0x02, 0x50, 0x01, 0x1b, 0x01, 0xc0, 0x4e, + 0x62, 0xd0, 0x00, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x71, 0x7c, + 0x19, 0x36, 0x06, 0x57, 0x8b, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, 0x44, + 0xd0, 0x16, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x71, 0x7c, 0x19, + 0x36, 0x06, 0x57, 0x8b, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, 0xe8, 0x80, + 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x8b, + 0x7c, 0x19, 0x36, 0x06, 0x57, 0x71, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, + 0xe8, 0x50, 0x5e, 0x13, 0x04, 0x50, 0x01, 0x1b, 0x03, 0xd0, 0x08, + 0x62, 0xd0, 0x00, 0x76, 0xaf, 0x81, 0xde, 0x62, 0xd0, 0x00, 0x7c, + 0x19, 0x2a, 0x51, 0x57, 0x01, 0x8b, 0x7c, 0x19, 0x36, 0x06, 0x57, + 0x71, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, 0x44, 0xd0, 0x5a, 0x7c, 0x19, + 0x2a, 0x7c, 0x1a, 0x1d, 0x06, 0x55, 0x01, 0x0e, 0x56, 0x00, 0x7c, + 0x1a, 0x05, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x8b, 0x7c, 0x19, + 0x36, 0x06, 0x57, 0x71, 0x7c, 0x19, 0xf9, 0x7c, 0x1a, 0x44, 0xd0, + 0xb4, 0x7c, 0x19, 0x2a, 0x7c, 0x1a, 0x1d, 0x06, 0x55, 0x01, 0x0e, + 0x56, 0x00, 0x7c, 0x1a, 0x05, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, + 0x8b, 0x7c, 0x19, 0x36, 0x06, 0x57, 0x71, 0x7c, 0x19, 0xf9, 0x7c, + 0x1a, 0x44, 0xd0, 0x90, 0x7c, 0x19, 0x2a, 0x7c, 0x1a, 0x1d, 0x06, + 0x55, 0x01, 0x0e, 0x56, 0x00, 0x7c, 0x1a, 0x05, 0x80, 0x7f, 0x62, + 0xd0, 0x00, 0x7c, 0x19, 0x2a, 0x51, 0x57, 0x01, 0x8b, 0x7c, 0x19, + 0x36, 0x06, 0x57, 0x71, 0x7c, 0x19, 0xf9, 0x3e, 0x57, 0x12, 0x55, + 0x51, 0x58, 0x1a, 0x56, 0xd0, 0x62, 0x7c, 0x19, 0x2a, 0x7c, 0x1a, + 0x1d, 0x16, 0x55, 0x01, 0x1e, 0x56, 0x00, 0x7c, 0x1a, 0x05, 0x97, + 0xf8, 0x40, 0x51, 0x57, 0x01, 0x8b, 0x97, 0xfd, 0x40, 0x06, 0x57, + 0x71, 0x7c, 0x19, 0xf9, 0x3e, 0x57, 0x12, 0x55, 0x51, 0x58, 0x1a, + 0x56, 0xd0, 0x39, 0x97, 0xde, 0x40, 0x7c, 0x1a, 0x1d, 0x16, 0x55, + 0x01, 0x1e, 0x56, 0x00, 0x7c, 0x1a, 0x05, 0x97, 0xcf, 0x40, 0x51, + 0x57, 0x01, 0x8b, 0x97, 0xd4, 0x40, 0x06, 0x57, 0x71, 0x7c, 0x19, + 0xf9, 0x3e, 0x57, 0x12, 0x55, 0x51, 0x58, 0x1a, 0x56, 0xd0, 0x10, + 0x97, 0xb5, 0x40, 0x7c, 0x1a, 0x1d, 0x16, 0x55, 0x01, 0x1e, 0x56, + 0x00, 0x7c, 0x1a, 0x05, 0x62, 0xd0, 0x00, 0x97, 0xa3, 0x40, 0x51, + 0x57, 0x01, 0x69, 0x97, 0xa8, 0x40, 0x06, 0x57, 0x61, 0x0e, 0x58, + 0x00, 0x7c, 0x1a, 0x05, 0x97, 0x90, 0x40, 0x51, 0x57, 0x01, 0x71, + 0x97, 0x95, 0x40, 0x06, 0x57, 0x69, 0x0e, 0x58, 0x00, 0x7c, 0x1a, + 0x05, 0x97, 0x7d, 0x40, 0x51, 0x57, 0x01, 0x8b, 0x97, 0x82, 0x40, + 0x06, 0x57, 0x71, 0x0e, 0x58, 0x00, 0x7c, 0x1a, 0x05, 0x10, 0x52, + 0x00, 0x7c, 0x06, 0x29, 0x20, 0x62, 0xd0, 0x00, 0x97, 0x60, 0x40, + 0x51, 0x57, 0x01, 0x8b, 0x97, 0x65, 0x40, 0x06, 0x57, 0x79, 0x7c, + 0x19, 0xf9, 0x7c, 0x1a, 0x44, 0xd0, 0x25, 0x52, 0x00, 0x53, 0x57, + 0x55, 0x58, 0x00, 0x06, 0x57, 0x97, 0x0e, 0x58, 0x00, 0x51, 0x58, + 0x60, 0xd4, 0x3e, 0x57, 0x7a, 0x57, 0x53, 0x56, 0x06, 0x56, 0x01, + 0x51, 0x58, 0x60, 0xd5, 0x51, 0x56, 0x3f, 0x57, 0x80, 0x0a, 0x97, + 0x44, 0x40, 0x06, 0x57, 0x97, 0x7c, 0x1a, 0xcf, 0x97, 0x3b, 0x40, + 0x06, 0x57, 0x97, 0x97, 0xe7, 0x40, 0x50, 0x05, 0x3a, 0x58, 0xd0, + 0x58, 0x97, 0x0f, 0x40, 0x51, 0x57, 0x01, 0x79, 0x53, 0x55, 0x51, + 0x58, 0x09, 0x00, 0x53, 0x56, 0x06, 0x57, 0x8b, 0x97, 0xcc, 0x40, + 0x3e, 0x57, 0x53, 0x57, 0x51, 0x56, 0x60, 0xd4, 0x3e, 0x55, 0x53, + 0x54, 0x3e, 0x55, 0x16, 0x55, 0x02, 0x02, 0x57, 0x53, 0x57, 0x51, + 0x54, 0x0a, 0x58, 0x53, 0x58, 0x70, 0xfb, 0x6e, 0x58, 0x6e, 0x57, + 0x51, 0x56, 0x60, 0xd5, 0x51, 0x58, 0x3f, 0x55, 0x51, 0x57, 0x3f, + 0x55, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, 0x06, 0x57, 0x97, + 0x0e, 0x58, 0x00, 0x51, 0x58, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x57, + 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcd, 0x77, 0x62, 0xd0, 0x00, 0x3c, + 0xae, 0x02, 0xb1, 0x10, 0x56, 0x00, 0x00, 0x81, 0x06, 0x62, 0xd0, + 0x00, 0x96, 0xa1, 0x40, 0x51, 0x57, 0x01, 0x8b, 0x96, 0xa6, 0x40, + 0x06, 0x57, 0x38, 0x0e, 0x58, 0x00, 0x97, 0x6c, 0x40, 0x96, 0x8e, + 0x40, 0x51, 0x57, 0x01, 0x79, 0x96, 0x93, 0x40, 0x06, 0x57, 0x3c, + 0x0e, 0x58, 0x00, 0x97, 0x59, 0x40, 0x96, 0x7b, 0x40, 0x51, 0x57, + 0x01, 0x79, 0x96, 0x80, 0x40, 0x51, 0x57, 0x01, 0x8b, 0x53, 0x53, + 0x51, 0x58, 0x97, 0xe9, 0x40, 0x51, 0x55, 0x12, 0x53, 0x51, 0x56, + 0x1a, 0x54, 0xd0, 0x21, 0x97, 0xb7, 0x40, 0x51, 0x55, 0x01, 0x79, + 0x53, 0x53, 0x51, 0x56, 0x97, 0xd1, 0x40, 0x06, 0x55, 0x8b, 0x97, + 0x7d, 0x40, 0x12, 0x53, 0x54, 0x02, 0x51, 0x56, 0x1a, 0x54, 0x54, + 0x01, 0x80, 0x07, 0x56, 0x02, 0x00, 0x56, 0x01, 0x00, 0x62, 0xd0, + 0x00, 0x06, 0x57, 0x34, 0x0e, 0x58, 0x00, 0x51, 0x58, 0x60, 0xd5, + 0x52, 0x01, 0x3f, 0x57, 0x52, 0x02, 0x3f, 0x57, 0x96, 0x21, 0x40, + 0x51, 0x57, 0x01, 0x8b, 0x96, 0x26, 0x40, 0x06, 0x57, 0x38, 0x0e, + 0x58, 0x00, 0x96, 0xec, 0x40, 0x96, 0x0e, 0x40, 0x51, 0x57, 0x01, + 0x79, 0x96, 0x13, 0x40, 0x06, 0x57, 0x3c, 0x0e, 0x58, 0x00, 0x96, + 0xd9, 0x40, 0x95, 0xfb, 0x40, 0x51, 0x57, 0x01, 0x79, 0x96, 0x00, + 0x40, 0x51, 0x57, 0x01, 0x8b, 0x53, 0x53, 0x51, 0x58, 0x97, 0x69, + 0x40, 0x51, 0x55, 0x12, 0x53, 0x51, 0x56, 0x1a, 0x54, 0xd0, 0x21, + 0x97, 0x37, 0x40, 0x51, 0x55, 0x01, 0x79, 0x53, 0x53, 0x51, 0x56, + 0x97, 0x51, 0x40, 0x06, 0x55, 0x8b, 0x96, 0xfd, 0x40, 0x12, 0x53, + 0x54, 0x04, 0x51, 0x56, 0x1a, 0x54, 0x54, 0x03, 0x80, 0x07, 0x56, + 0x04, 0x00, 0x56, 0x03, 0x00, 0x62, 0xd0, 0x00, 0x06, 0x57, 0x34, + 0x0e, 0x58, 0x00, 0x51, 0x58, 0x60, 0xd5, 0x52, 0x03, 0x3f, 0x57, + 0x52, 0x04, 0x3f, 0x57, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0xf7, + 0x62, 0xd0, 0x00, 0x3c, 0xae, 0x02, 0xa0, 0x18, 0x3c, 0xaf, 0x00, + 0xa0, 0x13, 0x50, 0x01, 0x08, 0x50, 0x2c, 0x08, 0x90, 0x0e, 0x38, + 0xfe, 0x7c, 0x0a, 0xf9, 0x10, 0x7c, 0x07, 0xe5, 0x20, 0x38, 0xfb, + 0x20, 0x7f, 0x10, 0x4f, 0x80, 0x02, 0x40, 0x62, 0xd0, 0x00, 0x52, + 0xfc, 0x53, 0x57, 0x52, 0xfb, 0x53, 0x58, 0x51, 0x57, 0x11, 0x01, + 0x54, 0xfc, 0x51, 0x58, 0x19, 0x00, 0x54, 0xfb, 0x3c, 0x58, 0x00, + 0xbf, 0xe4, 0x3c, 0x57, 0x00, 0xbf, 0xdf, 0x20, 0x7f, 0x10, 0x7c, + 0x04, 0xaf, 0x7c, 0x04, 0x8c, 0x20, 0x7f, 0x10, 0x7c, 0x04, 0xab, + 0x7c, 0x04, 0x88, 0x20, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0x42, 0x12, + 0x7e, 0x50, 0x00, 0x1a, 0x7d, 0xd0, 0x0f, 0x51, 0x43, 0x12, 0x80, + 0x50, 0x00, 0x1a, 0x7f, 0xd0, 0x05, 0x50, 0x0f, 0x80, 0x17, 0x62, + 0xd0, 0x00, 0x51, 0x80, 0x12, 0x7e, 0x51, 0x7f, 0x1a, 0x7d, 0xd0, + 0x05, 0x50, 0x00, 0x80, 0x06, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x7f, + 0x10, 0x4f, 0x38, 0x05, 0x62, 0xd0, 0x00, 0x51, 0x7e, 0x54, 0x02, + 0x51, 0x7d, 0x54, 0x01, 0x56, 0x04, 0x00, 0x56, 0x00, 0x00, 0x56, + 0x03, 0x00, 0x80, 0x61, 0x95, 0x0e, 0x40, 0x06, 0x57, 0x42, 0x0e, + 0x58, 0x00, 0x51, 0x58, 0x60, 0xd4, 0x3e, 0x57, 0x53, 0x57, 0x96, + 0x3b, 0x40, 0x06, 0x55, 0x7d, 0x0e, 0x56, 0x00, 0x51, 0x56, 0x95, + 0x8e, 0x40, 0x51, 0x57, 0x12, 0x55, 0x50, 0x00, 0x1a, 0x56, 0xd0, + 0x03, 0x77, 0x03, 0x62, 0xd0, 0x00, 0x94, 0xc3, 0x40, 0x06, 0x57, + 0x7d, 0x95, 0x8c, 0x40, 0x3e, 0x57, 0x53, 0x57, 0x52, 0x02, 0x12, + 0x57, 0x52, 0x01, 0x1a, 0x58, 0xd0, 0x1a, 0x94, 0xac, 0x40, 0x06, + 0x57, 0x7d, 0x0e, 0x58, 0x00, 0x51, 0x58, 0x60, 0xd4, 0x3e, 0x57, + 0x54, 0x01, 0x3e, 0x57, 0x54, 0x02, 0x52, 0x00, 0x54, 0x04, 0x77, + 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x9c, 0x50, 0x01, 0x3b, 0x03, 0xd0, + 0x08, 0x62, 0xd0, 0x00, 0x50, 0x0f, 0x80, 0x06, 0x52, 0x04, 0x62, + 0xd0, 0x00, 0x38, 0xfb, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x70, + 0xfe, 0x62, 0xd0, 0x00, 0x26, 0x2a, 0xf0, 0x51, 0xad, 0x01, 0x01, + 0x53, 0x58, 0x51, 0x2a, 0x2a, 0x58, 0x53, 0x2a, 0x71, 0x01, 0x62, + 0xe3, 0x38, 0x10, 0x7c, 0x05, 0xe5, 0x62, 0xd0, 0x00, 0x20, 0x41, + 0x00, 0xf7, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, + 0x7c, 0x05, 0xe5, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x58, 0x47, 0x58, + 0x20, 0xa0, 0x03, 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x01, 0x08, + 0x9e, 0xb6, 0x38, 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, + 0x11, 0x0a, 0x52, 0x00, 0x19, 0x04, 0xcf, 0xd7, 0x56, 0x01, 0x00, + 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, 0x7c, 0x05, 0xe5, 0x62, 0xd0, + 0x00, 0x20, 0x53, 0x58, 0x47, 0x58, 0x20, 0xb0, 0x03, 0x80, 0x1a, + 0x50, 0x00, 0x08, 0x50, 0x01, 0x08, 0x9e, 0x84, 0x38, 0xfe, 0x77, + 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, 0x11, 0xce, 0x52, 0x00, 0x19, + 0x00, 0xcf, 0xd7, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, + 0x4f, 0x38, 0x02, 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x26, 0x2a, 0xf0, + 0x51, 0xad, 0x01, 0x09, 0x53, 0x58, 0x51, 0x2a, 0x2a, 0x58, 0x53, + 0x2a, 0x71, 0x01, 0x62, 0xe3, 0x38, 0x10, 0x7c, 0x05, 0xe5, 0x62, + 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, 0x01, 0x00, 0x56, 0x00, + 0x00, 0x80, 0x21, 0x10, 0x7c, 0x05, 0xe5, 0x62, 0xd0, 0x00, 0x20, + 0x53, 0x58, 0x47, 0x58, 0x20, 0xa0, 0x03, 0x80, 0x1a, 0x50, 0x00, + 0x08, 0x50, 0x01, 0x08, 0x9e, 0x23, 0x38, 0xfe, 0x77, 0x01, 0x0f, + 0x00, 0x00, 0x52, 0x01, 0x11, 0x0a, 0x52, 0x00, 0x19, 0x04, 0xcf, + 0xd7, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, 0x7c, + 0x05, 0xe5, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x58, 0x47, 0x58, 0x20, + 0xb0, 0x03, 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x01, 0x08, 0x9d, + 0xf1, 0x38, 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, 0x11, + 0xce, 0x52, 0x00, 0x19, 0x00, 0xcf, 0xd7, 0x43, 0x00, 0x08, 0x38, + 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x04, 0x62, 0xd0, 0x00, 0x51, + 0x2a, 0x21, 0xf0, 0x54, 0x00, 0x51, 0x2d, 0x54, 0x01, 0x3d, 0x00, + 0x10, 0xb0, 0x2a, 0x55, 0xa5, 0x00, 0x3c, 0xa9, 0x01, 0xb0, 0x09, + 0x55, 0x93, 0x00, 0x55, 0x94, 0x00, 0x80, 0x0f, 0x62, 0xd0, 0x00, + 0x3c, 0xac, 0x01, 0xa0, 0x07, 0x55, 0x93, 0x00, 0x55, 0x94, 0x00, + 0x56, 0x00, 0x00, 0x62, 0xd0, 0x00, 0x26, 0x2a, 0x0f, 0x81, 0x7d, + 0x3d, 0x00, 0x20, 0xb0, 0x18, 0x62, 0xd0, 0x00, 0x55, 0xa5, 0x01, + 0x55, 0xa6, 0x00, 0x55, 0x93, 0x08, 0x55, 0x94, 0x08, 0x56, 0x00, + 0x00, 0x26, 0x2a, 0x0f, 0x81, 0x61, 0x3d, 0x00, 0x30, 0xb0, 0x0f, + 0x62, 0xd0, 0x00, 0x55, 0xae, 0x00, 0x56, 0x00, 0x00, 0x26, 0x2a, + 0x0f, 0x81, 0x4e, 0x3d, 0x00, 0x40, 0xb0, 0x0f, 0x62, 0xd0, 0x00, + 0x55, 0xae, 0x02, 0x56, 0x00, 0x00, 0x26, 0x2a, 0x0f, 0x81, 0x3b, + 0x3d, 0x00, 0x50, 0xb0, 0xa7, 0x52, 0x01, 0x54, 0x03, 0x56, 0x02, + 0x00, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x01, 0xa0, 0x21, + 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x02, 0xa0, 0x28, 0x3d, + 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x04, 0xa0, 0x36, 0x3d, 0x02, + 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x08, 0xa0, 0x48, 0x80, 0x62, 0x62, + 0xd0, 0x00, 0x55, 0xa8, 0x01, 0x51, 0x2f, 0x29, 0x80, 0x53, 0x2f, + 0x7c, 0x0c, 0x8c, 0x80, 0x51, 0x62, 0xd0, 0x00, 0x51, 0x2b, 0x53, + 0x42, 0x51, 0x2b, 0x53, 0x43, 0x51, 0x2b, 0x53, 0x2e, 0x51, 0x2c, + 0x53, 0x0e, 0x55, 0x0d, 0x00, 0x80, 0x39, 0x62, 0xd0, 0x00, 0x51, + 0x2b, 0x53, 0x2f, 0x3c, 0xa8, 0x00, 0xa0, 0x09, 0x51, 0x2f, 0x29, + 0x80, 0x53, 0x2f, 0x80, 0x25, 0x62, 0xd0, 0x00, 0x26, 0x2f, 0x7f, + 0x80, 0x1d, 0x62, 0xd0, 0x00, 0x55, 0xa8, 0x00, 0x26, 0x2f, 0x7f, + 0x51, 0x9b, 0x53, 0x24, 0x51, 0x24, 0x53, 0x30, 0x51, 0x9c, 0x53, + 0x25, 0x51, 0x25, 0x53, 0x31, 0x7c, 0x0c, 0x8c, 0x56, 0x00, 0x00, + 0x62, 0xd0, 0x00, 0x26, 0x2a, 0x0f, 0x55, 0x2b, 0x06, 0x55, 0x2c, + 0x05, 0x55, 0x2d, 0x00, 0x80, 0x90, 0x3d, 0x00, 0x60, 0xb0, 0x0f, + 0x62, 0xd0, 0x00, 0x55, 0xa9, 0x01, 0x56, 0x00, 0x00, 0x26, 0x2a, + 0x0f, 0x80, 0x7d, 0x3d, 0x00, 0x70, 0xb0, 0x0f, 0x62, 0xd0, 0x00, + 0x55, 0xa9, 0x00, 0x56, 0x00, 0x00, 0x26, 0x2a, 0x0f, 0x80, 0x6a, + 0x3d, 0x00, 0x80, 0xb0, 0x65, 0x56, 0x00, 0x00, 0x62, 0xd0, 0x00, + 0x26, 0x2a, 0x0f, 0x9c, 0x9f, 0x10, 0x7c, 0x08, 0xd8, 0x7c, 0x05, + 0xfb, 0x20, 0x70, 0xfe, 0x71, 0x10, 0x41, 0x00, 0xf7, 0x41, 0x01, + 0xf7, 0x70, 0xcf, 0x62, 0xda, 0x00, 0x71, 0x10, 0x41, 0xdc, 0xfe, + 0x70, 0xcf, 0x43, 0x01, 0x08, 0x43, 0x00, 0x08, 0x50, 0x00, 0x08, + 0x50, 0x1e, 0x08, 0x9c, 0x4b, 0x38, 0xfe, 0x71, 0x01, 0x43, 0xe0, + 0x10, 0x43, 0xff, 0x08, 0x70, 0xfe, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x10, 0x7c, 0x08, 0x07, 0x7c, 0x05, 0xaf, 0x7c, 0x05, + 0xf0, 0x20, 0x93, 0x0b, 0x40, 0x62, 0xe3, 0x38, 0x56, 0x00, 0x00, + 0x62, 0xd0, 0x00, 0x26, 0x2a, 0x0f, 0x38, 0xfc, 0x20, 0x7f, 0x62, + 0xd0, 0x00, 0x3c, 0xa5, 0x00, 0xa0, 0x13, 0x9c, 0x38, 0x62, 0xd0, + 0x00, 0x3c, 0xa6, 0x00, 0xb0, 0x33, 0x55, 0xa6, 0x01, 0x7c, 0x0a, + 0xf9, 0x80, 0x2b, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x93, 0xd0, + 0x08, 0x10, 0x7c, 0x04, 0xaf, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, + 0xab, 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x94, 0xd0, 0x08, + 0x10, 0x7c, 0x04, 0x8c, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0x88, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x03, 0x56, 0x02, 0x00, 0x56, 0x01, + 0x00, 0x56, 0x00, 0x00, 0x80, 0x3e, 0x62, 0xd0, 0x00, 0x91, 0x3a, + 0x40, 0x52, 0xfc, 0x04, 0x57, 0x52, 0xfb, 0x0c, 0x58, 0x51, 0x58, + 0x60, 0xd4, 0x3e, 0x57, 0x53, 0x58, 0x3e, 0x57, 0x53, 0x57, 0x52, + 0x02, 0x12, 0x57, 0x52, 0x01, 0x1a, 0x58, 0xd0, 0x18, 0x91, 0x19, + 0x40, 0x52, 0xfc, 0x04, 0x57, 0x52, 0xfb, 0x0c, 0x58, 0x51, 0x58, + 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x01, 0x3e, 0x57, 0x54, 0x02, 0x77, + 0x00, 0x52, 0x00, 0x3b, 0xfa, 0xcf, 0xbe, 0x62, 0xd0, 0x00, 0x52, + 0x02, 0x53, 0x57, 0x52, 0x01, 0x53, 0x58, 0x38, 0xfd, 0x20, 0x7f, + 0x10, 0x7c, 0x04, 0x3a, 0x20, 0x10, 0x50, 0x04, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x8b, 0x08, 0x7c, 0x04, 0x43, 0x38, 0xfd, 0x20, 0x10, + 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, 0x50, 0x79, 0x08, 0x7c, 0x04, + 0x43, 0x38, 0xfd, 0x20, 0x10, 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, + 0x50, 0x7d, 0x08, 0x7c, 0x04, 0x43, 0x38, 0xfd, 0x20, 0x10, 0x50, + 0x00, 0x7c, 0x03, 0x5e, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x5e, + 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x5e, 0x20, 0x7f, 0x62, 0xd0, + 0x00, 0x55, 0xa8, 0x00, 0x55, 0xa9, 0x01, 0x10, 0x7c, 0x04, 0xaf, + 0x7c, 0x04, 0x8c, 0x20, 0x9b, 0x3e, 0x62, 0xe3, 0x38, 0x71, 0x10, + 0x43, 0x00, 0x08, 0x41, 0x01, 0xf7, 0x70, 0xcf, 0x43, 0x00, 0x08, + 0x62, 0xd0, 0x00, 0x55, 0x2a, 0x08, 0x55, 0x2b, 0x06, 0x55, 0x2c, + 0x05, 0x55, 0x2e, 0x19, 0x55, 0x2f, 0x03, 0x55, 0x30, 0x56, 0x55, + 0x31, 0x45, 0x55, 0x32, 0x00, 0x55, 0x33, 0x00, 0x3c, 0xa8, 0x00, + 0xa0, 0x09, 0x51, 0x2f, 0x29, 0x80, 0x53, 0x2f, 0x80, 0x07, 0x62, + 0xd0, 0x00, 0x26, 0x2f, 0x7f, 0x10, 0x50, 0x00, 0x08, 0x50, 0x2a, + 0x08, 0x50, 0x06, 0x08, 0x50, 0x16, 0x08, 0x7c, 0x06, 0x02, 0x38, + 0xfc, 0x7c, 0x05, 0xaf, 0x7c, 0x05, 0xf0, 0x20, 0x91, 0x9a, 0x40, + 0x10, 0x7c, 0x08, 0x07, 0x7c, 0x07, 0x8d, 0x20, 0x7c, 0x0c, 0x8c, + 0x80, 0x22, 0x62, 0xe3, 0x38, 0x7c, 0x0f, 0x9d, 0x10, 0x7c, 0x07, + 0xcb, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xa0, 0x09, 0x7c, 0x0a, + 0x27, 0x7c, 0x0a, 0x51, 0x80, 0x04, 0x7c, 0x0a, 0xba, 0x9c, 0xb4, + 0x9e, 0x71, 0x8f, 0xde, 0x8f, 0xff, 0x52, 0x00, 0x53, 0x57, 0x55, + 0x58, 0x00, 0x65, 0x57, 0x6b, 0x58, 0x7f, 0x53, 0x55, 0x51, 0x58, + 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x55, 0x53, 0x56, 0x3e, 0x55, 0x53, + 0x55, 0x7f, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, + 0x00, 0x7f, 0x52, 0x00, 0x53, 0x57, 0x55, 0x58, 0x00, 0x55, 0x55, + 0x06, 0x55, 0x56, 0x00, 0x55, 0x52, 0x00, 0x55, 0x51, 0x00, 0x3c, + 0x56, 0x00, 0xb0, 0x06, 0x3c, 0x55, 0x00, 0xa0, 0x1a, 0x70, 0xfb, + 0x6e, 0x56, 0x6e, 0x55, 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x57, + 0x04, 0x52, 0x51, 0x58, 0x0c, 0x51, 0x65, 0x57, 0x6b, 0x58, 0x8f, + 0xde, 0x5f, 0x57, 0x52, 0x5f, 0x58, 0x51, 0x62, 0xd0, 0x00, 0x5a, + 0x55, 0x06, 0x55, 0x01, 0x51, 0x55, 0x04, 0x57, 0x0e, 0x58, 0x03, + 0x7f, 0x55, 0x55, 0x06, 0x55, 0x56, 0x00, 0x55, 0x52, 0x00, 0x55, + 0x51, 0x00, 0x3c, 0x56, 0x00, 0xb0, 0x06, 0x3c, 0x55, 0x00, 0xa0, + 0x1a, 0x70, 0xfb, 0x6e, 0x56, 0x6e, 0x55, 0xd0, 0x0c, 0x62, 0xd0, + 0x00, 0x51, 0x57, 0x04, 0x52, 0x51, 0x58, 0x0c, 0x51, 0x65, 0x57, + 0x6b, 0x58, 0x8f, 0xde, 0x5f, 0x57, 0x52, 0x5f, 0x58, 0x51, 0x62, + 0xd0, 0x00, 0x5a, 0x55, 0x06, 0x55, 0x01, 0x51, 0x55, 0x04, 0x57, + 0x0e, 0x58, 0x03, 0x7f, 0x60, 0xd4, 0x3e, 0x55, 0x53, 0x56, 0x3e, + 0x55, 0x53, 0x55, 0x7f, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x55, 0x53, + 0x56, 0x3e, 0x55, 0x7f, 0x0e, 0x58, 0x00, 0x51, 0x58, 0x60, 0xd4, + 0x3e, 0x57, 0x53, 0x58, 0x7f, 0x51, 0x58, 0x60, 0xd5, 0x51, 0x56, + 0x3f, 0x57, 0x51, 0x55, 0x3f, 0x57, 0x7f, 0x60, 0xd4, 0x3e, 0x57, + 0x53, 0x58, 0x3e, 0x57, 0x53, 0x57, 0x7f, 0x06, 0x57, 0x8b, 0x0e, + 0x58, 0x00, 0x51, 0x58, 0x60, 0xd4, 0x3e, 0x57, 0x53, 0x56, 0x3e, + 0x57, 0x16, 0x57, 0x02, 0x53, 0x55, 0x7f, 0x53, 0x55, 0x51, 0x58, + 0x09, 0x00, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x55, 0x52, 0x15, 0x3f, + 0x55, 0x7f, 0x3e, 0x57, 0x53, 0x57, 0x51, 0x55, 0x12, 0x57, 0x51, + 0x56, 0x1a, 0x58, 0x7f, 0x53, 0x57, 0x52, 0xfb, 0x09, 0x00, 0x60, + 0xd4, 0x3e, 0x57, 0x7f, 0x0e, 0x56, 0x00, 0x51, 0x56, 0x60, 0xd4, + 0x3e, 0x55, 0x53, 0x56, 0x3e, 0x55, 0x7f, 0x0e, 0x58, 0x00, 0x51, + 0x58, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x57, 0x52, 0x15, 0x3f, 0x57, + 0x7f, 0x60, 0xd4, 0x3e, 0x57, 0x54, 0x00, 0x3e, 0x57, 0x54, 0x01, + 0x7f, 0x52, 0x00, 0x53, 0x55, 0x55, 0x56, 0x00, 0x65, 0x55, 0x6b, + 0x56, 0x7f, 0x71, 0x10, 0x41, 0x04, 0xfe, 0x41, 0x05, 0xfe, 0x41, + 0x04, 0xfd, 0x41, 0x05, 0xfd, 0x70, 0xcf, 0x43, 0x04, 0x01, 0x43, + 0x04, 0x02, 0x71, 0x01, 0x7f, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x53, + 0x53, 0x54, 0x3e, 0x53, 0x53, 0x53, 0x7f, 0x53, 0x55, 0x55, 0x56, + 0x00, 0x65, 0x55, 0x6b, 0x56, 0x51, 0x55, 0x7f, 0x0e, 0x56, 0x00, + 0x51, 0x56, 0x60, 0xd5, 0x51, 0x58, 0x3f, 0x55, 0x7f, 0x0e, 0x58, + 0x00, 0x51, 0x58, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x57, 0x7f, 0x3e, + 0x57, 0x12, 0x55, 0x54, 0x02, 0x51, 0x58, 0x1a, 0x56, 0x54, 0x01, + 0x7f, 0x3e, 0x57, 0x12, 0x55, 0x54, 0x04, 0x51, 0x58, 0x1a, 0x56, + 0x54, 0x03, 0x7f, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x49, 0x00, 0x08, + 0x00, 0x59, 0x00, 0x20, 0x00, 0x81, 0x00, 0x0a, 0x00, 0x8f, 0x00, + 0x04, 0x00, 0x93, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, 0x97, 0x00, + 0x04, 0x00, 0x9b, 0x02, 0x56, 0x45, 0x00, 0x9d, 0x00, 0x08, 0x00, + 0xa5, 0x06, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0xab, 0x00, + 0x05, 0x00, 0xb0, 0x01, 0x03, 0xff, 0x00, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_NA.h b/drivers/input/keyboard/cypressbln/touchkey_fw_NA.h new file mode 100644 index 0000000..2667cf6 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_NA.h @@ -0,0 +1,747 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x00, 0x68, + 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x05, 0x47, 0x7e, 0x7e, 0x30, + 0x30, 0x30, 0x7d, 0x06, 0x93, 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, + 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x40, 0x71, 0x10, 0x62, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe3, 0x38, 0x50, 0x80, 0x4e, 0x62, 0xe3, 0x38, 0x5d, + 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, 0xfa, 0x01, 0x40, 0x4f, 0x5b, + 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, 0x50, 0x06, 0x00, 0x40, + 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, 0x70, 0xef, 0x18, 0x60, + 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, 0x71, 0x10, 0x62, 0xe0, + 0x02, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x71, 0x10, 0x41, 0xe1, 0xfe, + 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, 0x03, 0x50, 0x00, 0x4e, + 0x62, 0xd3, 0x03, 0x62, 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, + 0x00, 0x71, 0xc0, 0x7c, 0x03, 0x01, 0x62, 0xd0, 0x00, 0x50, 0x02, + 0x57, 0xff, 0x08, 0x28, 0x53, 0x79, 0x18, 0x75, 0x09, 0x00, 0x28, + 0x4b, 0x51, 0x79, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, + 0x08, 0x28, 0x60, 0xd5, 0x74, 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, + 0x08, 0x28, 0x53, 0x79, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xa0, + 0x1c, 0x53, 0x78, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x3f, 0x79, + 0x47, 0x79, 0xff, 0xb0, 0x06, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x18, + 0x7a, 0x78, 0xbf, 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, 0x00, 0x08, + 0x28, 0x53, 0x78, 0x50, 0x00, 0x3f, 0x79, 0x47, 0x79, 0xff, 0xb0, + 0x08, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x78, 0xbf, + 0xef, 0x18, 0x8f, 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, + 0x10, 0x62, 0xe0, 0x03, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x0a, + 0x86, 0x8f, 0xff, 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x00, 0xfd, 0x00, 0xcd, 0x00, + 0xce, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0xa1, 0x80, 0xa2, + 0xc0, 0xa3, 0x0c, 0xa8, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x7c, 0x33, + 0x7a, 0x00, 0x7b, 0x00, 0x79, 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, 0xca, 0x20, 0xd6, 0x44, 0xcf, 0x00, 0xcb, 0x00, + 0xc8, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xd7, 0x00, 0xa9, 0x00, 0x2b, + 0x00, 0xb0, 0x00, 0xb3, 0x02, 0xb6, 0x00, 0xb2, 0x00, 0xb5, 0x00, + 0xb8, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0x33, 0x00, 0x34, + 0x00, 0x35, 0x00, 0xff, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0xdc, 0x00, + 0xe2, 0x00, 0xdd, 0x00, 0xd8, 0x02, 0xd9, 0xb4, 0xda, 0x00, 0xdb, + 0x00, 0xdf, 0x00, 0x29, 0x00, 0x30, 0x00, 0xbd, 0x00, 0xff, 0x70, + 0xef, 0x62, 0x00, 0x08, 0x71, 0x10, 0x62, 0x00, 0x98, 0x62, 0x01, + 0x02, 0x70, 0xef, 0x62, 0x04, 0x03, 0x71, 0x10, 0x62, 0x04, 0x00, + 0x62, 0x05, 0xbc, 0x70, 0xef, 0x62, 0x08, 0x00, 0x71, 0x10, 0x62, + 0x08, 0x28, 0x62, 0x09, 0x00, 0x70, 0xef, 0x62, 0x0c, 0x00, 0x71, + 0x10, 0x62, 0x0c, 0x00, 0x62, 0x0d, 0x00, 0x70, 0xef, 0x62, 0x10, + 0x00, 0x71, 0x10, 0x62, 0x10, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, + 0x62, 0x01, 0x00, 0x62, 0x05, 0x00, 0x62, 0x09, 0x00, 0x62, 0x0d, + 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, 0x7f, 0x1d, 0x3e, 0x55, 0x02, + 0x08, 0x55, 0x03, 0x03, 0x55, 0x04, 0x00, 0x7c, 0x03, 0x11, 0x7c, + 0x02, 0xaa, 0x7f, 0x10, 0x70, 0xef, 0x50, 0x00, 0x67, 0x50, 0x02, + 0x57, 0x00, 0x7c, 0x03, 0x2c, 0x50, 0x01, 0x67, 0x50, 0x02, 0x57, + 0x83, 0x7c, 0x03, 0x2c, 0x70, 0xef, 0x20, 0x7f, 0x38, 0x02, 0x10, + 0x08, 0x4f, 0x56, 0xfc, 0x00, 0xd0, 0x04, 0x56, 0xfc, 0x01, 0x18, + 0x20, 0x70, 0xef, 0x62, 0xe3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xff, + 0xa0, 0x1f, 0x4f, 0x48, 0xfc, 0x01, 0xa0, 0x03, 0x71, 0x10, 0x54, + 0xfd, 0x18, 0x20, 0x75, 0x09, 0x00, 0x10, 0x08, 0x28, 0x4f, 0x59, + 0xfd, 0x61, 0x00, 0x18, 0x20, 0x75, 0x09, 0x00, 0x8f, 0xd7, 0x38, + 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + 0x62, 0xd0, 0x00, 0x2e, 0x02, 0x08, 0x51, 0x02, 0x60, 0x00, 0x71, + 0x10, 0x41, 0x01, 0xf7, 0x43, 0x00, 0x08, 0x70, 0xef, 0x7f, 0x62, + 0xd0, 0x00, 0x53, 0x00, 0x71, 0x10, 0x5d, 0xe0, 0x08, 0x21, 0xf8, + 0x29, 0x00, 0x70, 0xfe, 0x60, 0xe0, 0x70, 0xef, 0x4b, 0x4b, 0x4b, + 0x4b, 0x51, 0x02, 0x21, 0xf7, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, + 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, + 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, + 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, + 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, + 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, + 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, + 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x47, 0x00, + 0x00, 0x49, 0x01, 0x00, 0x29, 0x08, 0x60, 0x00, 0x57, 0x01, 0x79, + 0xbf, 0xfe, 0x18, 0x71, 0x10, 0x60, 0xe0, 0x70, 0xef, 0x71, 0x01, + 0x7f, 0x08, 0x67, 0x67, 0x67, 0x67, 0x21, 0x0f, 0xff, 0x2b, 0x9f, + 0x4e, 0x18, 0x21, 0x0f, 0xff, 0x24, 0x9f, 0x47, 0x7f, 0x08, 0x10, + 0x28, 0xa0, 0x0b, 0x9f, 0x3f, 0x20, 0x18, 0x75, 0xdf, 0xf5, 0x74, + 0x8f, 0xf2, 0x38, 0xfe, 0x7f, 0x52, 0x00, 0xa0, 0x08, 0x10, 0x9f, + 0x2d, 0x20, 0x75, 0x8f, 0xf6, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, + 0x0d, 0x9f, 0x20, 0x50, 0x0a, 0x9f, 0x1c, 0x7f, 0x70, 0xbf, 0x62, + 0xd3, 0x03, 0x4f, 0x52, 0xfb, 0xa0, 0x15, 0x7b, 0xfb, 0x52, 0xfc, + 0x59, 0xfd, 0x60, 0xd3, 0x52, 0x00, 0x9f, 0x05, 0x4f, 0x62, 0xd3, + 0x03, 0x77, 0xfd, 0x8f, 0xe9, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x3d, + 0xfa, 0x00, 0xb0, 0x06, 0x3d, 0xfb, 0x00, 0xa0, 0x18, 0x10, 0x52, + 0xfc, 0x59, 0xfd, 0x28, 0x9e, 0xe6, 0x20, 0x07, 0xfd, 0x01, 0x0f, + 0xfc, 0x00, 0x17, 0xfb, 0x01, 0x1f, 0xfa, 0x00, 0x8f, 0xe0, 0x7f, + 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, + 0xa0, 0x06, 0x26, 0x04, 0xdf, 0x80, 0x04, 0x2e, 0x04, 0x20, 0x51, + 0x04, 0x60, 0x08, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, + 0x29, 0x00, 0xa0, 0x06, 0x26, 0x04, 0xf7, 0x80, 0x04, 0x2e, 0x04, + 0x08, 0x51, 0x04, 0x60, 0x08, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, + 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x02, 0xef, 0x80, 0x04, + 0x2e, 0x02, 0x10, 0x51, 0x02, 0x60, 0x00, 0x70, 0x3f, 0x71, 0xc0, + 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, + 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x02, 0x7f, + 0x80, 0x04, 0x2e, 0x02, 0x80, 0x51, 0x02, 0x60, 0x00, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x10, 0x70, + 0x3f, 0x71, 0x80, 0x5d, 0xd3, 0x08, 0x5d, 0xd0, 0x08, 0x62, 0xd0, + 0x00, 0x51, 0x08, 0x60, 0xd3, 0x2e, 0x05, 0x80, 0x49, 0xd7, 0x08, + 0xa0, 0x09, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x00, 0x80, 0x08, 0x49, + 0xd7, 0x20, 0xa0, 0x03, 0x80, 0xac, 0x51, 0x05, 0x21, 0x0e, 0xe0, + 0x01, 0x80, 0x11, 0x80, 0x6d, 0x80, 0x7f, 0x80, 0x4d, 0x80, 0x9c, + 0x80, 0x9a, 0x80, 0x98, 0x80, 0x96, 0x80, 0x9d, 0x5d, 0xd8, 0x21, + 0xfe, 0x39, 0x40, 0xa0, 0x06, 0x62, 0xd7, 0x00, 0x80, 0x90, 0x49, + 0xd8, 0x01, 0xb0, 0x15, 0x55, 0x0c, 0x02, 0x26, 0x07, 0x00, 0x26, + 0x06, 0x00, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x62, 0xd7, 0x10, + 0x80, 0x77, 0x55, 0x0c, 0x01, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x06, + 0x5f, 0x07, 0x06, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, 0x60, + 0xd8, 0x76, 0x07, 0x62, 0xd7, 0x14, 0x80, 0x5b, 0x51, 0x0a, 0x78, + 0x3a, 0x07, 0xc0, 0x0f, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, + 0x60, 0xd8, 0x76, 0x07, 0x2e, 0x05, 0x20, 0x60, 0xd8, 0x62, 0xd7, + 0x04, 0x80, 0x3f, 0x5d, 0xd8, 0x3a, 0x0a, 0xd0, 0x2b, 0xa0, 0x29, + 0x53, 0x07, 0x53, 0x06, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x80, + 0x18, 0x51, 0x0b, 0x78, 0x3a, 0x07, 0xc0, 0x16, 0x51, 0x09, 0x02, + 0x07, 0x5c, 0x5d, 0xd8, 0x54, 0x00, 0x2e, 0x05, 0x10, 0x76, 0x07, + 0x80, 0x01, 0x62, 0xd7, 0x10, 0x80, 0x0f, 0x62, 0xd7, 0x00, 0x80, + 0x0a, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x00, 0x55, 0x0c, 0x00, 0x18, + 0x60, 0xd0, 0x18, 0x60, 0xd3, 0x20, 0x18, 0x7e, 0x62, 0xd0, 0x00, + 0x71, 0x10, 0x41, 0x04, 0xfc, 0x43, 0x05, 0x03, 0x70, 0xef, 0x26, + 0x03, 0xfc, 0x51, 0x03, 0x60, 0x04, 0x55, 0x0c, 0x00, 0x90, 0x28, + 0x90, 0x2d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x50, 0x00, 0x53, 0x06, + 0x71, 0x10, 0x43, 0x04, 0x03, 0x43, 0x05, 0x03, 0x70, 0xef, 0x2e, + 0x03, 0x03, 0x51, 0x03, 0x60, 0x04, 0x7f, 0x62, 0xd0, 0x00, 0x51, + 0x05, 0x21, 0xb0, 0x26, 0x05, 0x4f, 0x7f, 0x41, 0xe0, 0x7f, 0x43, + 0xe0, 0x80, 0x7f, 0x43, 0xd6, 0x31, 0x7f, 0x62, 0xd0, 0x00, 0x4f, + 0x52, 0xfd, 0x53, 0x0a, 0x52, 0xfc, 0x53, 0x0b, 0x52, 0xfb, 0x53, + 0x09, 0x52, 0xfa, 0x53, 0x08, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, + 0x5d, 0xa4, 0x04, 0x1b, 0x5d, 0xa5, 0x0c, 0x1a, 0x55, 0x1c, 0x01, + 0x18, 0x7e, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x53, 0x1e, + 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x7b, 0x62, 0xd3, 0x00, 0x13, + 0x60, 0x62, 0xd3, 0x00, 0x54, 0x68, 0x62, 0xd3, 0x00, 0x52, 0x7a, + 0x62, 0xd3, 0x00, 0x1b, 0x5f, 0x62, 0xd3, 0x00, 0x54, 0x67, 0x48, + 0x67, 0x80, 0xb0, 0x33, 0x3d, 0x67, 0x00, 0xb0, 0x7b, 0x51, 0x0d, + 0x3b, 0x68, 0xc0, 0x75, 0x52, 0x68, 0x58, 0x1e, 0x01, 0x00, 0x6d, + 0x62, 0xd3, 0x00, 0x05, 0x4e, 0xc0, 0x09, 0x51, 0x0f, 0x3b, 0x4e, + 0xd0, 0x12, 0xa0, 0x10, 0x56, 0x4e, 0x00, 0x5b, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x07, 0x60, 0x01, 0x0f, 0x5f, 0x00, 0x80, 0x41, 0x3d, + 0x67, 0xff, 0xb0, 0x09, 0x50, 0xff, 0x12, 0x0e, 0x3b, 0x68, 0xc0, + 0x20, 0x62, 0xd3, 0x00, 0x56, 0x68, 0x00, 0x56, 0x67, 0x00, 0x5b, + 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x5b, 0x78, 0xd0, 0x03, 0x50, + 0x00, 0x54, 0x5b, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, 0x62, + 0xd3, 0x00, 0x52, 0x7b, 0x62, 0xd3, 0x00, 0x54, 0x60, 0x62, 0xd3, + 0x00, 0x52, 0x7a, 0x62, 0xd3, 0x00, 0x54, 0x5f, 0x51, 0x1e, 0x64, + 0x5c, 0x62, 0xd3, 0x00, 0x56, 0x68, 0x00, 0x56, 0x67, 0x00, 0x5b, + 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x12, 0x54, 0x5b, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x08, + 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x52, 0x53, 0x19, 0x55, 0x18, 0x00, + 0x18, 0x08, 0x90, 0x7e, 0x62, 0xd3, 0x00, 0x23, 0x56, 0xb0, 0x2c, + 0x51, 0x10, 0x04, 0x19, 0x0e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x52, 0x68, 0x12, 0x19, 0x52, 0x67, 0x1a, 0x18, 0xc0, + 0x39, 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x57, 0x78, 0x54, + 0x57, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x3e, 0x80, 0x18, 0x51, + 0x10, 0x14, 0x19, 0x1e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, + 0x00, 0x52, 0x68, 0x12, 0x19, 0x52, 0x67, 0x1a, 0x18, 0xc0, 0x0e, + 0x5b, 0x67, 0x90, 0x31, 0x62, 0xd3, 0x00, 0x2d, 0x56, 0x50, 0x01, + 0x80, 0x24, 0x5b, 0x67, 0x08, 0x90, 0x23, 0x73, 0x62, 0xd3, 0x00, + 0x25, 0x56, 0x62, 0xd3, 0x00, 0x20, 0x51, 0x11, 0x54, 0x57, 0x50, + 0x00, 0x80, 0x0d, 0x5b, 0x67, 0x90, 0x0d, 0x73, 0x62, 0xd3, 0x00, + 0x25, 0x56, 0x50, 0x00, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x67, + 0x67, 0x67, 0x5c, 0x18, 0x21, 0x07, 0xf0, 0x01, 0x7f, 0x01, 0x02, + 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x70, 0xbf, 0x70, 0xbf, 0x62, + 0xd3, 0x00, 0x50, 0x04, 0x78, 0x08, 0x5c, 0x56, 0x52, 0x32, 0x18, + 0x78, 0xdf, 0xf8, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x91, 0x99, + 0x70, 0xbf, 0x18, 0x08, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x7b, + 0x62, 0xd3, 0x00, 0x54, 0x60, 0x62, 0xd3, 0x00, 0x52, 0x7a, 0x62, + 0xd3, 0x00, 0x54, 0x5f, 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x14, 0x00, 0x50, 0x04, 0x78, + 0x08, 0x9f, 0x0e, 0x39, 0x01, 0xb0, 0x04, 0x55, 0x14, 0x01, 0x18, + 0x78, 0xdf, 0xf3, 0x51, 0x14, 0x7f, 0x50, 0x04, 0x78, 0x08, 0x9e, + 0x3e, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x98, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0xd8, 0xd9, 0xda, 0xdb, 0xdf, 0x00, 0x01, + 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x70, 0xbf, 0x62, 0xd0, + 0x00, 0x62, 0xd3, 0x00, 0x57, 0x00, 0x56, 0x56, 0x00, 0x79, 0xdf, + 0xfb, 0x62, 0xd3, 0x00, 0x57, 0x03, 0x50, 0x02, 0x54, 0x57, 0x79, + 0xdf, 0xfc, 0x62, 0xd3, 0x00, 0x50, 0x0a, 0x57, 0x03, 0x54, 0x5b, + 0x79, 0xdf, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x55, 0x0d, 0x28, 0x55, + 0x0e, 0x05, 0x55, 0x0f, 0x0f, 0x55, 0x10, 0x01, 0x55, 0x11, 0x02, + 0x55, 0x12, 0x0a, 0x55, 0x22, 0x05, 0x55, 0x1f, 0x64, 0x43, 0x61, + 0x0d, 0x57, 0x00, 0x50, 0x02, 0x90, 0x95, 0x50, 0x05, 0xff, 0x98, + 0x29, 0x00, 0x60, 0xa9, 0x62, 0xa0, 0x08, 0x43, 0xa2, 0x04, 0x62, + 0xa3, 0x70, 0x43, 0x7a, 0x01, 0x43, 0xaa, 0x02, 0x43, 0xdf, 0x01, + 0x50, 0x01, 0x57, 0x09, 0x90, 0x20, 0x90, 0x55, 0x57, 0x01, 0x50, + 0xb3, 0x91, 0x44, 0x50, 0x01, 0x57, 0x0e, 0x90, 0x12, 0x90, 0x47, + 0x7f, 0x53, 0x22, 0xff, 0x67, 0x29, 0x00, 0x60, 0xa9, 0x51, 0x21, + 0x58, 0x20, 0x90, 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, 0x03, 0x53, + 0x21, 0x64, 0x64, 0x64, 0x64, 0x64, 0x29, 0x80, 0x60, 0xa1, 0x5b, + 0x78, 0x21, 0x0f, 0x29, 0x08, 0x74, 0x53, 0x20, 0x12, 0x22, 0x02, + 0x21, 0x5c, 0x50, 0x00, 0x53, 0x1d, 0x53, 0x23, 0x29, 0x01, 0x79, + 0xa0, 0x08, 0x64, 0x6b, 0x1d, 0x6b, 0x23, 0x8f, 0xf5, 0x60, 0xb5, + 0x51, 0x1d, 0x60, 0xb4, 0x7f, 0x50, 0x04, 0x78, 0x08, 0x90, 0x0f, + 0x90, 0x41, 0x18, 0x78, 0xdf, 0xf8, 0x7f, 0x01, 0x04, 0x01, 0x10, + 0x01, 0x20, 0x01, 0x80, 0x64, 0x5c, 0xff, 0xf4, 0x4b, 0x74, 0xff, + 0xf0, 0x7f, 0x62, 0xd0, 0x00, 0x53, 0x1d, 0x10, 0x5b, 0x64, 0x64, + 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x2a, 0x1d, 0x61, 0x01, 0x36, 0x1d, + 0xff, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x36, 0x1d, 0xff, 0x18, + 0xfe, 0xef, 0x5c, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, + 0x7f, 0x62, 0xd0, 0x00, 0x10, 0x73, 0x53, 0x1d, 0x71, 0x10, 0x5b, + 0xfe, 0xd9, 0x5c, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x70, 0xef, + 0x18, 0x64, 0x64, 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x22, 0x1d, 0x61, + 0x01, 0x36, 0x1d, 0xff, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, + 0xef, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, 0x1e, 0x50, 0x00, + 0x53, 0x1a, 0x53, 0x1b, 0x51, 0x1e, 0x5c, 0x62, 0xd3, 0x00, 0x52, + 0x24, 0x53, 0x1f, 0x43, 0xa0, 0x01, 0x51, 0x1f, 0x60, 0xfd, 0x41, + 0xa3, 0xdf, 0x51, 0x1e, 0x9f, 0x7a, 0x9f, 0x81, 0x58, 0x23, 0x55, + 0x1c, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x43, 0xb3, 0x01, + 0x51, 0x1c, 0xaf, 0xfd, 0x79, 0xdf, 0xee, 0x51, 0x1e, 0x9f, 0x5f, + 0x9f, 0x91, 0x43, 0xa3, 0x20, 0x41, 0xa0, 0xfe, 0x62, 0xfd, 0x00, + 0x50, 0xff, 0x4c, 0x1b, 0x14, 0x1b, 0x51, 0x20, 0x11, 0x08, 0xfe, + 0x66, 0x4c, 0x1a, 0x1c, 0x1a, 0xd0, 0x07, 0x55, 0x1a, 0x00, 0x55, + 0x1b, 0x00, 0x51, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x1b, + 0x54, 0x7b, 0x51, 0x1a, 0x54, 0x7a, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x08, 0x9f, 0x86, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x70, 0xbf, 0x62, + 0xd0, 0x00, 0x53, 0x29, 0x5a, 0x28, 0x55, 0x1e, 0x03, 0x62, 0xd3, + 0x00, 0x58, 0x1e, 0x56, 0x24, 0x80, 0x55, 0x2b, 0x08, 0x55, 0x2a, + 0x80, 0x51, 0x1e, 0x9f, 0x63, 0x51, 0x1e, 0x9f, 0x5f, 0x70, 0xbf, + 0x58, 0x1e, 0x62, 0xd3, 0x00, 0x51, 0x1b, 0x3a, 0x29, 0x51, 0x1a, + 0x1a, 0x28, 0xd0, 0x06, 0x51, 0x2a, 0x73, 0x25, 0x24, 0x68, 0x2a, + 0x26, 0x2a, 0x7f, 0x51, 0x2a, 0x2d, 0x24, 0x7a, 0x2b, 0xbf, 0xd6, + 0x7a, 0x1e, 0xdf, 0xc4, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x10, 0x4f, + 0x38, 0x02, 0x62, 0xd0, 0x00, 0x55, 0xb9, 0x00, 0x10, 0x7c, 0x05, + 0x28, 0x7c, 0x05, 0x05, 0x7c, 0x04, 0xe2, 0x7c, 0x04, 0xbf, 0x20, + 0x7c, 0x16, 0x44, 0x10, 0x7c, 0x03, 0x7c, 0x20, 0x43, 0x00, 0x08, + 0x62, 0xd0, 0x00, 0x55, 0x2c, 0x08, 0x55, 0x2d, 0x0e, 0x55, 0x2e, + 0x08, 0x55, 0x30, 0x32, 0x55, 0x31, 0x04, 0x10, 0x50, 0x00, 0x08, + 0x50, 0x2c, 0x08, 0x50, 0x05, 0x08, 0x50, 0x22, 0x08, 0x7c, 0x06, + 0x7a, 0x38, 0xfc, 0x7c, 0x06, 0x2e, 0x7c, 0x06, 0x6f, 0x20, 0x71, + 0x10, 0x41, 0x04, 0xfc, 0x41, 0x05, 0xfc, 0x71, 0x01, 0x10, 0x70, + 0xcf, 0x7c, 0x08, 0x7f, 0x20, 0x62, 0xd0, 0x00, 0x55, 0xb4, 0x00, + 0x93, 0x17, 0x10, 0x7c, 0x08, 0x05, 0x20, 0x80, 0xc7, 0x7c, 0x17, + 0xfe, 0x95, 0xa1, 0x62, 0xd0, 0x00, 0x3c, 0xbd, 0x01, 0xb0, 0x09, + 0x50, 0x00, 0x08, 0x7c, 0x1a, 0xb0, 0x38, 0xff, 0x10, 0x7c, 0x08, + 0x43, 0x20, 0x39, 0x00, 0xa0, 0x7c, 0x62, 0xd0, 0x00, 0x3c, 0xb1, + 0x00, 0xb0, 0x74, 0x51, 0xaf, 0x11, 0x4a, 0x51, 0xae, 0x19, 0x01, + 0xd0, 0x12, 0x7c, 0x16, 0x62, 0x39, 0xe1, 0xa0, 0x16, 0x62, 0xd0, + 0x00, 0x76, 0xaf, 0x0e, 0xae, 0x00, 0x80, 0x0c, 0x62, 0xd0, 0x00, + 0x55, 0xaf, 0x00, 0x55, 0xae, 0x00, 0x90, 0x81, 0x62, 0xd0, 0x00, + 0x3c, 0xbb, 0xf0, 0xd0, 0x03, 0x76, 0xbb, 0x62, 0xd0, 0x00, 0x51, + 0x31, 0x21, 0x0f, 0x53, 0x79, 0x51, 0xbb, 0x3a, 0x79, 0xb0, 0x5f, + 0x7c, 0x16, 0x62, 0x62, 0xd0, 0x00, 0x53, 0xbc, 0x3c, 0xbc, 0xe1, + 0xa0, 0x20, 0x3c, 0xb5, 0x01, 0xb0, 0x04, 0x55, 0xb0, 0x01, 0x62, + 0xd0, 0x00, 0x55, 0xb5, 0x00, 0x55, 0xb8, 0x01, 0x7c, 0x16, 0xf7, + 0x62, 0xd0, 0x00, 0x55, 0xb2, 0x01, 0x55, 0xb3, 0x03, 0x80, 0x33, + 0x62, 0xd0, 0x00, 0x55, 0xbb, 0x00, 0x80, 0x2b, 0x62, 0xd0, 0x00, + 0x55, 0xaf, 0x00, 0x55, 0xae, 0x00, 0x55, 0xb0, 0x00, 0x55, 0xb7, + 0x00, 0x55, 0xb8, 0x00, 0x3c, 0xb2, 0x01, 0xb0, 0x14, 0x7a, 0xb3, + 0x3c, 0xb3, 0x00, 0xb0, 0x0d, 0x7c, 0x17, 0x7f, 0x62, 0xd0, 0x00, + 0x55, 0xb2, 0x00, 0x55, 0xbb, 0x00, 0x7c, 0x18, 0xe4, 0x8f, 0x39, + 0x38, 0xfe, 0x20, 0x8f, 0xff, 0x10, 0x4f, 0x38, 0x24, 0x62, 0xd0, + 0x00, 0x3c, 0xb9, 0x00, 0xb0, 0x05, 0x51, 0xaa, 0x53, 0x24, 0x56, + 0x19, 0x00, 0x81, 0x5d, 0x56, 0x00, 0x00, 0x81, 0x51, 0x62, 0xd0, + 0x00, 0x3c, 0xb9, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x78, 0x55, + 0x79, 0x00, 0x06, 0x78, 0xaa, 0x7c, 0x1c, 0x33, 0x52, 0x00, 0x53, + 0x76, 0x55, 0x77, 0x00, 0x06, 0x76, 0x24, 0x7c, 0x1c, 0xc9, 0x10, + 0x52, 0x00, 0x7c, 0x09, 0xb9, 0x20, 0x10, 0x7c, 0x06, 0x64, 0x20, + 0x39, 0x00, 0xbf, 0xf1, 0x62, 0xd0, 0x00, 0x3c, 0xb9, 0x01, 0xb0, + 0xcf, 0x52, 0x00, 0x54, 0x23, 0x56, 0x22, 0x00, 0x3d, 0x22, 0x00, + 0xb0, 0x06, 0x3d, 0x23, 0x00, 0xa0, 0x21, 0x3d, 0x22, 0x00, 0xb0, + 0x06, 0x3d, 0x23, 0x01, 0xa0, 0x38, 0x3d, 0x22, 0x00, 0xb0, 0x06, + 0x3d, 0x23, 0x02, 0xa0, 0x66, 0x3d, 0x22, 0x00, 0xb0, 0x06, 0x3d, + 0x23, 0x03, 0xa0, 0x89, 0x80, 0x9e, 0x62, 0xd0, 0x00, 0x51, 0x7a, + 0x08, 0x51, 0x7b, 0x08, 0x50, 0x00, 0x08, 0x50, 0x01, 0x08, 0x7c, + 0x1b, 0xd0, 0x38, 0xfc, 0x51, 0x71, 0x62, 0xd0, 0x00, 0x53, 0x7b, + 0x51, 0x70, 0x53, 0x7a, 0x80, 0x7d, 0x62, 0xd0, 0x00, 0x51, 0x7c, + 0x08, 0x51, 0x7d, 0x08, 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x7c, + 0x1b, 0xd0, 0x38, 0xfc, 0x51, 0x71, 0x53, 0x78, 0x51, 0x70, 0x53, + 0x79, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x79, 0x08, 0x51, + 0x78, 0x08, 0x7c, 0x1b, 0x8c, 0x18, 0x62, 0xd0, 0x00, 0x53, 0x7d, + 0x18, 0x53, 0x7c, 0x38, 0xfe, 0x80, 0x45, 0x62, 0xd0, 0x00, 0x51, + 0x7f, 0x08, 0x51, 0x7e, 0x53, 0x79, 0x18, 0x53, 0x78, 0x65, 0x78, + 0x6b, 0x79, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x79, 0x08, + 0x51, 0x78, 0x08, 0x7c, 0x1b, 0x8c, 0x18, 0x62, 0xd0, 0x00, 0x53, + 0x7f, 0x18, 0x53, 0x7e, 0x38, 0xfe, 0x80, 0x18, 0x62, 0xd0, 0x00, + 0x51, 0x80, 0x08, 0x51, 0x81, 0x08, 0x50, 0x00, 0x08, 0x50, 0x01, + 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, 0x86, 0x62, 0xd0, + 0x00, 0x55, 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, 0x00, + 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, + 0x50, 0x06, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, 0x3f, + 0x53, 0x79, 0x52, 0x19, 0x53, 0x76, 0x55, 0x77, 0x00, 0x65, 0x76, + 0x6b, 0x77, 0x7c, 0x1c, 0x6a, 0x53, 0x79, 0x7c, 0x1c, 0xbd, 0x06, + 0x76, 0x7a, 0x0e, 0x77, 0x00, 0x51, 0x77, 0x7c, 0x1c, 0x28, 0x7c, + 0x1c, 0x52, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xce, 0xac, 0x77, 0x19, + 0x3d, 0x19, 0x03, 0xce, 0xa0, 0x56, 0x00, 0x00, 0x80, 0xc0, 0x62, + 0xd0, 0x00, 0x55, 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, + 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, + 0x3f, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1a, 0x3e, 0x78, 0x54, 0x1b, + 0x5a, 0x78, 0x06, 0x78, 0x03, 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, + 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, + 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, 0x3f, 0x60, 0xd4, 0x3e, 0x78, + 0x54, 0x1c, 0x3e, 0x78, 0x54, 0x1d, 0x5a, 0x78, 0x06, 0x78, 0x05, + 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, + 0x1c, 0x3f, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1e, 0x3e, 0x78, 0x54, + 0x1f, 0x50, 0x03, 0x08, 0x5a, 0x78, 0x06, 0x78, 0x1a, 0x08, 0x51, + 0x78, 0x08, 0x7c, 0x19, 0xe5, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, + 0x78, 0x54, 0x21, 0x51, 0x79, 0x54, 0x20, 0x7c, 0x1c, 0x00, 0x7c, + 0x1c, 0x5f, 0x7c, 0x1c, 0xa2, 0x06, 0x78, 0x5f, 0x7c, 0x1c, 0xad, + 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x82, 0x7c, 0x1c, 0x75, 0x51, + 0x78, 0x01, 0x8a, 0x7c, 0x1c, 0x75, 0x06, 0x78, 0x92, 0x7c, 0x1c, + 0xad, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xcf, 0x3d, 0x38, 0xdc, 0x20, + 0x7f, 0x10, 0x4f, 0x38, 0x24, 0x10, 0x57, 0x30, 0x50, 0x00, 0x7c, + 0x0a, 0x38, 0x20, 0x62, 0xd0, 0x00, 0x3c, 0xb9, 0x01, 0xb0, 0x13, + 0x51, 0x24, 0x53, 0x32, 0x51, 0x25, 0x53, 0x33, 0x51, 0x26, 0x53, + 0x34, 0x51, 0x27, 0x53, 0x35, 0x80, 0x14, 0x62, 0xd0, 0x00, 0x51, + 0xaa, 0x53, 0x24, 0x51, 0xab, 0x53, 0x25, 0x51, 0xac, 0x53, 0x26, + 0x51, 0xad, 0x53, 0x27, 0x10, 0x50, 0x00, 0x7c, 0x09, 0xb9, 0x20, + 0x56, 0x19, 0x00, 0x81, 0x5d, 0x56, 0x00, 0x00, 0x81, 0x51, 0x62, + 0xd0, 0x00, 0x3c, 0xb9, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x78, + 0x55, 0x79, 0x00, 0x06, 0x78, 0xaa, 0x7c, 0x1c, 0x33, 0x52, 0x00, + 0x53, 0x76, 0x55, 0x77, 0x00, 0x06, 0x76, 0x24, 0x7c, 0x1c, 0xc9, + 0x10, 0x52, 0x00, 0x7c, 0x09, 0xb9, 0x20, 0x10, 0x7c, 0x06, 0x64, + 0x20, 0x39, 0x00, 0xbf, 0xf1, 0x62, 0xd0, 0x00, 0x3c, 0xb9, 0x01, + 0xb0, 0xcf, 0x52, 0x00, 0x54, 0x23, 0x56, 0x22, 0x00, 0x3d, 0x22, + 0x00, 0xb0, 0x06, 0x3d, 0x23, 0x00, 0xa0, 0x21, 0x3d, 0x22, 0x00, + 0xb0, 0x06, 0x3d, 0x23, 0x01, 0xa0, 0x38, 0x3d, 0x22, 0x00, 0xb0, + 0x06, 0x3d, 0x23, 0x02, 0xa0, 0x66, 0x3d, 0x22, 0x00, 0xb0, 0x06, + 0x3d, 0x23, 0x03, 0xa0, 0x89, 0x80, 0x9e, 0x62, 0xd0, 0x00, 0x51, + 0x7a, 0x08, 0x51, 0x7b, 0x08, 0x50, 0x00, 0x08, 0x50, 0x01, 0x08, + 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x51, 0x71, 0x62, 0xd0, 0x00, 0x53, + 0x7b, 0x51, 0x70, 0x53, 0x7a, 0x80, 0x7d, 0x62, 0xd0, 0x00, 0x51, + 0x7c, 0x08, 0x51, 0x7d, 0x08, 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, + 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x51, 0x71, 0x53, 0x78, 0x51, 0x70, + 0x53, 0x79, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x79, 0x08, + 0x51, 0x78, 0x08, 0x7c, 0x1b, 0x8c, 0x18, 0x62, 0xd0, 0x00, 0x53, + 0x7d, 0x18, 0x53, 0x7c, 0x38, 0xfe, 0x80, 0x45, 0x62, 0xd0, 0x00, + 0x51, 0x7f, 0x08, 0x51, 0x7e, 0x53, 0x79, 0x18, 0x53, 0x78, 0x65, + 0x78, 0x6b, 0x79, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x79, + 0x08, 0x51, 0x78, 0x08, 0x7c, 0x1b, 0x8c, 0x18, 0x62, 0xd0, 0x00, + 0x53, 0x7f, 0x18, 0x53, 0x7e, 0x38, 0xfe, 0x80, 0x18, 0x62, 0xd0, + 0x00, 0x51, 0x80, 0x08, 0x51, 0x81, 0x08, 0x50, 0x00, 0x08, 0x50, + 0x01, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, 0x86, 0x62, + 0xd0, 0x00, 0x55, 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, + 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, + 0x3f, 0x53, 0x79, 0x52, 0x19, 0x53, 0x76, 0x55, 0x77, 0x00, 0x65, + 0x76, 0x6b, 0x77, 0x7c, 0x1c, 0x6a, 0x53, 0x79, 0x7c, 0x1c, 0xbd, + 0x06, 0x76, 0x7a, 0x0e, 0x77, 0x00, 0x51, 0x77, 0x7c, 0x1c, 0x28, + 0x7c, 0x1c, 0x52, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xce, 0xac, 0x77, + 0x19, 0x3d, 0x19, 0x03, 0xce, 0xa0, 0x56, 0x00, 0x00, 0x80, 0xc0, + 0x62, 0xd0, 0x00, 0x55, 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, + 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, + 0x1c, 0x3f, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1a, 0x3e, 0x78, 0x54, + 0x1b, 0x5a, 0x78, 0x06, 0x78, 0x03, 0x52, 0x00, 0x53, 0x76, 0x50, + 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, + 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x7c, 0x1c, 0x3f, 0x60, 0xd4, 0x3e, + 0x78, 0x54, 0x1c, 0x3e, 0x78, 0x54, 0x1d, 0x5a, 0x78, 0x06, 0x78, + 0x05, 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, + 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, + 0x7c, 0x1c, 0x3f, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1e, 0x3e, 0x78, + 0x54, 0x1f, 0x50, 0x03, 0x08, 0x5a, 0x78, 0x06, 0x78, 0x1a, 0x08, + 0x51, 0x78, 0x08, 0x7c, 0x19, 0xe5, 0x38, 0xfd, 0x62, 0xd0, 0x00, + 0x51, 0x78, 0x54, 0x21, 0x51, 0x79, 0x54, 0x20, 0x7c, 0x1c, 0x00, + 0x7c, 0x1c, 0x5f, 0x7c, 0x1c, 0xa2, 0x06, 0x78, 0x5f, 0x7c, 0x1c, + 0xad, 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x82, 0x7c, 0x1c, 0x75, + 0x51, 0x78, 0x01, 0x8a, 0x7c, 0x1c, 0x75, 0x06, 0x78, 0x92, 0x7c, + 0x1c, 0xad, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xcf, 0x3d, 0x56, 0x00, + 0x00, 0x80, 0x19, 0x7c, 0x1c, 0x0c, 0x06, 0x78, 0x24, 0x7c, 0x1c, + 0x33, 0x52, 0x00, 0x53, 0x76, 0x55, 0x77, 0x00, 0x06, 0x76, 0x32, + 0x7c, 0x1c, 0xc9, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xcf, 0xe4, 0x38, + 0xdc, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x07, 0x56, 0x02, 0x00, 0x62, + 0xd0, 0x00, 0x55, 0xb1, 0x00, 0x3c, 0xb9, 0x00, 0xb0, 0x05, 0x51, + 0xaa, 0x53, 0x24, 0x10, 0x50, 0x00, 0x7c, 0x09, 0xb9, 0x20, 0x56, + 0x00, 0x00, 0x81, 0x0b, 0x62, 0xd0, 0x00, 0x3c, 0xb9, 0x00, 0xb0, + 0x1b, 0x52, 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, 0xaa, + 0x7c, 0x1c, 0x33, 0x52, 0x00, 0x53, 0x76, 0x55, 0x77, 0x00, 0x06, + 0x76, 0x24, 0x7c, 0x1c, 0xc9, 0x10, 0x52, 0x00, 0x7c, 0x09, 0xb9, + 0x20, 0x10, 0x7c, 0x06, 0x64, 0x20, 0x39, 0x00, 0xbf, 0xf1, 0x62, + 0xd0, 0x00, 0x3c, 0xb9, 0x01, 0xb0, 0xcf, 0x52, 0x00, 0x54, 0x04, + 0x56, 0x03, 0x00, 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x00, + 0xa0, 0x21, 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x01, 0xa0, + 0x38, 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x02, 0xa0, 0x66, + 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x03, 0xa0, 0x89, 0x80, + 0x9e, 0x62, 0xd0, 0x00, 0x51, 0x7a, 0x08, 0x51, 0x7b, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x01, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x51, + 0x71, 0x62, 0xd0, 0x00, 0x53, 0x7b, 0x51, 0x70, 0x53, 0x7a, 0x80, + 0x7d, 0x62, 0xd0, 0x00, 0x51, 0x7c, 0x08, 0x51, 0x7d, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x03, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x51, + 0x71, 0x53, 0x78, 0x51, 0x70, 0x53, 0x79, 0x50, 0x00, 0x08, 0x50, + 0x05, 0x08, 0x51, 0x79, 0x08, 0x51, 0x78, 0x08, 0x7c, 0x1b, 0x8c, + 0x18, 0x62, 0xd0, 0x00, 0x53, 0x7d, 0x18, 0x53, 0x7c, 0x38, 0xfe, + 0x80, 0x45, 0x62, 0xd0, 0x00, 0x51, 0x7f, 0x08, 0x51, 0x7e, 0x53, + 0x79, 0x18, 0x53, 0x78, 0x65, 0x78, 0x6b, 0x79, 0x50, 0x00, 0x08, + 0x50, 0x05, 0x08, 0x51, 0x79, 0x08, 0x51, 0x78, 0x08, 0x7c, 0x1b, + 0x8c, 0x18, 0x62, 0xd0, 0x00, 0x53, 0x7f, 0x18, 0x53, 0x7e, 0x38, + 0xfe, 0x80, 0x18, 0x62, 0xd0, 0x00, 0x51, 0x80, 0x08, 0x51, 0x81, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x01, 0x08, 0x7c, 0x1b, 0xd0, 0x38, + 0xfc, 0x7c, 0x1c, 0x86, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xce, 0xf2, + 0x56, 0x00, 0x00, 0x82, 0xa6, 0x62, 0xd0, 0x00, 0x3c, 0xbd, 0x02, + 0xa0, 0xb1, 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x5f, 0x7c, 0x1c, + 0x17, 0x06, 0x78, 0x7a, 0x7c, 0x1c, 0x33, 0x3e, 0x78, 0x53, 0x78, + 0x51, 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x16, 0x7c, + 0x1c, 0x00, 0x51, 0x78, 0x01, 0x5f, 0x7c, 0x1c, 0x17, 0x06, 0x78, + 0x7a, 0x7c, 0x1c, 0x33, 0x7c, 0x1d, 0x0b, 0x80, 0x17, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x7a, 0x7c, 0x1c, 0x17, + 0x06, 0x78, 0x5f, 0x7c, 0x1c, 0x33, 0x7c, 0x1d, 0x0b, 0x50, 0x2c, + 0x13, 0x04, 0x50, 0x01, 0x1b, 0x03, 0xc0, 0x57, 0x62, 0xd0, 0x00, + 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x92, 0x7c, 0x1c, 0x17, 0x06, + 0x78, 0x7a, 0x7c, 0x1c, 0x33, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, + 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x16, 0x7c, 0x1c, 0x00, + 0x51, 0x78, 0x01, 0x92, 0x7c, 0x1c, 0x17, 0x06, 0x78, 0x7a, 0x7c, + 0x1c, 0x33, 0x7c, 0x1c, 0xfe, 0x80, 0x17, 0x62, 0xd0, 0x00, 0x7c, + 0x1c, 0x00, 0x51, 0x78, 0x01, 0x7a, 0x7c, 0x1c, 0x17, 0x06, 0x78, + 0x92, 0x7c, 0x1c, 0x33, 0x7c, 0x1c, 0xfe, 0x50, 0x2c, 0x13, 0x06, + 0x50, 0x01, 0x1b, 0x05, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x76, 0xb1, + 0x81, 0xec, 0x56, 0x01, 0x00, 0x80, 0x61, 0x62, 0xd0, 0x00, 0x7c, + 0x1c, 0x00, 0x51, 0x78, 0x01, 0x7a, 0x7c, 0x1c, 0x17, 0x06, 0x78, + 0x8a, 0x7c, 0x1c, 0x33, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, 0x12, + 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x12, 0x7c, 0x1c, 0x00, 0x7c, + 0x1c, 0xd5, 0x06, 0x76, 0x01, 0x0e, 0x77, 0x00, 0x7c, 0x1c, 0x52, + 0x80, 0x2d, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, + 0x7a, 0x7c, 0x1c, 0x17, 0x06, 0x78, 0x8a, 0x7c, 0x1c, 0x33, 0x3e, + 0x78, 0x12, 0x76, 0x51, 0x79, 0x1a, 0x77, 0xd0, 0x10, 0x7c, 0x1c, + 0x00, 0x7c, 0x1c, 0xd5, 0x16, 0x76, 0x01, 0x1e, 0x77, 0x00, 0x7c, + 0x1c, 0x52, 0x77, 0x01, 0x3d, 0x01, 0x06, 0xcf, 0x9c, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x8a, 0x7c, 0x1c, 0x17, + 0x06, 0x78, 0x82, 0x0e, 0x79, 0x00, 0x7c, 0x1c, 0x52, 0x7c, 0x1c, + 0x00, 0x51, 0x78, 0x01, 0x92, 0x7c, 0x1c, 0x17, 0x06, 0x78, 0x8a, + 0x0e, 0x79, 0x00, 0x7c, 0x1c, 0x52, 0x7c, 0x1c, 0x00, 0x51, 0x78, + 0x01, 0x7a, 0x7c, 0x1c, 0x17, 0x06, 0x78, 0x92, 0x0e, 0x79, 0x00, + 0x7c, 0x1c, 0x52, 0x7c, 0x1c, 0x00, 0x7c, 0x1c, 0x5f, 0x53, 0x77, + 0x7c, 0x1d, 0x32, 0x53, 0x74, 0x08, 0x51, 0x75, 0x53, 0x73, 0x18, + 0x53, 0x72, 0x65, 0x72, 0x6b, 0x73, 0x06, 0x78, 0x8a, 0x7c, 0x1c, + 0x33, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x72, 0x04, 0x78, 0x51, 0x73, + 0x0c, 0x79, 0x51, 0x74, 0x04, 0x78, 0x51, 0x75, 0x0c, 0x79, 0x70, + 0xfb, 0x6e, 0x79, 0x6e, 0x78, 0x7c, 0x1c, 0xeb, 0x10, 0x52, 0x00, + 0x7c, 0x06, 0xa1, 0x20, 0x7c, 0x1c, 0x0c, 0x06, 0x78, 0x52, 0x0e, + 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x53, 0x78, 0x7c, + 0x1c, 0xbd, 0x06, 0x76, 0x67, 0x0e, 0x77, 0x00, 0x51, 0x77, 0x7c, + 0x1c, 0x28, 0x51, 0x78, 0x12, 0x76, 0x50, 0x00, 0x1a, 0x77, 0xd0, + 0x1b, 0x55, 0x79, 0x01, 0x52, 0x00, 0xa0, 0x09, 0x62, 0xd0, 0x00, + 0x65, 0x79, 0x78, 0xbf, 0xf9, 0x62, 0xd0, 0x00, 0x51, 0xb4, 0x2a, + 0x79, 0x53, 0xb4, 0x80, 0x1b, 0x62, 0xd0, 0x00, 0x55, 0x79, 0x01, + 0x52, 0x00, 0xa0, 0x09, 0x62, 0xd0, 0x00, 0x65, 0x79, 0x78, 0xbf, + 0xf9, 0x62, 0xd0, 0x00, 0x51, 0x79, 0x73, 0x24, 0xb4, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x00, 0x51, 0x78, 0x01, 0x7a, 0x7c, 0x1c, 0x17, + 0x06, 0x78, 0x5f, 0x7c, 0x1c, 0x33, 0x3e, 0x78, 0x53, 0x78, 0x51, + 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x25, 0x52, 0x00, + 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, 0xa2, 0x0e, 0x79, 0x00, + 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x7a, 0x78, 0x53, 0x77, 0x06, + 0x77, 0x01, 0x51, 0x79, 0x60, 0xd5, 0x51, 0x77, 0x3f, 0x78, 0x80, + 0x12, 0x97, 0xf7, 0x40, 0x06, 0x78, 0xa2, 0x0e, 0x79, 0x00, 0x51, + 0x79, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x78, 0x97, 0xe6, 0x40, 0x06, + 0x78, 0xa2, 0x7c, 0x1c, 0x33, 0x50, 0x05, 0x3a, 0x79, 0xd0, 0x41, + 0x97, 0xcb, 0x40, 0x51, 0x78, 0x01, 0x5f, 0x53, 0x76, 0x51, 0x79, + 0x09, 0x00, 0x53, 0x77, 0x06, 0x78, 0x7a, 0x97, 0xec, 0x40, 0x3e, + 0x78, 0x53, 0x78, 0x51, 0x77, 0x7c, 0x1d, 0x32, 0x02, 0x78, 0x53, + 0x78, 0x51, 0x75, 0x0a, 0x79, 0x53, 0x79, 0x7c, 0x1c, 0xeb, 0x52, + 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, 0xa2, 0x0e, 0x79, + 0x00, 0x51, 0x79, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x78, 0x77, 0x00, + 0x3d, 0x00, 0x04, 0xcd, 0x57, 0x56, 0x00, 0x00, 0x81, 0x73, 0x62, + 0xd0, 0x00, 0x3c, 0xb9, 0x01, 0xb0, 0xd9, 0x52, 0x00, 0x54, 0x04, + 0x56, 0x03, 0x00, 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x00, + 0xa0, 0x21, 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x01, 0xa0, + 0x38, 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x02, 0xa0, 0x66, + 0x3d, 0x03, 0x00, 0xb0, 0x06, 0x3d, 0x04, 0x03, 0xa0, 0x8d, 0x80, + 0xbe, 0x62, 0xd0, 0x00, 0x51, 0x7a, 0x08, 0x51, 0x7b, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x01, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x51, + 0x71, 0x62, 0xd0, 0x00, 0x53, 0x3f, 0x51, 0x70, 0x53, 0x3e, 0x80, + 0x9d, 0x62, 0xd0, 0x00, 0x51, 0x7c, 0x08, 0x51, 0x7d, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x05, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, 0x51, + 0x71, 0x53, 0x78, 0x51, 0x70, 0x53, 0x79, 0x50, 0x00, 0x08, 0x50, + 0x03, 0x08, 0x51, 0x79, 0x08, 0x51, 0x78, 0x08, 0x7c, 0x1b, 0x8c, + 0x18, 0x62, 0xd0, 0x00, 0x53, 0x41, 0x18, 0x53, 0x40, 0x38, 0xfe, + 0x80, 0x65, 0x62, 0xd0, 0x00, 0x51, 0x7e, 0x08, 0x51, 0x7f, 0x08, + 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x7c, 0x1b, 0xd0, 0x38, 0xfc, + 0x51, 0x71, 0x53, 0x78, 0x51, 0x70, 0x53, 0x79, 0x70, 0xfb, 0x6e, + 0x79, 0x6e, 0x78, 0x51, 0x78, 0x08, 0x51, 0x79, 0x62, 0xd0, 0x00, + 0x53, 0x42, 0x18, 0x53, 0x43, 0x80, 0x34, 0x62, 0xd0, 0x00, 0x51, + 0x81, 0x08, 0x51, 0x80, 0x53, 0x79, 0x18, 0x53, 0x78, 0x65, 0x78, + 0x6b, 0x79, 0x51, 0x78, 0x08, 0x51, 0x79, 0x53, 0x44, 0x18, 0x53, + 0x45, 0x80, 0x17, 0x62, 0xd0, 0x00, 0x96, 0x9c, 0x40, 0x51, 0x78, + 0x01, 0x7a, 0x96, 0xac, 0x40, 0x06, 0x78, 0x3e, 0x0e, 0x79, 0x00, + 0x96, 0xde, 0x40, 0x62, 0xd0, 0x00, 0x96, 0x86, 0x40, 0x51, 0x78, + 0x01, 0x5f, 0x96, 0x96, 0x40, 0x51, 0x78, 0x01, 0x7a, 0x53, 0x74, + 0x51, 0x79, 0x97, 0x99, 0x40, 0x51, 0x76, 0x12, 0x74, 0x51, 0x77, + 0x1a, 0x75, 0xd0, 0x2b, 0x97, 0x24, 0x40, 0x51, 0x76, 0x01, 0x5f, + 0x53, 0x74, 0x51, 0x77, 0x97, 0x81, 0x40, 0x06, 0x76, 0x7a, 0x0e, + 0x77, 0x00, 0x51, 0x77, 0x60, 0xd4, 0x3e, 0x76, 0x53, 0x77, 0x3e, + 0x76, 0x12, 0x74, 0x54, 0x04, 0x51, 0x77, 0x1a, 0x75, 0x54, 0x03, + 0x80, 0x07, 0x56, 0x04, 0x00, 0x56, 0x03, 0x00, 0x62, 0xd0, 0x00, + 0x06, 0x78, 0x36, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd5, 0x52, + 0x03, 0x3f, 0x78, 0x52, 0x04, 0x3f, 0x78, 0x96, 0x22, 0x40, 0x51, + 0x78, 0x01, 0x5f, 0x96, 0x32, 0x40, 0x06, 0x78, 0x46, 0x0e, 0x79, + 0x00, 0x96, 0x64, 0x40, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xce, 0x8a, + 0x62, 0xd0, 0x00, 0x3c, 0xbd, 0x02, 0xa0, 0x1b, 0x3c, 0xb1, 0x00, + 0xa0, 0x16, 0x55, 0xbb, 0x00, 0x50, 0x75, 0x08, 0x50, 0x30, 0x08, + 0x90, 0x0e, 0x38, 0xfe, 0x7c, 0x0b, 0xc1, 0x10, 0x7c, 0x08, 0x5d, + 0x20, 0x38, 0xf9, 0x20, 0x7f, 0x10, 0x4f, 0x80, 0x02, 0x40, 0x62, + 0xd0, 0x00, 0x52, 0xfc, 0x53, 0x78, 0x52, 0xfb, 0x53, 0x79, 0x51, + 0x78, 0x11, 0x01, 0x54, 0xfc, 0x51, 0x79, 0x19, 0x00, 0x54, 0xfb, + 0x3c, 0x79, 0x00, 0xbf, 0xe4, 0x3c, 0x78, 0x00, 0xbf, 0xdf, 0x20, + 0x7f, 0x10, 0x7c, 0x05, 0x28, 0x7c, 0x05, 0x05, 0x7c, 0x04, 0xe2, + 0x7c, 0x04, 0xbf, 0x20, 0x7f, 0x10, 0x7c, 0x05, 0x24, 0x7c, 0x05, + 0x01, 0x7c, 0x04, 0xde, 0x7c, 0x04, 0xbb, 0x20, 0x7f, 0x10, 0x4f, + 0x38, 0x05, 0x62, 0xd0, 0x00, 0x51, 0x68, 0x54, 0x02, 0x51, 0x67, + 0x54, 0x01, 0x56, 0x04, 0x00, 0x56, 0x00, 0x00, 0x56, 0x03, 0x00, + 0x80, 0x61, 0x95, 0x8e, 0x40, 0x06, 0x78, 0x52, 0x0e, 0x79, 0x00, + 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x53, 0x78, 0x96, 0x2e, 0x40, + 0x06, 0x76, 0x67, 0x0e, 0x77, 0x00, 0x51, 0x77, 0x95, 0x8e, 0x40, + 0x51, 0x78, 0x12, 0x76, 0x50, 0x00, 0x1a, 0x77, 0xd0, 0x03, 0x77, + 0x03, 0x62, 0xd0, 0x00, 0x95, 0x54, 0x40, 0x06, 0x78, 0x67, 0x95, + 0x81, 0x40, 0x3e, 0x78, 0x53, 0x78, 0x52, 0x02, 0x12, 0x78, 0x52, + 0x01, 0x1a, 0x79, 0xd0, 0x1a, 0x95, 0x3d, 0x40, 0x06, 0x78, 0x67, + 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x01, + 0x3e, 0x78, 0x54, 0x02, 0x52, 0x00, 0x54, 0x04, 0x77, 0x00, 0x3d, + 0x00, 0x04, 0xcf, 0x9c, 0x50, 0x01, 0x3b, 0x03, 0xd0, 0x08, 0x62, + 0xd0, 0x00, 0x50, 0xe1, 0x80, 0x06, 0x52, 0x04, 0x62, 0xd0, 0x00, + 0x38, 0xfb, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x62, 0xd0, 0x00, + 0x3c, 0xb2, 0x00, 0xb0, 0x79, 0x70, 0xfe, 0x26, 0x2c, 0xf0, 0x51, + 0xbc, 0x01, 0x01, 0x53, 0x79, 0x51, 0x2c, 0x2a, 0x79, 0x53, 0x2c, + 0x71, 0x01, 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x41, + 0x00, 0xf7, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x64, + 0x62, 0xd0, 0x00, 0x20, 0x53, 0x79, 0x47, 0x79, 0x20, 0xa0, 0x03, + 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0xdd, 0x38, + 0xfe, 0x77, 0x00, 0x3d, 0x00, 0xc8, 0xcf, 0xdf, 0x56, 0x00, 0x00, + 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x53, + 0x79, 0x47, 0x79, 0x20, 0xb0, 0x03, 0x80, 0x12, 0x50, 0x00, 0x08, + 0x50, 0x14, 0x08, 0x9e, 0xb6, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, + 0x1e, 0xcf, 0xdf, 0x62, 0xd0, 0x00, 0x51, 0x2c, 0x29, 0x08, 0x53, + 0x2c, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, + 0x02, 0x62, 0xd0, 0x00, 0x3c, 0xb2, 0x01, 0xb0, 0x70, 0x70, 0xfe, + 0x26, 0x2c, 0xf0, 0x51, 0xbc, 0x01, 0x09, 0x53, 0x79, 0x51, 0x2c, + 0x2a, 0x79, 0x53, 0x2c, 0x71, 0x01, 0x10, 0x7c, 0x06, 0x64, 0x62, + 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, 0x00, 0x00, 0x80, 0x1e, + 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x79, 0x47, + 0x79, 0x20, 0xa0, 0x03, 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, + 0x08, 0x9e, 0x55, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, 0xc8, 0xcf, + 0xdf, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x64, 0x62, + 0xd0, 0x00, 0x20, 0x53, 0x79, 0x47, 0x79, 0x20, 0xb0, 0x03, 0x80, + 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0x2e, 0x38, 0xfe, + 0x77, 0x00, 0x3d, 0x00, 0x1e, 0xcf, 0xdf, 0x43, 0x00, 0x08, 0x38, + 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x04, 0x62, 0xd0, 0x00, 0x51, + 0x2c, 0x21, 0xf0, 0x54, 0x00, 0x51, 0x2f, 0x54, 0x01, 0x3d, 0x00, + 0x10, 0xb0, 0x18, 0x55, 0xb5, 0x00, 0x55, 0x9b, 0x00, 0x55, 0x9a, + 0x00, 0x55, 0x9d, 0x00, 0x55, 0x9c, 0x00, 0x56, 0x00, 0x00, 0x26, + 0x2c, 0x0f, 0x80, 0xb6, 0x3d, 0x00, 0x20, 0xb0, 0x1e, 0x62, 0xd0, + 0x00, 0x55, 0xb5, 0x01, 0x55, 0xb6, 0x00, 0x55, 0x9b, 0x08, 0x55, + 0x9a, 0x08, 0x55, 0x9d, 0x08, 0x55, 0x9c, 0x08, 0x56, 0x00, 0x00, + 0x26, 0x2c, 0x0f, 0x80, 0x94, 0x3d, 0x00, 0x40, 0xb0, 0x0f, 0x62, + 0xd0, 0x00, 0x55, 0xbd, 0x02, 0x56, 0x00, 0x00, 0x26, 0x2c, 0x0f, + 0x80, 0x81, 0x3d, 0x00, 0x50, 0xb0, 0x7c, 0x52, 0x01, 0x54, 0x03, + 0x56, 0x02, 0x00, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x01, + 0xa0, 0x17, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x02, 0xa0, + 0x27, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x04, 0xa0, 0x38, + 0x80, 0x46, 0x62, 0xd0, 0x00, 0x55, 0xb9, 0x01, 0x7c, 0x0e, 0x04, + 0x62, 0xd0, 0x00, 0x51, 0x31, 0x29, 0x80, 0x53, 0x31, 0x56, 0x00, + 0x00, 0x26, 0x2c, 0x0f, 0x80, 0x2c, 0x62, 0xd0, 0x00, 0x51, 0x30, + 0x53, 0x52, 0x51, 0x30, 0x53, 0x53, 0x51, 0x30, 0x53, 0x54, 0x51, + 0x30, 0x53, 0x55, 0x56, 0x00, 0x00, 0x26, 0x2c, 0x0f, 0x80, 0x11, + 0x62, 0xd0, 0x00, 0x51, 0x30, 0x53, 0x0e, 0x55, 0x0d, 0x00, 0x56, + 0x00, 0x00, 0x26, 0x2c, 0x0f, 0x56, 0x01, 0x00, 0x62, 0xd0, 0x00, + 0x55, 0x2d, 0x0e, 0x55, 0x2e, 0x08, 0x55, 0x2f, 0x00, 0x38, 0xfc, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x04, 0x62, 0xd0, 0x00, 0x3c, 0xb5, + 0x00, 0xa0, 0x13, 0x9d, 0x52, 0x62, 0xd0, 0x00, 0x3c, 0xb6, 0x00, + 0xb0, 0xe8, 0x55, 0xb6, 0x01, 0x7c, 0x0b, 0xc1, 0x80, 0xe0, 0x62, + 0xd0, 0x00, 0x3c, 0xb7, 0x00, 0xb0, 0x27, 0x3c, 0xb0, 0x01, 0xb0, + 0x22, 0x51, 0xbc, 0x53, 0x78, 0x55, 0x79, 0x00, 0x65, 0x78, 0x6b, + 0x79, 0x06, 0x78, 0x67, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, + 0x3e, 0x78, 0x54, 0x01, 0x3e, 0x78, 0x54, 0x02, 0x51, 0xbc, 0x54, + 0x00, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x9a, 0xd0, 0x08, 0x10, + 0x7c, 0x05, 0x28, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x05, 0x24, 0x20, + 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x9b, 0xd0, 0x08, 0x10, 0x7c, + 0x05, 0x05, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x05, 0x01, 0x20, 0x62, + 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x9c, 0xd0, 0x08, 0x10, 0x7c, 0x04, + 0xe2, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0xde, 0x20, 0x62, 0xd0, + 0x00, 0x50, 0x01, 0x3a, 0x9d, 0xd0, 0x08, 0x10, 0x7c, 0x04, 0xbf, + 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0xbb, 0x20, 0x7c, 0x10, 0x9a, + 0x62, 0xd0, 0x00, 0x3c, 0xb7, 0x00, 0xb0, 0x53, 0x3c, 0xb0, 0x01, + 0xb0, 0x4e, 0x3c, 0xb1, 0x00, 0xb0, 0x40, 0x92, 0x65, 0x40, 0x51, + 0x78, 0x01, 0x5f, 0x92, 0x75, 0x40, 0x06, 0x76, 0x37, 0x0e, 0x77, + 0x00, 0x06, 0x78, 0x7a, 0x92, 0x85, 0x40, 0x3e, 0x78, 0x53, 0x78, + 0x51, 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x1c, 0x92, + 0x41, 0x40, 0x51, 0x78, 0x01, 0x7a, 0x92, 0x51, 0x40, 0x52, 0x02, + 0x14, 0x76, 0x52, 0x01, 0x1c, 0x77, 0x06, 0x78, 0x5f, 0x0e, 0x79, + 0x00, 0x92, 0x7b, 0x40, 0x62, 0xd0, 0x00, 0x55, 0xb7, 0x01, 0x55, + 0xb0, 0x00, 0x38, 0xfc, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x03, 0x56, + 0x02, 0x00, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x3e, 0x62, + 0xd0, 0x00, 0x92, 0x07, 0x40, 0x52, 0xfc, 0x04, 0x78, 0x52, 0xfb, + 0x0c, 0x79, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x53, 0x79, 0x3e, + 0x78, 0x53, 0x78, 0x52, 0x02, 0x12, 0x78, 0x52, 0x01, 0x1a, 0x79, + 0xd0, 0x18, 0x91, 0xe6, 0x40, 0x52, 0xfc, 0x04, 0x78, 0x52, 0xfb, + 0x0c, 0x79, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x01, 0x3e, + 0x78, 0x54, 0x02, 0x77, 0x00, 0x52, 0x00, 0x3b, 0xfa, 0xcf, 0xbe, + 0x62, 0xd0, 0x00, 0x52, 0x02, 0x53, 0x78, 0x52, 0x01, 0x53, 0x79, + 0x38, 0xfd, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x71, 0x10, 0x41, + 0x01, 0xf7, 0x43, 0x00, 0x08, 0x70, 0xcf, 0x43, 0x00, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x64, 0x08, 0x9b, 0xbd, 0x71, 0x10, 0x41, 0x01, + 0xf7, 0x41, 0x00, 0xf7, 0x70, 0xcf, 0x43, 0x00, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x64, 0x08, 0x9b, 0xa8, 0x38, 0xfc, 0x5d, 0x00, 0x62, + 0xd0, 0x00, 0x53, 0x79, 0x26, 0x79, 0x08, 0x3c, 0x79, 0x08, 0xb0, + 0x09, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x07, 0x56, 0x01, + 0x01, 0x56, 0x00, 0x00, 0x52, 0x01, 0x62, 0xd0, 0x00, 0x53, 0xbd, + 0x71, 0x10, 0x43, 0x00, 0x08, 0x41, 0x01, 0xf7, 0x70, 0xcf, 0x3c, + 0xbd, 0x00, 0xb0, 0x04, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, + 0x10, 0x4f, 0x38, 0x01, 0x10, 0x50, 0x02, 0x7c, 0x03, 0x91, 0x20, + 0x10, 0x50, 0xff, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0xff, 0x7c, + 0x03, 0x91, 0x20, 0x10, 0x50, 0x08, 0x08, 0x50, 0x00, 0x08, 0x50, + 0x7a, 0x08, 0x7c, 0x04, 0x76, 0x38, 0xfd, 0x20, 0x56, 0x00, 0x00, + 0x80, 0x6e, 0x62, 0xd0, 0x00, 0x91, 0x1d, 0x40, 0x51, 0x78, 0x01, + 0x5f, 0x91, 0x2d, 0x40, 0x06, 0x78, 0x7a, 0x91, 0x43, 0x40, 0x3e, + 0x78, 0x53, 0x78, 0x51, 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, + 0xd0, 0x3d, 0x90, 0xff, 0x40, 0x51, 0x78, 0x01, 0x5f, 0x91, 0x0f, + 0x40, 0x06, 0x78, 0x7a, 0x91, 0x25, 0x40, 0x92, 0x07, 0x40, 0x51, + 0x79, 0x10, 0x7c, 0x03, 0x91, 0x20, 0x62, 0xd0, 0x00, 0x90, 0xe2, + 0x40, 0x51, 0x78, 0x01, 0x5f, 0x90, 0xf2, 0x40, 0x06, 0x78, 0x7a, + 0x91, 0x08, 0x40, 0x91, 0xea, 0x40, 0x26, 0x79, 0x00, 0x51, 0x78, + 0x10, 0x7c, 0x03, 0x91, 0x20, 0x80, 0x0f, 0x10, 0x50, 0x00, 0x7c, + 0x03, 0x91, 0x20, 0x10, 0x50, 0x00, 0x7c, 0x03, 0x91, 0x20, 0x77, + 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x8f, 0x10, 0x50, 0x00, 0x7c, 0x03, + 0x91, 0x20, 0x10, 0x50, 0x01, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, + 0x00, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0x01, 0x7c, 0x03, 0x91, + 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0xff, + 0x7c, 0x03, 0x91, 0x7c, 0x04, 0x6d, 0x20, 0x50, 0x13, 0x08, 0x50, + 0x88, 0x08, 0x9a, 0x97, 0x38, 0xfe, 0x38, 0xff, 0x20, 0x7f, 0x7f, + 0x10, 0x4f, 0x7c, 0x1b, 0x97, 0x20, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, 0x00, 0x53, 0x70, 0x53, + 0x71, 0x55, 0x6f, 0x10, 0x66, 0xfc, 0x6c, 0xfb, 0x6b, 0x70, 0x6b, + 0x71, 0x51, 0x70, 0x1b, 0xfa, 0x51, 0x71, 0x1b, 0xf9, 0xc0, 0x09, + 0x53, 0x71, 0x52, 0xfa, 0x1c, 0x70, 0x77, 0xfc, 0x7a, 0x6f, 0xbf, + 0xe3, 0x51, 0x70, 0x54, 0xfa, 0x51, 0x71, 0x54, 0xf9, 0x18, 0x60, + 0xd0, 0x7f, 0x10, 0x4f, 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, + 0x00, 0x53, 0x71, 0x53, 0x70, 0x55, 0x6f, 0x10, 0x6f, 0xf9, 0x6f, + 0xfa, 0xd0, 0x09, 0x52, 0xfc, 0x04, 0x71, 0x52, 0xfb, 0x0c, 0x70, + 0x66, 0xfc, 0x6c, 0xfb, 0x7a, 0x6f, 0xbf, 0xeb, 0x18, 0x60, 0xd0, + 0x20, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x52, 0x00, 0x53, 0x78, 0x55, + 0x79, 0x00, 0x65, 0x78, 0x6b, 0x79, 0x7f, 0x62, 0xd0, 0x00, 0x52, + 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x7f, 0x53, 0x76, 0x51, 0x79, + 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x76, 0x53, 0x77, 0x3e, 0x76, 0x53, + 0x76, 0x7f, 0x60, 0xd4, 0x3e, 0x76, 0x53, 0x77, 0x3e, 0x76, 0x53, + 0x76, 0x7f, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, + 0x53, 0x79, 0x7f, 0x51, 0x71, 0x53, 0x76, 0x51, 0x70, 0x53, 0x77, + 0x51, 0x76, 0x02, 0x78, 0x53, 0x78, 0x51, 0x77, 0x0a, 0x79, 0x7f, + 0x51, 0x79, 0x60, 0xd5, 0x51, 0x77, 0x3f, 0x78, 0x51, 0x76, 0x3f, + 0x78, 0x7f, 0x51, 0x78, 0x01, 0x7a, 0x53, 0x76, 0x51, 0x79, 0x09, + 0x00, 0x7f, 0x51, 0x76, 0x02, 0x78, 0x53, 0x78, 0x51, 0x77, 0x0a, + 0x79, 0x7f, 0x53, 0x76, 0x51, 0x79, 0x09, 0x00, 0x60, 0xd5, 0x52, + 0x20, 0x3f, 0x76, 0x52, 0x21, 0x3f, 0x76, 0x7f, 0x51, 0x71, 0x53, + 0x78, 0x51, 0x70, 0x53, 0x79, 0x70, 0xfb, 0x6e, 0x79, 0x6e, 0x78, + 0x51, 0x78, 0x08, 0x51, 0x79, 0x62, 0xd0, 0x00, 0x53, 0x80, 0x18, + 0x53, 0x81, 0x7f, 0x60, 0xd5, 0x52, 0x20, 0x3f, 0x76, 0x52, 0x21, + 0x3f, 0x76, 0x7f, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd5, 0x52, + 0x20, 0x3f, 0x78, 0x52, 0x21, 0x3f, 0x78, 0x7f, 0x52, 0x00, 0x53, + 0x76, 0x55, 0x77, 0x00, 0x65, 0x76, 0x6b, 0x77, 0x7f, 0x0e, 0x77, + 0x00, 0x51, 0x77, 0x60, 0xd5, 0x51, 0x79, 0x3f, 0x76, 0x7f, 0x06, + 0x78, 0x7a, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, + 0x53, 0x77, 0x3e, 0x78, 0x16, 0x78, 0x02, 0x53, 0x76, 0x7f, 0x70, + 0xfb, 0x6e, 0x79, 0x6e, 0x78, 0x51, 0x77, 0x60, 0xd5, 0x51, 0x79, + 0x3f, 0x76, 0x51, 0x78, 0x3f, 0x76, 0x7f, 0x3e, 0x78, 0x12, 0x76, + 0x54, 0x06, 0x51, 0x79, 0x1a, 0x77, 0x54, 0x05, 0x7f, 0x3e, 0x78, + 0x12, 0x76, 0x54, 0x04, 0x51, 0x79, 0x1a, 0x77, 0x54, 0x03, 0x7f, + 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, 0x14, 0x78, 0x51, 0x77, 0x1c, + 0x79, 0x7f, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x74, 0x53, 0x75, 0x3e, + 0x74, 0x53, 0x74, 0x7f, 0x60, 0xd4, 0x3e, 0x76, 0x53, 0x75, 0x3e, + 0x76, 0x16, 0x76, 0x02, 0x7f, 0x00, 0x2c, 0x00, 0x22, 0x00, 0x82, + 0x00, 0x18, 0x00, 0x9a, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, 0x9e, + 0x00, 0x09, 0x00, 0xa7, 0x07, 0x01, 0x02, 0x03, 0x10, 0x17, 0x1c, + 0x15, 0x00, 0xae, 0x00, 0x05, 0x00, 0xb3, 0x04, 0x03, 0x00, 0x01, + 0x01, 0x00, 0xb7, 0x00, 0x07, 0xff, 0x00, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_NAATT.h b/drivers/input/keyboard/cypressbln/touchkey_fw_NAATT.h new file mode 100644 index 0000000..9a966b1 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_NAATT.h @@ -0,0 +1,747 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x00, 0x68, + 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x05, 0x47, 0x7e, 0x7e, 0x30, + 0x30, 0x30, 0x7d, 0x06, 0x93, 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, + 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x40, 0x71, 0x10, 0x62, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe3, 0x38, 0x50, 0x80, 0x4e, 0x62, 0xe3, 0x38, 0x5d, + 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, 0xfa, 0x01, 0x40, 0x4f, 0x5b, + 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, 0x50, 0x06, 0x00, 0x40, + 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, 0x70, 0xef, 0x18, 0x60, + 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, 0x71, 0x10, 0x62, 0xe0, + 0x02, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x71, 0x10, 0x41, 0xe1, 0xfe, + 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, 0x03, 0x50, 0x00, 0x4e, + 0x62, 0xd3, 0x03, 0x62, 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, + 0x00, 0x71, 0xc0, 0x7c, 0x03, 0x01, 0x62, 0xd0, 0x00, 0x50, 0x02, + 0x57, 0xff, 0x08, 0x28, 0x53, 0x79, 0x18, 0x75, 0x09, 0x00, 0x28, + 0x4b, 0x51, 0x79, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, + 0x08, 0x28, 0x60, 0xd5, 0x74, 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, + 0x08, 0x28, 0x53, 0x79, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xa0, + 0x1c, 0x53, 0x78, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x3f, 0x79, + 0x47, 0x79, 0xff, 0xb0, 0x06, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x18, + 0x7a, 0x78, 0xbf, 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, 0x00, 0x08, + 0x28, 0x53, 0x78, 0x50, 0x00, 0x3f, 0x79, 0x47, 0x79, 0xff, 0xb0, + 0x08, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x78, 0xbf, + 0xef, 0x18, 0x8f, 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, + 0x10, 0x62, 0xe0, 0x02, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x0a, + 0x86, 0x8f, 0xff, 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x00, 0xfd, 0x00, 0xcd, 0x00, + 0xce, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0xa1, 0x80, 0xa2, + 0xc0, 0xa3, 0x0c, 0xa8, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x7c, 0x33, + 0x7a, 0x00, 0x7b, 0x00, 0x79, 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, 0xca, 0x20, 0xd6, 0x44, 0xcf, 0x00, 0xcb, 0x00, + 0xc8, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xd7, 0x00, 0xa9, 0x00, 0x2b, + 0x00, 0xb0, 0x00, 0xb3, 0x02, 0xb6, 0x00, 0xb2, 0x00, 0xb5, 0x00, + 0xb8, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0x33, 0x00, 0x34, + 0x00, 0x35, 0x00, 0xff, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0xdc, 0x00, + 0xe2, 0x00, 0xdd, 0x00, 0xd8, 0x02, 0xd9, 0xa0, 0xda, 0x28, 0xdb, + 0x00, 0xdf, 0x00, 0x29, 0x00, 0x30, 0x00, 0xbd, 0x00, 0xff, 0x70, + 0xef, 0x62, 0x00, 0x08, 0x71, 0x10, 0x62, 0x00, 0x98, 0x62, 0x01, + 0x02, 0x70, 0xef, 0x62, 0x04, 0x03, 0x71, 0x10, 0x62, 0x04, 0x17, + 0x62, 0x05, 0xab, 0x70, 0xef, 0x62, 0x08, 0x00, 0x71, 0x10, 0x62, + 0x08, 0x00, 0x62, 0x09, 0x28, 0x70, 0xef, 0x62, 0x0c, 0x00, 0x71, + 0x10, 0x62, 0x0c, 0x00, 0x62, 0x0d, 0x00, 0x70, 0xef, 0x62, 0x10, + 0x00, 0x71, 0x10, 0x62, 0x10, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, + 0x62, 0x01, 0x00, 0x62, 0x05, 0x00, 0x62, 0x09, 0x00, 0x62, 0x0d, + 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, 0x7f, 0x1d, 0x8c, 0x55, 0x02, + 0x08, 0x55, 0x03, 0x03, 0x55, 0x04, 0x00, 0x7c, 0x03, 0x11, 0x7c, + 0x02, 0xaa, 0x7f, 0x10, 0x70, 0xef, 0x50, 0x00, 0x67, 0x50, 0x02, + 0x57, 0x00, 0x7c, 0x03, 0x2c, 0x50, 0x01, 0x67, 0x50, 0x02, 0x57, + 0x83, 0x7c, 0x03, 0x2c, 0x70, 0xef, 0x20, 0x7f, 0x38, 0x02, 0x10, + 0x08, 0x4f, 0x56, 0xfc, 0x00, 0xd0, 0x04, 0x56, 0xfc, 0x01, 0x18, + 0x20, 0x70, 0xef, 0x62, 0xe3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xff, + 0xa0, 0x1f, 0x4f, 0x48, 0xfc, 0x01, 0xa0, 0x03, 0x71, 0x10, 0x54, + 0xfd, 0x18, 0x20, 0x75, 0x09, 0x00, 0x10, 0x08, 0x28, 0x4f, 0x59, + 0xfd, 0x61, 0x00, 0x18, 0x20, 0x75, 0x09, 0x00, 0x8f, 0xd7, 0x38, + 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + 0x62, 0xd0, 0x00, 0x2e, 0x02, 0x08, 0x51, 0x02, 0x60, 0x00, 0x71, + 0x10, 0x41, 0x01, 0xf7, 0x43, 0x00, 0x08, 0x70, 0xef, 0x7f, 0x62, + 0xd0, 0x00, 0x53, 0x00, 0x71, 0x10, 0x5d, 0xe0, 0x08, 0x21, 0xf8, + 0x29, 0x00, 0x70, 0xfe, 0x60, 0xe0, 0x70, 0xef, 0x4b, 0x4b, 0x4b, + 0x4b, 0x51, 0x02, 0x21, 0xf7, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, + 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, + 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, + 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, + 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, + 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, + 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, + 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x47, 0x00, + 0x00, 0x49, 0x01, 0x00, 0x29, 0x08, 0x60, 0x00, 0x57, 0x01, 0x79, + 0xbf, 0xfe, 0x18, 0x71, 0x10, 0x60, 0xe0, 0x70, 0xef, 0x71, 0x01, + 0x7f, 0x08, 0x67, 0x67, 0x67, 0x67, 0x21, 0x0f, 0xff, 0x2b, 0x9f, + 0x4e, 0x18, 0x21, 0x0f, 0xff, 0x24, 0x9f, 0x47, 0x7f, 0x08, 0x10, + 0x28, 0xa0, 0x0b, 0x9f, 0x3f, 0x20, 0x18, 0x75, 0xdf, 0xf5, 0x74, + 0x8f, 0xf2, 0x38, 0xfe, 0x7f, 0x52, 0x00, 0xa0, 0x08, 0x10, 0x9f, + 0x2d, 0x20, 0x75, 0x8f, 0xf6, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, + 0x0d, 0x9f, 0x20, 0x50, 0x0a, 0x9f, 0x1c, 0x7f, 0x70, 0xbf, 0x62, + 0xd3, 0x03, 0x4f, 0x52, 0xfb, 0xa0, 0x15, 0x7b, 0xfb, 0x52, 0xfc, + 0x59, 0xfd, 0x60, 0xd3, 0x52, 0x00, 0x9f, 0x05, 0x4f, 0x62, 0xd3, + 0x03, 0x77, 0xfd, 0x8f, 0xe9, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x3d, + 0xfa, 0x00, 0xb0, 0x06, 0x3d, 0xfb, 0x00, 0xa0, 0x18, 0x10, 0x52, + 0xfc, 0x59, 0xfd, 0x28, 0x9e, 0xe6, 0x20, 0x07, 0xfd, 0x01, 0x0f, + 0xfc, 0x00, 0x17, 0xfb, 0x01, 0x1f, 0xfa, 0x00, 0x8f, 0xe0, 0x7f, + 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, + 0xa0, 0x06, 0x26, 0x02, 0x7f, 0x80, 0x04, 0x2e, 0x02, 0x80, 0x51, + 0x02, 0x60, 0x00, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, + 0x29, 0x00, 0xa0, 0x06, 0x26, 0x02, 0xef, 0x80, 0x04, 0x2e, 0x02, + 0x10, 0x51, 0x02, 0x60, 0x00, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, + 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xef, 0x80, 0x04, + 0x2e, 0x03, 0x10, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, 0x71, 0xc0, + 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, + 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xfb, + 0x80, 0x04, 0x2e, 0x03, 0x04, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x10, 0x70, + 0x3f, 0x71, 0x80, 0x5d, 0xd3, 0x08, 0x5d, 0xd0, 0x08, 0x62, 0xd0, + 0x00, 0x51, 0x08, 0x60, 0xd3, 0x2e, 0x05, 0x80, 0x49, 0xd7, 0x08, + 0xa0, 0x09, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x00, 0x80, 0x08, 0x49, + 0xd7, 0x20, 0xa0, 0x03, 0x80, 0xac, 0x51, 0x05, 0x21, 0x0e, 0xe0, + 0x01, 0x80, 0x11, 0x80, 0x6d, 0x80, 0x7f, 0x80, 0x4d, 0x80, 0x9c, + 0x80, 0x9a, 0x80, 0x98, 0x80, 0x96, 0x80, 0x9d, 0x5d, 0xd8, 0x21, + 0xfe, 0x39, 0x40, 0xa0, 0x06, 0x62, 0xd7, 0x00, 0x80, 0x90, 0x49, + 0xd8, 0x01, 0xb0, 0x15, 0x55, 0x0c, 0x02, 0x26, 0x07, 0x00, 0x26, + 0x06, 0x00, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x62, 0xd7, 0x10, + 0x80, 0x77, 0x55, 0x0c, 0x01, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x06, + 0x5f, 0x07, 0x06, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, 0x60, + 0xd8, 0x76, 0x07, 0x62, 0xd7, 0x14, 0x80, 0x5b, 0x51, 0x0a, 0x78, + 0x3a, 0x07, 0xc0, 0x0f, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, + 0x60, 0xd8, 0x76, 0x07, 0x2e, 0x05, 0x20, 0x60, 0xd8, 0x62, 0xd7, + 0x04, 0x80, 0x3f, 0x5d, 0xd8, 0x3a, 0x0a, 0xd0, 0x2b, 0xa0, 0x29, + 0x53, 0x07, 0x53, 0x06, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x80, + 0x18, 0x51, 0x0b, 0x78, 0x3a, 0x07, 0xc0, 0x16, 0x51, 0x09, 0x02, + 0x07, 0x5c, 0x5d, 0xd8, 0x54, 0x00, 0x2e, 0x05, 0x10, 0x76, 0x07, + 0x80, 0x01, 0x62, 0xd7, 0x10, 0x80, 0x0f, 0x62, 0xd7, 0x00, 0x80, + 0x0a, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x00, 0x55, 0x0c, 0x00, 0x18, + 0x60, 0xd0, 0x18, 0x60, 0xd3, 0x20, 0x18, 0x7e, 0x62, 0xd0, 0x00, + 0x71, 0x10, 0x41, 0x04, 0xfc, 0x43, 0x05, 0x03, 0x70, 0xef, 0x26, + 0x03, 0xfc, 0x51, 0x03, 0x60, 0x04, 0x55, 0x0c, 0x00, 0x90, 0x28, + 0x90, 0x2d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x50, 0x00, 0x53, 0x06, + 0x71, 0x10, 0x43, 0x04, 0x03, 0x43, 0x05, 0x03, 0x70, 0xef, 0x2e, + 0x03, 0x03, 0x51, 0x03, 0x60, 0x04, 0x7f, 0x62, 0xd0, 0x00, 0x51, + 0x05, 0x21, 0xb0, 0x26, 0x05, 0x4f, 0x7f, 0x41, 0xe0, 0x7f, 0x43, + 0xe0, 0x80, 0x7f, 0x43, 0xd6, 0x31, 0x7f, 0x62, 0xd0, 0x00, 0x4f, + 0x52, 0xfd, 0x53, 0x0a, 0x52, 0xfc, 0x53, 0x0b, 0x52, 0xfb, 0x53, + 0x09, 0x52, 0xfa, 0x53, 0x08, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, + 0x5d, 0xa4, 0x04, 0x1b, 0x5d, 0xa5, 0x0c, 0x1a, 0x55, 0x1c, 0x01, + 0x18, 0x7e, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x53, 0x1e, + 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0xa3, 0x62, 0xd3, 0x00, 0x13, + 0x60, 0x62, 0xd3, 0x00, 0x54, 0x68, 0x62, 0xd3, 0x00, 0x52, 0xa2, + 0x62, 0xd3, 0x00, 0x1b, 0x5f, 0x62, 0xd3, 0x00, 0x54, 0x67, 0x48, + 0x67, 0x80, 0xb0, 0x33, 0x3d, 0x67, 0x00, 0xb0, 0x7b, 0x51, 0x0d, + 0x3b, 0x68, 0xc0, 0x75, 0x52, 0x68, 0x58, 0x1e, 0x01, 0x00, 0x6d, + 0x62, 0xd3, 0x00, 0x05, 0x4e, 0xc0, 0x09, 0x51, 0x0f, 0x3b, 0x4e, + 0xd0, 0x12, 0xa0, 0x10, 0x56, 0x4e, 0x00, 0x5b, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x07, 0x60, 0x01, 0x0f, 0x5f, 0x00, 0x80, 0x41, 0x3d, + 0x67, 0xff, 0xb0, 0x09, 0x50, 0xff, 0x12, 0x0e, 0x3b, 0x68, 0xc0, + 0x20, 0x62, 0xd3, 0x00, 0x56, 0x68, 0x00, 0x56, 0x67, 0x00, 0x5b, + 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x5b, 0x78, 0xd0, 0x03, 0x50, + 0x00, 0x54, 0x5b, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, 0x62, + 0xd3, 0x00, 0x52, 0xa3, 0x62, 0xd3, 0x00, 0x54, 0x60, 0x62, 0xd3, + 0x00, 0x52, 0xa2, 0x62, 0xd3, 0x00, 0x54, 0x5f, 0x51, 0x1e, 0x64, + 0x5c, 0x62, 0xd3, 0x00, 0x56, 0x68, 0x00, 0x56, 0x67, 0x00, 0x5b, + 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x12, 0x54, 0x5b, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x08, + 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x52, 0x53, 0x19, 0x55, 0x18, 0x00, + 0x18, 0x08, 0x90, 0x7e, 0x62, 0xd3, 0x00, 0x23, 0x56, 0xb0, 0x2c, + 0x51, 0x10, 0x04, 0x19, 0x0e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x52, 0x68, 0x12, 0x19, 0x52, 0x67, 0x1a, 0x18, 0xc0, + 0x39, 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x57, 0x78, 0x54, + 0x57, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x3e, 0x80, 0x18, 0x51, + 0x10, 0x14, 0x19, 0x1e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, + 0x00, 0x52, 0x68, 0x12, 0x19, 0x52, 0x67, 0x1a, 0x18, 0xc0, 0x0e, + 0x5b, 0x67, 0x90, 0x31, 0x62, 0xd3, 0x00, 0x2d, 0x56, 0x50, 0x01, + 0x80, 0x24, 0x5b, 0x67, 0x08, 0x90, 0x23, 0x73, 0x62, 0xd3, 0x00, + 0x25, 0x56, 0x62, 0xd3, 0x00, 0x20, 0x51, 0x11, 0x54, 0x57, 0x50, + 0x00, 0x80, 0x0d, 0x5b, 0x67, 0x90, 0x0d, 0x73, 0x62, 0xd3, 0x00, + 0x25, 0x56, 0x50, 0x00, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x67, + 0x67, 0x67, 0x5c, 0x18, 0x21, 0x07, 0xf0, 0x01, 0x7f, 0x01, 0x02, + 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x70, 0xbf, 0x70, 0xbf, 0x62, + 0xd3, 0x00, 0x50, 0x04, 0x78, 0x08, 0x5c, 0x56, 0x52, 0x32, 0x18, + 0x78, 0xdf, 0xf8, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x91, 0x99, + 0x70, 0xbf, 0x18, 0x08, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0xa3, + 0x62, 0xd3, 0x00, 0x54, 0x60, 0x62, 0xd3, 0x00, 0x52, 0xa2, 0x62, + 0xd3, 0x00, 0x54, 0x5f, 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x14, 0x00, 0x50, 0x04, 0x78, + 0x08, 0x9f, 0x0e, 0x39, 0x01, 0xb0, 0x04, 0x55, 0x14, 0x01, 0x18, + 0x78, 0xdf, 0xf3, 0x51, 0x14, 0x7f, 0x50, 0x04, 0x78, 0x08, 0x9e, + 0x3e, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x98, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0xd8, 0xd9, 0xda, 0xdb, 0xdf, 0x00, 0x01, + 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x70, 0xbf, 0x62, 0xd0, + 0x00, 0x62, 0xd3, 0x00, 0x57, 0x00, 0x56, 0x56, 0x00, 0x79, 0xdf, + 0xfb, 0x62, 0xd3, 0x00, 0x57, 0x03, 0x50, 0x03, 0x54, 0x57, 0x79, + 0xdf, 0xfc, 0x62, 0xd3, 0x00, 0x50, 0x32, 0x57, 0x03, 0x54, 0x5b, + 0x79, 0xdf, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x55, 0x0d, 0x19, 0x55, + 0x0e, 0x0a, 0x55, 0x0f, 0x0f, 0x55, 0x10, 0x0a, 0x55, 0x11, 0x03, + 0x55, 0x12, 0x32, 0x55, 0x22, 0x01, 0x55, 0x1f, 0x14, 0x43, 0x61, + 0x0d, 0x57, 0x00, 0x50, 0x02, 0x90, 0x95, 0x50, 0x01, 0xff, 0x98, + 0x29, 0x00, 0x60, 0xa9, 0x62, 0xa0, 0x28, 0x43, 0xa2, 0x04, 0x62, + 0xa3, 0x70, 0x43, 0x7a, 0x01, 0x43, 0xaa, 0x02, 0x43, 0xdf, 0x01, + 0x50, 0x01, 0x57, 0x09, 0x90, 0x20, 0x90, 0x55, 0x57, 0x01, 0x50, + 0xb3, 0x91, 0x44, 0x50, 0x01, 0x57, 0x0e, 0x90, 0x12, 0x90, 0x47, + 0x7f, 0x53, 0x22, 0xff, 0x67, 0x29, 0x00, 0x60, 0xa9, 0x51, 0x21, + 0x58, 0x20, 0x90, 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, 0x03, 0x53, + 0x21, 0x64, 0x64, 0x64, 0x64, 0x64, 0x29, 0x80, 0x60, 0xa1, 0x5b, + 0x78, 0x21, 0x0f, 0x29, 0x08, 0x74, 0x53, 0x20, 0x12, 0x22, 0x02, + 0x21, 0x5c, 0x50, 0x00, 0x53, 0x1d, 0x53, 0x23, 0x29, 0x01, 0x79, + 0xa0, 0x08, 0x64, 0x6b, 0x1d, 0x6b, 0x23, 0x8f, 0xf5, 0x60, 0xb5, + 0x51, 0x1d, 0x60, 0xb4, 0x7f, 0x50, 0x04, 0x78, 0x08, 0x90, 0x0f, + 0x90, 0x41, 0x18, 0x78, 0xdf, 0xf8, 0x7f, 0x02, 0x20, 0x02, 0x08, + 0x01, 0x80, 0x01, 0x20, 0x64, 0x5c, 0xff, 0xf4, 0x4b, 0x74, 0xff, + 0xf0, 0x7f, 0x62, 0xd0, 0x00, 0x53, 0x1d, 0x10, 0x5b, 0x64, 0x64, + 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x2a, 0x1d, 0x61, 0x01, 0x36, 0x1d, + 0xff, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x36, 0x1d, 0xff, 0x18, + 0xfe, 0xef, 0x5c, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, + 0x7f, 0x62, 0xd0, 0x00, 0x10, 0x73, 0x53, 0x1d, 0x71, 0x10, 0x5b, + 0xfe, 0xd9, 0x5c, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x70, 0xef, + 0x18, 0x64, 0x64, 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x22, 0x1d, 0x61, + 0x01, 0x36, 0x1d, 0xff, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, + 0xef, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, 0x1e, 0x50, 0x00, + 0x53, 0x1a, 0x53, 0x1b, 0x51, 0x1e, 0x5c, 0x62, 0xd3, 0x00, 0x52, + 0x24, 0x53, 0x1f, 0x43, 0xa0, 0x01, 0x51, 0x1f, 0x60, 0xfd, 0x41, + 0xa3, 0xdf, 0x51, 0x1e, 0x9f, 0x7a, 0x9f, 0x81, 0x58, 0x23, 0x55, + 0x1c, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x43, 0xb3, 0x01, + 0x51, 0x1c, 0xaf, 0xfd, 0x79, 0xdf, 0xee, 0x51, 0x1e, 0x9f, 0x5f, + 0x9f, 0x91, 0x43, 0xa3, 0x20, 0x41, 0xa0, 0xfe, 0x62, 0xfd, 0x00, + 0x50, 0xff, 0x4c, 0x1b, 0x14, 0x1b, 0x51, 0x20, 0x11, 0x08, 0xfe, + 0x66, 0x4c, 0x1a, 0x1c, 0x1a, 0xd0, 0x07, 0x55, 0x1a, 0x00, 0x55, + 0x1b, 0x00, 0x51, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x1b, + 0x54, 0xa3, 0x51, 0x1a, 0x54, 0xa2, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x08, 0x9f, 0x86, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x70, 0xbf, 0x62, + 0xd0, 0x00, 0x53, 0x29, 0x5a, 0x28, 0x55, 0x1e, 0x03, 0x62, 0xd3, + 0x00, 0x58, 0x1e, 0x56, 0x24, 0x80, 0x55, 0x2b, 0x08, 0x55, 0x2a, + 0x80, 0x51, 0x1e, 0x9f, 0x63, 0x51, 0x1e, 0x9f, 0x5f, 0x70, 0xbf, + 0x58, 0x1e, 0x62, 0xd3, 0x00, 0x51, 0x1b, 0x3a, 0x29, 0x51, 0x1a, + 0x1a, 0x28, 0xd0, 0x06, 0x51, 0x2a, 0x73, 0x25, 0x24, 0x68, 0x2a, + 0x26, 0x2a, 0x7f, 0x51, 0x2a, 0x2d, 0x24, 0x7a, 0x2b, 0xbf, 0xd6, + 0x7a, 0x1e, 0xdf, 0xc4, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x10, 0x7c, + 0x05, 0x28, 0x7c, 0x05, 0x05, 0x7c, 0x04, 0xe2, 0x7c, 0x04, 0xbf, + 0x20, 0x7c, 0x16, 0xb6, 0x62, 0xe3, 0x38, 0x10, 0x7c, 0x03, 0x7c, + 0x20, 0x43, 0x00, 0x08, 0x62, 0xd0, 0x00, 0x55, 0x2c, 0x08, 0x55, + 0x2d, 0x0b, 0x55, 0x2e, 0x03, 0x55, 0x30, 0x32, 0x55, 0x31, 0x02, + 0x10, 0x50, 0x00, 0x08, 0x50, 0x2c, 0x08, 0x50, 0x06, 0x08, 0x50, + 0x22, 0x08, 0x7c, 0x06, 0x7a, 0x38, 0xfc, 0x7c, 0x06, 0x2e, 0x7c, + 0x06, 0x6f, 0x20, 0x71, 0x10, 0x41, 0x04, 0xfc, 0x41, 0x05, 0xfc, + 0x71, 0x01, 0x10, 0x70, 0xcf, 0x7c, 0x08, 0x7f, 0x20, 0x62, 0xd0, + 0x00, 0x55, 0xc5, 0x00, 0x91, 0x23, 0x10, 0x7c, 0x08, 0x05, 0x20, + 0x93, 0xcb, 0x81, 0x17, 0x7c, 0x18, 0x71, 0x62, 0xe3, 0x38, 0x7c, + 0x11, 0x2d, 0x62, 0xd0, 0x00, 0x3c, 0xc0, 0x01, 0xb0, 0x09, 0x50, + 0x00, 0x08, 0x7c, 0x1a, 0xfe, 0x38, 0xff, 0x62, 0xe3, 0x38, 0x10, + 0x7c, 0x08, 0x43, 0x20, 0x39, 0x00, 0xa0, 0xa8, 0x62, 0xd0, 0x00, + 0x51, 0xbf, 0x11, 0x4a, 0x51, 0xbe, 0x19, 0x01, 0xd0, 0x12, 0x7c, + 0x16, 0xd4, 0x39, 0xe1, 0xa0, 0x16, 0x62, 0xd0, 0x00, 0x76, 0xbf, + 0x0e, 0xbe, 0x00, 0x80, 0x0c, 0x62, 0xd0, 0x00, 0x55, 0xbf, 0x00, + 0x55, 0xbe, 0x00, 0x90, 0xcc, 0x62, 0xd0, 0x00, 0x3c, 0xcd, 0xf0, + 0xd0, 0x03, 0x76, 0xcd, 0x62, 0xd0, 0x00, 0x51, 0x31, 0x21, 0x7f, + 0x53, 0x79, 0x51, 0xcd, 0x3a, 0x79, 0xb0, 0xad, 0x7c, 0x16, 0xd4, + 0x62, 0xd0, 0x00, 0x53, 0xce, 0x3c, 0xce, 0xe1, 0xa0, 0x51, 0x3c, + 0xca, 0x00, 0xb0, 0x06, 0x7c, 0x1d, 0x14, 0x80, 0x25, 0x62, 0xd0, + 0x00, 0x55, 0xb7, 0x00, 0x55, 0xb6, 0x00, 0x55, 0xb9, 0x00, 0x55, + 0xb8, 0x00, 0x51, 0xce, 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, + 0xb6, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd5, 0x50, 0x08, 0x3f, + 0x78, 0x62, 0xd0, 0x00, 0x3c, 0xc6, 0x01, 0xb0, 0x04, 0x55, 0xc8, + 0x01, 0x62, 0xd0, 0x00, 0x55, 0xc6, 0x00, 0x55, 0xc1, 0x01, 0x7c, + 0x17, 0x5a, 0x62, 0xd0, 0x00, 0x55, 0xc3, 0x01, 0x55, 0xc4, 0x03, + 0x80, 0x50, 0x62, 0xd0, 0x00, 0x55, 0xcd, 0x00, 0x80, 0x48, 0x62, + 0xd0, 0x00, 0x55, 0xbf, 0x00, 0x55, 0xbe, 0x00, 0x55, 0xc8, 0x00, + 0x55, 0xc9, 0x00, 0x55, 0xc1, 0x00, 0x3c, 0xc3, 0x01, 0xb0, 0x31, + 0x7a, 0xc4, 0x3c, 0xc4, 0x00, 0xb0, 0x2a, 0x7c, 0x17, 0xea, 0x62, + 0xd0, 0x00, 0x55, 0xc3, 0x00, 0x3c, 0xca, 0x00, 0xb0, 0x06, 0x7c, + 0x1d, 0x14, 0x80, 0x11, 0x62, 0xd0, 0x00, 0x51, 0xce, 0x53, 0x78, + 0x55, 0x79, 0x00, 0x06, 0x78, 0xb6, 0x7c, 0x1d, 0x4c, 0x62, 0xd0, + 0x00, 0x55, 0xcd, 0x00, 0x7c, 0x19, 0x32, 0x8e, 0xe9, 0x8f, 0xff, + 0x10, 0x4f, 0x38, 0x24, 0x62, 0xd0, 0x00, 0x3c, 0xcb, 0x00, 0xb0, + 0x05, 0x51, 0xaa, 0x53, 0x24, 0x10, 0x50, 0x00, 0x7c, 0x09, 0xb9, + 0x20, 0x56, 0x19, 0x00, 0x81, 0x2c, 0x56, 0x00, 0x00, 0x81, 0x20, + 0x62, 0xd0, 0x00, 0x3c, 0xcb, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, + 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, 0xaa, 0x7c, 0x1c, 0x86, 0x52, + 0x00, 0x53, 0x76, 0x55, 0x77, 0x00, 0x06, 0x76, 0x24, 0x7c, 0x1d, + 0x08, 0x10, 0x52, 0x00, 0x7c, 0x09, 0xb9, 0x20, 0x10, 0x7c, 0x06, + 0x64, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, + 0x00, 0xb0, 0x3c, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, 0x9d, 0x51, 0x77, + 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x7c, + 0x1c, 0x21, 0x38, 0xfc, 0x51, 0x71, 0x53, 0x76, 0x51, 0x70, 0x53, + 0x77, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x77, 0x08, 0x51, + 0x76, 0x08, 0x7c, 0x1b, 0xdd, 0x18, 0x53, 0x76, 0x18, 0x53, 0x77, + 0x38, 0xfe, 0x7c, 0x1c, 0x79, 0x80, 0x63, 0x3d, 0x00, 0x01, 0xb0, + 0x2e, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, 0x9d, 0x65, + 0x76, 0x6b, 0x77, 0x65, 0x76, 0x6b, 0x77, 0x50, 0x00, 0x08, 0x50, + 0x05, 0x08, 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, 0x7c, 0x1b, 0xdd, + 0x18, 0x53, 0x76, 0x18, 0x53, 0x77, 0x38, 0xfe, 0x7c, 0x1c, 0x79, + 0x80, 0x31, 0x3d, 0x00, 0x02, 0xb0, 0x2c, 0x62, 0xd0, 0x00, 0x7c, + 0x1c, 0x51, 0x7c, 0x1c, 0x9d, 0x65, 0x76, 0x6b, 0x77, 0x65, 0x76, + 0x6b, 0x77, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x77, 0x08, + 0x51, 0x76, 0x08, 0x7c, 0x1b, 0xdd, 0x18, 0x53, 0x76, 0x18, 0x53, + 0x77, 0x38, 0xfe, 0x7c, 0x1c, 0x79, 0x62, 0xd0, 0x00, 0x55, 0x79, + 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, 0x00, 0x53, 0x76, 0x50, + 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, + 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x7c, 0x1c, 0xbe, 0x53, 0x79, 0x52, + 0x19, 0x53, 0x76, 0x55, 0x77, 0x00, 0x65, 0x76, 0x6b, 0x77, 0x7c, + 0x1c, 0xe2, 0x53, 0x79, 0x7c, 0x1d, 0x34, 0x06, 0x76, 0xa2, 0x0e, + 0x77, 0x00, 0x51, 0x77, 0x7c, 0x1c, 0x92, 0x7c, 0x1c, 0x79, 0x77, + 0x00, 0x3d, 0x00, 0x04, 0xce, 0xdd, 0x77, 0x19, 0x3d, 0x19, 0x03, + 0xce, 0xd1, 0x56, 0x00, 0x00, 0x80, 0xd0, 0x62, 0xd0, 0x00, 0x55, + 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, 0x00, 0x53, 0x76, + 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, + 0x08, 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x7c, 0x1c, 0xbe, 0x60, 0xd4, + 0x3e, 0x78, 0x54, 0x1a, 0x3e, 0x78, 0x54, 0x1b, 0x5a, 0x78, 0x06, + 0x78, 0x03, 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1c, 0x21, 0x38, + 0xfc, 0x7c, 0x1c, 0xbe, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1c, 0x3e, + 0x78, 0x54, 0x1d, 0x5a, 0x78, 0x06, 0x78, 0x05, 0x52, 0x00, 0x53, + 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, + 0x06, 0x08, 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x7c, 0x1c, 0xbe, 0x60, + 0xd4, 0x3e, 0x78, 0x54, 0x1e, 0x3e, 0x78, 0x54, 0x1f, 0x50, 0x03, + 0x08, 0x5a, 0x78, 0x06, 0x78, 0x1a, 0x08, 0x51, 0x78, 0x08, 0x7c, + 0x1a, 0x33, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, 0x78, 0x54, 0x21, + 0x51, 0x79, 0x54, 0x20, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, 0xb3, 0x7c, + 0x1c, 0xfd, 0x06, 0x78, 0x5f, 0x7c, 0x1c, 0xed, 0x7c, 0x1c, 0x51, + 0x51, 0x78, 0x01, 0x8a, 0x7c, 0x1c, 0xd1, 0x51, 0x78, 0x01, 0x92, + 0x7c, 0x1c, 0xd1, 0x06, 0x78, 0x9a, 0x7c, 0x1c, 0xed, 0x7c, 0x1c, + 0x51, 0x51, 0x78, 0x01, 0x7a, 0x7c, 0x1c, 0xd1, 0x06, 0x78, 0x82, + 0x7c, 0x1c, 0xed, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xcf, 0x2d, 0x3d, + 0x00, 0x00, 0xb0, 0x09, 0x62, 0xd0, 0x00, 0x47, 0xc5, 0x0e, 0xa0, + 0x0e, 0x3d, 0x00, 0x03, 0xb0, 0x75, 0x62, 0xd0, 0x00, 0x47, 0xc5, + 0x07, 0xb0, 0x6d, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, + 0xb3, 0x53, 0x77, 0x7c, 0x1d, 0x40, 0x53, 0x74, 0x06, 0x78, 0x5f, + 0x7c, 0x1c, 0x86, 0x3e, 0x78, 0x12, 0x74, 0x51, 0x79, 0x1a, 0x75, + 0xd0, 0x2c, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0x5f, 0x53, 0x72, + 0x51, 0x79, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x72, 0x53, 0x73, 0x3e, + 0x72, 0x53, 0x72, 0x06, 0x78, 0xa2, 0x7c, 0x1c, 0x86, 0x3e, 0x78, + 0x12, 0x72, 0x54, 0x23, 0x51, 0x79, 0x1a, 0x73, 0x54, 0x22, 0x80, + 0x07, 0x56, 0x23, 0x00, 0x56, 0x22, 0x00, 0x62, 0xd0, 0x00, 0x51, + 0x74, 0x03, 0x23, 0x53, 0x78, 0x51, 0x75, 0x0b, 0x22, 0x53, 0x79, + 0x51, 0x77, 0x60, 0xd5, 0x51, 0x79, 0x3f, 0x76, 0x51, 0x78, 0x3f, + 0x76, 0x38, 0xdc, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x22, 0x10, 0x57, + 0x30, 0x50, 0x00, 0x7c, 0x0a, 0x38, 0x20, 0x62, 0xd0, 0x00, 0x3c, + 0xcb, 0x01, 0xb0, 0x13, 0x51, 0x24, 0x53, 0x32, 0x51, 0x25, 0x53, + 0x33, 0x51, 0x26, 0x53, 0x34, 0x51, 0x27, 0x53, 0x35, 0x80, 0x14, + 0x62, 0xd0, 0x00, 0x51, 0xaa, 0x53, 0x24, 0x51, 0xab, 0x53, 0x25, + 0x51, 0xac, 0x53, 0x26, 0x51, 0xad, 0x53, 0x27, 0x10, 0x50, 0x00, + 0x7c, 0x09, 0xb9, 0x20, 0x56, 0x19, 0x00, 0x81, 0x2c, 0x56, 0x00, + 0x00, 0x81, 0x20, 0x62, 0xd0, 0x00, 0x3c, 0xcb, 0x00, 0xb0, 0x1b, + 0x52, 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, 0xaa, 0x7c, + 0x1c, 0x86, 0x52, 0x00, 0x53, 0x76, 0x55, 0x77, 0x00, 0x06, 0x76, + 0x24, 0x7c, 0x1d, 0x08, 0x10, 0x52, 0x00, 0x7c, 0x09, 0xb9, 0x20, + 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, + 0xee, 0x3d, 0x00, 0x00, 0xb0, 0x3c, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, + 0x9d, 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, + 0x03, 0x08, 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x51, 0x71, 0x53, 0x76, + 0x51, 0x70, 0x53, 0x77, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, + 0x77, 0x08, 0x51, 0x76, 0x08, 0x7c, 0x1b, 0xdd, 0x18, 0x53, 0x76, + 0x18, 0x53, 0x77, 0x38, 0xfe, 0x7c, 0x1c, 0x79, 0x80, 0x63, 0x3d, + 0x00, 0x01, 0xb0, 0x2e, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x51, 0x7c, + 0x1c, 0x9d, 0x65, 0x76, 0x6b, 0x77, 0x65, 0x76, 0x6b, 0x77, 0x50, + 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, + 0x7c, 0x1b, 0xdd, 0x18, 0x53, 0x76, 0x18, 0x53, 0x77, 0x38, 0xfe, + 0x7c, 0x1c, 0x79, 0x80, 0x31, 0x3d, 0x00, 0x02, 0xb0, 0x2c, 0x62, + 0xd0, 0x00, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, 0x9d, 0x65, 0x76, 0x6b, + 0x77, 0x65, 0x76, 0x6b, 0x77, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, + 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, 0x7c, 0x1b, 0xdd, 0x18, 0x53, + 0x76, 0x18, 0x53, 0x77, 0x38, 0xfe, 0x7c, 0x1c, 0x79, 0x62, 0xd0, + 0x00, 0x55, 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, 0x00, + 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, + 0x50, 0x06, 0x08, 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x7c, 0x1c, 0xbe, + 0x53, 0x79, 0x52, 0x19, 0x53, 0x76, 0x55, 0x77, 0x00, 0x65, 0x76, + 0x6b, 0x77, 0x7c, 0x1c, 0xe2, 0x53, 0x79, 0x7c, 0x1d, 0x34, 0x06, + 0x76, 0xa2, 0x0e, 0x77, 0x00, 0x51, 0x77, 0x7c, 0x1c, 0x92, 0x7c, + 0x1c, 0x79, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xce, 0xdd, 0x77, 0x19, + 0x3d, 0x19, 0x03, 0xce, 0xd1, 0x56, 0x00, 0x00, 0x80, 0xd0, 0x62, + 0xd0, 0x00, 0x55, 0x79, 0x03, 0x5a, 0x78, 0x06, 0x78, 0x01, 0x52, + 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x7c, 0x1c, + 0xbe, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1a, 0x3e, 0x78, 0x54, 0x1b, + 0x5a, 0x78, 0x06, 0x78, 0x03, 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, + 0x08, 0x51, 0x76, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, + 0x1c, 0x21, 0x38, 0xfc, 0x7c, 0x1c, 0xbe, 0x60, 0xd4, 0x3e, 0x78, + 0x54, 0x1c, 0x3e, 0x78, 0x54, 0x1d, 0x5a, 0x78, 0x06, 0x78, 0x05, + 0x52, 0x00, 0x53, 0x76, 0x50, 0x00, 0x08, 0x51, 0x76, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x1c, 0x21, 0x38, 0xfc, 0x7c, + 0x1c, 0xbe, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x1e, 0x3e, 0x78, 0x54, + 0x1f, 0x50, 0x03, 0x08, 0x5a, 0x78, 0x06, 0x78, 0x1a, 0x08, 0x51, + 0x78, 0x08, 0x7c, 0x1a, 0x33, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, + 0x78, 0x54, 0x21, 0x51, 0x79, 0x54, 0x20, 0x7c, 0x1c, 0x51, 0x7c, + 0x1c, 0xb3, 0x7c, 0x1c, 0xfd, 0x06, 0x78, 0x5f, 0x7c, 0x1c, 0xed, + 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0x8a, 0x7c, 0x1c, 0xd1, 0x51, + 0x78, 0x01, 0x92, 0x7c, 0x1c, 0xd1, 0x06, 0x78, 0x9a, 0x7c, 0x1c, + 0xed, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0x7a, 0x7c, 0x1c, 0xd1, + 0x06, 0x78, 0x82, 0x7c, 0x1c, 0xed, 0x77, 0x00, 0x3d, 0x00, 0x04, + 0xcf, 0x2d, 0x56, 0x00, 0x00, 0x80, 0x19, 0x7c, 0x1c, 0x5d, 0x06, + 0x78, 0x24, 0x7c, 0x1c, 0x86, 0x52, 0x00, 0x53, 0x76, 0x55, 0x77, + 0x00, 0x06, 0x76, 0x32, 0x7c, 0x1d, 0x08, 0x77, 0x00, 0x3d, 0x00, + 0x04, 0xcf, 0xe4, 0x38, 0xde, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x07, + 0x56, 0x02, 0x00, 0x62, 0xd0, 0x00, 0x55, 0xc2, 0x00, 0x3c, 0xcb, + 0x00, 0xb0, 0x05, 0x51, 0xaa, 0x53, 0x24, 0x10, 0x50, 0x00, 0x7c, + 0x09, 0xb9, 0x20, 0x56, 0x00, 0x00, 0x80, 0xda, 0x62, 0xd0, 0x00, + 0x3c, 0xcb, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x78, 0x55, 0x79, + 0x00, 0x06, 0x78, 0xaa, 0x7c, 0x1c, 0x86, 0x52, 0x00, 0x53, 0x76, + 0x55, 0x77, 0x00, 0x06, 0x76, 0x24, 0x7c, 0x1d, 0x08, 0x10, 0x52, + 0x00, 0x7c, 0x09, 0xb9, 0x20, 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, + 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, 0x00, 0xb0, 0x3c, + 0x7c, 0x1c, 0x51, 0x7c, 0x1c, 0x9d, 0x51, 0x77, 0x08, 0x51, 0x76, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x7c, 0x1c, 0x21, 0x38, + 0xfc, 0x51, 0x71, 0x53, 0x76, 0x51, 0x70, 0x53, 0x77, 0x50, 0x00, + 0x08, 0x50, 0x05, 0x08, 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, 0x7c, + 0x1b, 0xdd, 0x18, 0x53, 0x76, 0x18, 0x53, 0x77, 0x38, 0xfe, 0x7c, + 0x1c, 0x79, 0x80, 0x63, 0x3d, 0x00, 0x01, 0xb0, 0x2e, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, 0x9d, 0x65, 0x76, 0x6b, 0x77, + 0x65, 0x76, 0x6b, 0x77, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, + 0x77, 0x08, 0x51, 0x76, 0x08, 0x7c, 0x1b, 0xdd, 0x18, 0x53, 0x76, + 0x18, 0x53, 0x77, 0x38, 0xfe, 0x7c, 0x1c, 0x79, 0x80, 0x31, 0x3d, + 0x00, 0x02, 0xb0, 0x2c, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x51, 0x7c, + 0x1c, 0x9d, 0x65, 0x76, 0x6b, 0x77, 0x65, 0x76, 0x6b, 0x77, 0x50, + 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, + 0x7c, 0x1b, 0xdd, 0x18, 0x53, 0x76, 0x18, 0x53, 0x77, 0x38, 0xfe, + 0x7c, 0x1c, 0x79, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xcf, 0x23, 0x56, + 0x00, 0x00, 0x82, 0xb5, 0x62, 0xd0, 0x00, 0x3c, 0xc0, 0x02, 0xa0, + 0xb1, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0x5f, 0x7c, 0x1c, 0x68, + 0x06, 0x78, 0xa2, 0x7c, 0x1c, 0x86, 0x3e, 0x78, 0x53, 0x78, 0x51, + 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x16, 0x7c, 0x1c, + 0x51, 0x51, 0x78, 0x01, 0x5f, 0x7c, 0x1c, 0x68, 0x06, 0x78, 0xa2, + 0x7c, 0x1c, 0x86, 0x7c, 0x1d, 0x65, 0x80, 0x17, 0x62, 0xd0, 0x00, + 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0xa2, 0x7c, 0x1c, 0x68, 0x06, + 0x78, 0x5f, 0x7c, 0x1c, 0x86, 0x7c, 0x1d, 0x65, 0x50, 0x90, 0x13, + 0x04, 0x50, 0x01, 0x1b, 0x03, 0xc0, 0x57, 0x62, 0xd0, 0x00, 0x7c, + 0x1c, 0x51, 0x51, 0x78, 0x01, 0x9a, 0x7c, 0x1c, 0x68, 0x06, 0x78, + 0xa2, 0x7c, 0x1c, 0x86, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, 0x12, + 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x16, 0x7c, 0x1c, 0x51, 0x51, + 0x78, 0x01, 0x9a, 0x7c, 0x1c, 0x68, 0x06, 0x78, 0xa2, 0x7c, 0x1c, + 0x86, 0x7c, 0x1d, 0x58, 0x80, 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x1c, + 0x51, 0x51, 0x78, 0x01, 0xa2, 0x7c, 0x1c, 0x68, 0x06, 0x78, 0x9a, + 0x7c, 0x1c, 0x86, 0x7c, 0x1d, 0x58, 0x50, 0x90, 0x13, 0x06, 0x50, + 0x01, 0x1b, 0x05, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x76, 0xc2, 0x81, + 0xfb, 0x56, 0x01, 0x00, 0x80, 0x61, 0x62, 0xd0, 0x00, 0x7c, 0x1c, + 0x51, 0x51, 0x78, 0x01, 0xa2, 0x7c, 0x1c, 0x68, 0x06, 0x78, 0x92, + 0x7c, 0x1c, 0x86, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, 0x12, 0x78, + 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x12, 0x7c, 0x1c, 0x51, 0x7c, 0x1c, + 0x9d, 0x06, 0x76, 0x01, 0x0e, 0x77, 0x00, 0x7c, 0x1c, 0x79, 0x80, + 0x2d, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0xa2, + 0x7c, 0x1c, 0x68, 0x06, 0x78, 0x92, 0x7c, 0x1c, 0x86, 0x3e, 0x78, + 0x12, 0x76, 0x51, 0x79, 0x1a, 0x77, 0xd0, 0x10, 0x7c, 0x1c, 0x51, + 0x7c, 0x1c, 0x9d, 0x16, 0x76, 0x01, 0x1e, 0x77, 0x00, 0x7c, 0x1c, + 0x79, 0x77, 0x01, 0x3d, 0x01, 0x04, 0xcf, 0x9c, 0x62, 0xd0, 0x00, + 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0x82, 0x7c, 0x1c, 0x68, 0x06, + 0x78, 0x7a, 0x0e, 0x79, 0x00, 0x7c, 0x1c, 0x79, 0x7c, 0x1c, 0x51, + 0x51, 0x78, 0x01, 0x8a, 0x7c, 0x1c, 0x68, 0x06, 0x78, 0x82, 0x0e, + 0x79, 0x00, 0x7c, 0x1c, 0x79, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, + 0x92, 0x7c, 0x1c, 0x68, 0x06, 0x78, 0x8a, 0x0e, 0x79, 0x00, 0x7c, + 0x1c, 0x79, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0x9a, 0x7c, 0x1c, + 0x68, 0x06, 0x78, 0x92, 0x0e, 0x79, 0x00, 0x7c, 0x1c, 0x79, 0x7c, + 0x1c, 0x51, 0x51, 0x78, 0x01, 0xa2, 0x7c, 0x1c, 0x68, 0x06, 0x78, + 0x9a, 0x0e, 0x79, 0x00, 0x7c, 0x1c, 0x79, 0x7c, 0x1c, 0x51, 0x7c, + 0x1c, 0xb3, 0x53, 0x77, 0x7c, 0x1d, 0x40, 0x53, 0x74, 0x08, 0x51, + 0x75, 0x53, 0x73, 0x18, 0x53, 0x72, 0x65, 0x72, 0x6b, 0x73, 0x06, + 0x78, 0x92, 0x7c, 0x1c, 0x86, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x72, + 0x04, 0x78, 0x51, 0x73, 0x0c, 0x79, 0x51, 0x74, 0x04, 0x78, 0x51, + 0x75, 0x0c, 0x79, 0x70, 0xfb, 0x6e, 0x79, 0x6e, 0x78, 0x7c, 0x1d, + 0x21, 0x10, 0x52, 0x00, 0x7c, 0x06, 0xa1, 0x20, 0x62, 0xd0, 0x00, + 0x7c, 0x1c, 0x51, 0x06, 0x78, 0x67, 0x7c, 0x1c, 0x86, 0x3e, 0x78, + 0x53, 0x78, 0x50, 0x32, 0x12, 0x78, 0x50, 0x00, 0x1a, 0x79, 0xd0, + 0x1b, 0x55, 0x79, 0x01, 0x52, 0x00, 0xa0, 0x09, 0x62, 0xd0, 0x00, + 0x65, 0x79, 0x78, 0xbf, 0xf9, 0x62, 0xd0, 0x00, 0x51, 0xc5, 0x2a, + 0x79, 0x53, 0xc5, 0x80, 0x1b, 0x62, 0xd0, 0x00, 0x55, 0x79, 0x01, + 0x52, 0x00, 0xa0, 0x09, 0x62, 0xd0, 0x00, 0x65, 0x79, 0x78, 0xbf, + 0xf9, 0x62, 0xd0, 0x00, 0x51, 0x79, 0x73, 0x24, 0xc5, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x51, 0x51, 0x78, 0x01, 0xa2, 0x7c, 0x1c, 0x68, + 0x06, 0x78, 0x5f, 0x7c, 0x1c, 0x86, 0x3e, 0x78, 0x53, 0x78, 0x51, + 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x25, 0x52, 0x00, + 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, 0xba, 0x0e, 0x79, 0x00, + 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x7a, 0x78, 0x53, 0x77, 0x06, + 0x77, 0x01, 0x51, 0x79, 0x60, 0xd5, 0x51, 0x77, 0x3f, 0x78, 0x80, + 0x0a, 0x97, 0xcf, 0x40, 0x06, 0x78, 0xba, 0x7c, 0x1d, 0x4c, 0x97, + 0xc6, 0x40, 0x06, 0x78, 0xba, 0x97, 0xe9, 0x40, 0x50, 0x05, 0x3a, + 0x79, 0xd0, 0x41, 0x97, 0xab, 0x40, 0x51, 0x78, 0x01, 0x5f, 0x53, + 0x76, 0x51, 0x79, 0x09, 0x00, 0x53, 0x77, 0x06, 0x78, 0xa2, 0x97, + 0xce, 0x40, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x77, 0x7c, 0x1d, 0x40, + 0x02, 0x78, 0x53, 0x78, 0x51, 0x75, 0x0a, 0x79, 0x53, 0x79, 0x7c, + 0x1d, 0x21, 0x52, 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x06, 0x78, + 0xba, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd5, 0x50, 0x00, 0x3f, + 0x78, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xcd, 0x48, 0x62, 0xd0, 0x00, + 0x3c, 0xc0, 0x02, 0xb1, 0x73, 0x56, 0x00, 0x00, 0x81, 0x69, 0x3d, + 0x00, 0x00, 0xb0, 0x49, 0x62, 0xd0, 0x00, 0x97, 0x4f, 0x40, 0x51, + 0x78, 0x01, 0xa2, 0x97, 0x5f, 0x40, 0x51, 0x77, 0x08, 0x51, 0x76, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x7c, 0x1c, 0x21, 0x38, + 0xfc, 0x51, 0x71, 0x53, 0x76, 0x51, 0x70, 0x53, 0x77, 0x50, 0x00, + 0x08, 0x50, 0x03, 0x08, 0x51, 0x77, 0x08, 0x51, 0x76, 0x08, 0x7c, + 0x1b, 0xdd, 0x18, 0x53, 0x76, 0x18, 0x53, 0x77, 0x38, 0xfe, 0x06, + 0x78, 0x3e, 0x0e, 0x79, 0x00, 0x97, 0x37, 0x40, 0x80, 0xa0, 0x3d, + 0x00, 0x01, 0xb0, 0x3e, 0x62, 0xd0, 0x00, 0x97, 0x02, 0x40, 0x51, + 0x78, 0x01, 0xa2, 0x97, 0x12, 0x40, 0x51, 0x77, 0x08, 0x51, 0x76, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x7c, 0x1c, 0x21, 0x38, + 0xfc, 0x51, 0x71, 0x53, 0x76, 0x51, 0x70, 0x53, 0x77, 0x70, 0xfb, + 0x6e, 0x77, 0x6e, 0x76, 0x70, 0xfb, 0x6e, 0x77, 0x6e, 0x76, 0x06, + 0x78, 0x3e, 0x0e, 0x79, 0x00, 0x96, 0xf5, 0x40, 0x80, 0x5e, 0x3d, + 0x00, 0x02, 0xb0, 0x3e, 0x62, 0xd0, 0x00, 0x96, 0xc0, 0x40, 0x51, + 0x78, 0x01, 0xa2, 0x96, 0xd0, 0x40, 0x51, 0x77, 0x08, 0x51, 0x76, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x7c, 0x1c, 0x21, 0x38, + 0xfc, 0x51, 0x71, 0x53, 0x76, 0x51, 0x70, 0x53, 0x77, 0x70, 0xfb, + 0x6e, 0x77, 0x6e, 0x76, 0x70, 0xfb, 0x6e, 0x77, 0x6e, 0x76, 0x06, + 0x78, 0x3e, 0x0e, 0x79, 0x00, 0x96, 0xb3, 0x40, 0x80, 0x1c, 0x3d, + 0x00, 0x03, 0xb0, 0x17, 0x62, 0xd0, 0x00, 0x96, 0x7e, 0x40, 0x51, + 0x78, 0x01, 0xa2, 0x96, 0x8e, 0x40, 0x06, 0x78, 0x3e, 0x0e, 0x79, + 0x00, 0x96, 0x96, 0x40, 0x62, 0xd0, 0x00, 0x96, 0x68, 0x40, 0x51, + 0x78, 0x01, 0x5f, 0x96, 0x78, 0x40, 0x06, 0x78, 0x46, 0x0e, 0x79, + 0x00, 0x96, 0x80, 0x40, 0x96, 0x55, 0x40, 0x51, 0x78, 0x01, 0x5f, + 0x96, 0x65, 0x40, 0x51, 0x78, 0x01, 0xa2, 0x53, 0x74, 0x51, 0x79, + 0x97, 0x71, 0x40, 0x51, 0x76, 0x12, 0x74, 0x51, 0x77, 0x1a, 0x75, + 0xd0, 0x2b, 0x97, 0x19, 0x40, 0x51, 0x76, 0x01, 0x5f, 0x53, 0x74, + 0x51, 0x77, 0x97, 0x59, 0x40, 0x06, 0x76, 0xa2, 0x0e, 0x77, 0x00, + 0x51, 0x77, 0x60, 0xd4, 0x3e, 0x76, 0x53, 0x77, 0x3e, 0x76, 0x12, + 0x74, 0x54, 0x04, 0x51, 0x77, 0x1a, 0x75, 0x54, 0x03, 0x80, 0x07, + 0x56, 0x04, 0x00, 0x56, 0x03, 0x00, 0x62, 0xd0, 0x00, 0x06, 0x78, + 0x36, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd5, 0x52, 0x03, 0x3f, + 0x78, 0x52, 0x04, 0x3f, 0x78, 0x77, 0x00, 0x3d, 0x00, 0x04, 0xce, + 0x94, 0x62, 0xd0, 0x00, 0x3c, 0xc0, 0x02, 0xa0, 0x1e, 0x3c, 0xc2, + 0x00, 0xa0, 0x19, 0x55, 0xc2, 0x00, 0x55, 0xcd, 0x00, 0x50, 0x75, + 0x08, 0x50, 0x30, 0x08, 0x90, 0x0e, 0x38, 0xfe, 0x7c, 0x0c, 0x09, + 0x10, 0x7c, 0x08, 0x5d, 0x20, 0x38, 0xf9, 0x20, 0x7f, 0x10, 0x4f, + 0x80, 0x02, 0x40, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, 0x78, 0x52, + 0xfb, 0x53, 0x79, 0x51, 0x78, 0x11, 0x01, 0x54, 0xfc, 0x51, 0x79, + 0x19, 0x00, 0x54, 0xfb, 0x3c, 0x79, 0x00, 0xbf, 0xe4, 0x3c, 0x78, + 0x00, 0xbf, 0xdf, 0x20, 0x7f, 0x10, 0x7c, 0x05, 0x28, 0x7c, 0x05, + 0x05, 0x7c, 0x04, 0xe2, 0x7c, 0x04, 0xbf, 0x20, 0x7f, 0x10, 0x7c, + 0x05, 0x24, 0x7c, 0x05, 0x01, 0x7c, 0x04, 0xde, 0x7c, 0x04, 0xbb, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x05, 0x62, 0xd0, 0x00, 0x51, 0x68, + 0x54, 0x02, 0x51, 0x67, 0x54, 0x01, 0x56, 0x04, 0x00, 0x56, 0x00, + 0x00, 0x56, 0x03, 0x00, 0x80, 0x52, 0x62, 0xd0, 0x00, 0x95, 0x5e, + 0x40, 0x06, 0x78, 0x67, 0x95, 0x8d, 0x40, 0x3e, 0x78, 0x53, 0x78, + 0x50, 0x32, 0x12, 0x78, 0x50, 0x00, 0x1a, 0x79, 0xd0, 0x03, 0x77, + 0x03, 0x62, 0xd0, 0x00, 0x95, 0x42, 0x40, 0x06, 0x78, 0x67, 0x95, + 0x71, 0x40, 0x3e, 0x78, 0x53, 0x78, 0x52, 0x02, 0x12, 0x78, 0x52, + 0x01, 0x1a, 0x79, 0xd0, 0x1a, 0x95, 0x2b, 0x40, 0x06, 0x78, 0x67, + 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x01, + 0x3e, 0x78, 0x54, 0x02, 0x52, 0x00, 0x54, 0x04, 0x77, 0x00, 0x3d, + 0x00, 0x04, 0xcf, 0xab, 0x50, 0x01, 0x3b, 0x03, 0xd0, 0x08, 0x62, + 0xd0, 0x00, 0x50, 0xe1, 0x80, 0x06, 0x52, 0x04, 0x62, 0xd0, 0x00, + 0x38, 0xfb, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x62, 0xd0, 0x00, + 0x3c, 0xc2, 0x00, 0xa0, 0x07, 0x51, 0x2c, 0x29, 0x10, 0x53, 0x2c, + 0x62, 0xd0, 0x00, 0x3c, 0xc3, 0x00, 0xb0, 0x73, 0x70, 0xfe, 0x51, + 0xce, 0x01, 0x01, 0x53, 0x2c, 0x71, 0x01, 0x62, 0xe3, 0x38, 0x10, + 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, + 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, + 0x20, 0x53, 0x79, 0x47, 0x79, 0x20, 0xa0, 0x03, 0x80, 0x12, 0x50, + 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0xe4, 0x38, 0xfe, 0x77, 0x00, + 0x3d, 0x00, 0xc8, 0xcf, 0xdf, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, + 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x79, 0x47, 0x79, + 0x20, 0xb0, 0x03, 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, + 0x9e, 0xbd, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, 0x1e, 0xcf, 0xdf, + 0x62, 0xd0, 0x00, 0x51, 0x2c, 0x29, 0x08, 0x53, 0x2c, 0x43, 0x00, + 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x62, 0xd0, + 0x00, 0x3c, 0xc2, 0x00, 0xa0, 0x07, 0x51, 0x2c, 0x29, 0x10, 0x53, + 0x2c, 0x62, 0xd0, 0x00, 0x3c, 0xc3, 0x01, 0xb0, 0x6a, 0x70, 0xfe, + 0x51, 0xce, 0x01, 0x09, 0x53, 0x2c, 0x71, 0x01, 0x62, 0xe3, 0x38, + 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, + 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, + 0x00, 0x20, 0x53, 0x79, 0x47, 0x79, 0x20, 0xa0, 0x03, 0x80, 0x12, + 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0x54, 0x38, 0xfe, 0x77, + 0x00, 0x3d, 0x00, 0xc8, 0xcf, 0xdf, 0x56, 0x00, 0x00, 0x80, 0x1e, + 0x10, 0x7c, 0x06, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x79, 0x47, + 0x79, 0x20, 0xb0, 0x03, 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, + 0x08, 0x9e, 0x2d, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, 0x1e, 0xcf, + 0xdf, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, + 0x02, 0x62, 0xd0, 0x00, 0x51, 0x2c, 0x54, 0x00, 0x51, 0x2f, 0x54, + 0x01, 0x3d, 0x00, 0x01, 0xb0, 0x1d, 0x3c, 0xca, 0x00, 0xb0, 0x18, + 0x55, 0xc6, 0x00, 0x55, 0xb7, 0x00, 0x55, 0xb6, 0x00, 0x55, 0xb9, + 0x00, 0x55, 0xb8, 0x00, 0x55, 0x2c, 0x00, 0x55, 0xca, 0x01, 0x80, + 0x8e, 0x3d, 0x00, 0x02, 0xb0, 0x1a, 0x62, 0xd0, 0x00, 0x3c, 0xca, + 0x01, 0xb0, 0x12, 0x55, 0xc6, 0x01, 0x55, 0xc7, 0x00, 0x94, 0x5e, + 0x40, 0x55, 0x2c, 0x00, 0x55, 0xca, 0x00, 0x80, 0x70, 0x52, 0x00, + 0x21, 0xf0, 0x39, 0x40, 0xb0, 0x0c, 0x62, 0xd0, 0x00, 0x55, 0xc0, + 0x02, 0x55, 0x2c, 0x00, 0x80, 0x5d, 0x52, 0x00, 0x21, 0xf0, 0x39, + 0x50, 0xb0, 0x55, 0x3d, 0x01, 0x01, 0xb0, 0x15, 0x62, 0xd0, 0x00, + 0x55, 0xcb, 0x01, 0x7c, 0x0e, 0xb8, 0x62, 0xd0, 0x00, 0x51, 0x31, + 0x29, 0x80, 0x53, 0x31, 0x80, 0x27, 0x3d, 0x01, 0x02, 0xb0, 0x16, + 0x62, 0xd0, 0x00, 0x51, 0x30, 0x53, 0x52, 0x51, 0x30, 0x53, 0x53, + 0x51, 0x30, 0x53, 0x54, 0x51, 0x30, 0x53, 0x55, 0x80, 0x0d, 0x3d, + 0x01, 0x04, 0xb0, 0x08, 0x62, 0xd0, 0x00, 0x51, 0x30, 0x53, 0x0d, + 0x62, 0xd0, 0x00, 0x55, 0x2d, 0x0b, 0x55, 0x2e, 0x03, 0x56, 0x00, + 0x00, 0x56, 0x01, 0x00, 0x55, 0x2f, 0x00, 0x55, 0x2c, 0x00, 0x38, + 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x04, 0x62, 0xd0, 0x00, 0x3c, + 0xc6, 0x00, 0xa0, 0x13, 0x9d, 0x76, 0x62, 0xd0, 0x00, 0x3c, 0xc7, + 0x00, 0xb0, 0xe8, 0x55, 0xc7, 0x01, 0x7c, 0x0c, 0x09, 0x80, 0xe0, + 0x62, 0xd0, 0x00, 0x3c, 0xc9, 0x00, 0xb0, 0x27, 0x3c, 0xc8, 0x01, + 0xb0, 0x22, 0x51, 0xce, 0x53, 0x78, 0x55, 0x79, 0x00, 0x65, 0x78, + 0x6b, 0x79, 0x06, 0x78, 0x67, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, + 0xd4, 0x3e, 0x78, 0x54, 0x01, 0x3e, 0x78, 0x54, 0x02, 0x51, 0xce, + 0x54, 0x00, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0xb6, 0xd0, 0x08, + 0x10, 0x7c, 0x05, 0x28, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x05, 0x24, + 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0xb7, 0xd0, 0x08, 0x10, + 0x7c, 0x05, 0x05, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x05, 0x01, 0x20, + 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0xb8, 0xd0, 0x08, 0x10, 0x7c, + 0x04, 0xe2, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0xde, 0x20, 0x62, + 0xd0, 0x00, 0x50, 0x01, 0x3a, 0xb9, 0xd0, 0x08, 0x10, 0x7c, 0x04, + 0xbf, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0xbb, 0x20, 0x7c, 0x11, + 0x2d, 0x62, 0xd0, 0x00, 0x3c, 0xc9, 0x00, 0xb0, 0x53, 0x3c, 0xc8, + 0x01, 0xb0, 0x4e, 0x3c, 0xc2, 0x00, 0xb0, 0x40, 0x92, 0x68, 0x40, + 0x51, 0x78, 0x01, 0x5f, 0x92, 0x78, 0x40, 0x06, 0x76, 0x32, 0x0e, + 0x77, 0x00, 0x06, 0x78, 0xa2, 0x92, 0x8a, 0x40, 0x3e, 0x78, 0x53, + 0x78, 0x51, 0x76, 0x12, 0x78, 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x1c, + 0x92, 0x44, 0x40, 0x51, 0x78, 0x01, 0xa2, 0x92, 0x54, 0x40, 0x52, + 0x02, 0x14, 0x76, 0x52, 0x01, 0x1c, 0x77, 0x06, 0x78, 0x5f, 0x0e, + 0x79, 0x00, 0x92, 0x54, 0x40, 0x62, 0xd0, 0x00, 0x55, 0xc9, 0x01, + 0x55, 0xc8, 0x00, 0x38, 0xfc, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x03, + 0x56, 0x02, 0x00, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x3e, + 0x62, 0xd0, 0x00, 0x92, 0x0a, 0x40, 0x52, 0xfc, 0x04, 0x78, 0x52, + 0xfb, 0x0c, 0x79, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x53, 0x79, + 0x3e, 0x78, 0x53, 0x78, 0x52, 0x02, 0x12, 0x78, 0x52, 0x01, 0x1a, + 0x79, 0xd0, 0x18, 0x91, 0xe9, 0x40, 0x52, 0xfc, 0x04, 0x78, 0x52, + 0xfb, 0x0c, 0x79, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x54, 0x01, + 0x3e, 0x78, 0x54, 0x02, 0x77, 0x00, 0x52, 0x00, 0x3b, 0xfa, 0xcf, + 0xbe, 0x62, 0xd0, 0x00, 0x52, 0x02, 0x53, 0x78, 0x52, 0x01, 0x53, + 0x79, 0x38, 0xfd, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x71, 0x10, + 0x41, 0x01, 0xf7, 0x43, 0x00, 0x08, 0x70, 0xcf, 0x43, 0x00, 0x08, + 0x50, 0x00, 0x08, 0x50, 0x64, 0x08, 0x9b, 0xe1, 0x71, 0x10, 0x41, + 0x01, 0xf7, 0x41, 0x00, 0xf7, 0x70, 0xcf, 0x43, 0x00, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x64, 0x08, 0x9b, 0xcc, 0x38, 0xfc, 0x5d, 0x00, + 0x62, 0xd0, 0x00, 0x53, 0x79, 0x26, 0x79, 0x08, 0x3c, 0x79, 0x08, + 0xb0, 0x09, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x07, 0x56, + 0x01, 0x01, 0x56, 0x00, 0x00, 0x52, 0x01, 0x62, 0xd0, 0x00, 0x53, + 0xc0, 0x71, 0x10, 0x43, 0x00, 0x08, 0x41, 0x01, 0xf7, 0x70, 0xcf, + 0x3c, 0xc0, 0x00, 0xb0, 0x04, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, + 0x7f, 0x10, 0x4f, 0x38, 0x01, 0x62, 0xe3, 0x38, 0x10, 0x50, 0x02, + 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x91, 0x20, + 0x10, 0x50, 0xff, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0x08, 0x08, + 0x50, 0x00, 0x08, 0x50, 0xa2, 0x08, 0x7c, 0x04, 0x76, 0x38, 0xfd, + 0x20, 0x56, 0x00, 0x00, 0x80, 0x6e, 0x62, 0xd0, 0x00, 0x91, 0x1d, + 0x40, 0x51, 0x78, 0x01, 0x5f, 0x91, 0x2d, 0x40, 0x06, 0x78, 0xa2, + 0x91, 0x45, 0x40, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, 0x12, 0x78, + 0x51, 0x77, 0x1a, 0x79, 0xd0, 0x3d, 0x90, 0xff, 0x40, 0x51, 0x78, + 0x01, 0x5f, 0x91, 0x0f, 0x40, 0x06, 0x78, 0xa2, 0x91, 0x27, 0x40, + 0x92, 0x10, 0x40, 0x51, 0x79, 0x10, 0x7c, 0x03, 0x91, 0x20, 0x62, + 0xd0, 0x00, 0x90, 0xe2, 0x40, 0x51, 0x78, 0x01, 0x5f, 0x90, 0xf2, + 0x40, 0x06, 0x78, 0xa2, 0x91, 0x0a, 0x40, 0x91, 0xf3, 0x40, 0x26, + 0x79, 0x00, 0x51, 0x78, 0x10, 0x7c, 0x03, 0x91, 0x20, 0x80, 0x0f, + 0x10, 0x50, 0x00, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0x00, 0x7c, + 0x03, 0x91, 0x20, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x8f, 0x10, + 0x50, 0x00, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0x01, 0x7c, 0x03, + 0x91, 0x20, 0x10, 0x50, 0x00, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, + 0x01, 0x7c, 0x03, 0x91, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x91, + 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x91, 0x7c, 0x04, 0x6d, 0x20, + 0x50, 0x13, 0x08, 0x50, 0x88, 0x08, 0x9a, 0xb8, 0x38, 0xfe, 0x38, + 0xff, 0x20, 0x7f, 0x7f, 0x10, 0x4f, 0x7c, 0x1b, 0xe8, 0x20, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, + 0x00, 0x53, 0x70, 0x53, 0x71, 0x55, 0x6f, 0x10, 0x66, 0xfc, 0x6c, + 0xfb, 0x6b, 0x70, 0x6b, 0x71, 0x51, 0x70, 0x1b, 0xfa, 0x51, 0x71, + 0x1b, 0xf9, 0xc0, 0x09, 0x53, 0x71, 0x52, 0xfa, 0x1c, 0x70, 0x77, + 0xfc, 0x7a, 0x6f, 0xbf, 0xe3, 0x51, 0x70, 0x54, 0xfa, 0x51, 0x71, + 0x54, 0xf9, 0x18, 0x60, 0xd0, 0x7f, 0x10, 0x4f, 0x5d, 0xd0, 0x08, + 0x62, 0xd0, 0x00, 0x50, 0x00, 0x53, 0x71, 0x53, 0x70, 0x55, 0x6f, + 0x10, 0x6f, 0xf9, 0x6f, 0xfa, 0xd0, 0x09, 0x52, 0xfc, 0x04, 0x71, + 0x52, 0xfb, 0x0c, 0x70, 0x66, 0xfc, 0x6c, 0xfb, 0x7a, 0x6f, 0xbf, + 0xeb, 0x18, 0x60, 0xd0, 0x20, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x52, + 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x65, 0x78, 0x6b, 0x79, 0x7f, + 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x78, 0x55, 0x79, 0x00, 0x7f, + 0x53, 0x76, 0x51, 0x79, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x76, 0x53, + 0x77, 0x3e, 0x76, 0x53, 0x76, 0x7f, 0x51, 0x79, 0x60, 0xd5, 0x51, + 0x77, 0x3f, 0x78, 0x51, 0x76, 0x3f, 0x78, 0x7f, 0x0e, 0x79, 0x00, + 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x53, 0x79, 0x7f, 0x60, 0xd4, + 0x3e, 0x76, 0x53, 0x77, 0x3e, 0x76, 0x53, 0x76, 0x7f, 0x06, 0x78, + 0xa2, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd4, 0x3e, 0x78, 0x53, + 0x77, 0x3e, 0x78, 0x16, 0x78, 0x02, 0x53, 0x76, 0x7f, 0x51, 0x78, + 0x01, 0xa2, 0x53, 0x76, 0x51, 0x79, 0x09, 0x00, 0x7f, 0x51, 0x71, + 0x53, 0x76, 0x51, 0x70, 0x53, 0x77, 0x51, 0x76, 0x02, 0x78, 0x53, + 0x78, 0x51, 0x77, 0x0a, 0x79, 0x7f, 0x53, 0x76, 0x51, 0x79, 0x09, + 0x00, 0x60, 0xd5, 0x52, 0x20, 0x3f, 0x76, 0x52, 0x21, 0x3f, 0x76, + 0x7f, 0x51, 0x76, 0x02, 0x78, 0x53, 0x78, 0x51, 0x77, 0x0a, 0x79, + 0x7f, 0x0e, 0x79, 0x00, 0x51, 0x79, 0x60, 0xd5, 0x52, 0x20, 0x3f, + 0x78, 0x52, 0x21, 0x3f, 0x78, 0x7f, 0x60, 0xd5, 0x52, 0x20, 0x3f, + 0x76, 0x52, 0x21, 0x3f, 0x76, 0x7f, 0x0e, 0x77, 0x00, 0x51, 0x77, + 0x60, 0xd5, 0x51, 0x79, 0x3f, 0x76, 0x7f, 0x55, 0xb7, 0x08, 0x55, + 0xb6, 0x08, 0x55, 0xb9, 0x08, 0x55, 0xb8, 0x08, 0x7f, 0x70, 0xfb, + 0x6e, 0x79, 0x6e, 0x78, 0x51, 0x77, 0x60, 0xd5, 0x51, 0x79, 0x3f, + 0x76, 0x51, 0x78, 0x3f, 0x76, 0x7f, 0x52, 0x00, 0x53, 0x76, 0x55, + 0x77, 0x00, 0x65, 0x76, 0x6b, 0x77, 0x7f, 0x60, 0xd4, 0x3e, 0x76, + 0x53, 0x75, 0x3e, 0x76, 0x16, 0x76, 0x02, 0x7f, 0x0e, 0x79, 0x00, + 0x51, 0x79, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x78, 0x7f, 0x3e, 0x78, + 0x12, 0x76, 0x54, 0x06, 0x51, 0x79, 0x1a, 0x77, 0x54, 0x05, 0x7f, + 0x3e, 0x78, 0x12, 0x76, 0x54, 0x04, 0x51, 0x79, 0x1a, 0x77, 0x54, + 0x03, 0x7f, 0x3e, 0x78, 0x53, 0x78, 0x51, 0x76, 0x14, 0x78, 0x51, + 0x77, 0x1c, 0x79, 0x7f, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x74, 0x53, + 0x75, 0x3e, 0x74, 0x53, 0x74, 0x7f, 0x00, 0x2c, 0x00, 0x22, 0x00, + 0x7a, 0x00, 0x28, 0x00, 0xaa, 0x04, 0x2a, 0x31, 0x33, 0x39, 0x00, + 0xae, 0x00, 0x05, 0x00, 0xb3, 0x07, 0x01, 0x02, 0x03, 0x08, 0x08, + 0x08, 0x08, 0x00, 0xba, 0x00, 0x0a, 0x00, 0xc4, 0x04, 0x03, 0x00, + 0x01, 0x01, 0x00, 0xc8, 0x00, 0x07, 0xff, 0x00, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_NTT.h b/drivers/input/keyboard/cypressbln/touchkey_fw_NTT.h new file mode 100644 index 0000000..b5b5564 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_NTT.h @@ -0,0 +1,685 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x04, 0x6f, 0x7e, 0x7e, 0x30, 0x30, 0x30, + 0x7d, 0x05, 0xa4, 0x7e, 0x7d, 0x04, 0x28, 0x7e, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x71, 0x10, 0x41, 0xd7, + 0x9f, 0x70, 0xef, 0x40, 0x71, 0x10, 0x62, 0xe3, 0x00, 0x70, 0xef, 0x50, + 0x80, 0x4e, 0x5d, 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, 0xfa, 0x01, 0x40, + 0x4f, 0x5b, 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, 0x50, 0x06, 0x00, + 0x40, 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, 0x70, 0xef, 0x18, 0x60, + 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, 0x71, 0x10, 0x41, 0xe1, 0xfe, + 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, 0x03, 0x50, 0x00, 0x4e, 0x62, + 0xd3, 0x03, 0x62, 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, 0x00, 0x71, + 0xc0, 0x7c, 0x02, 0x71, 0x62, 0xd0, 0x00, 0x50, 0x02, 0x57, 0x6f, 0x08, + 0x28, 0x53, 0x40, 0x18, 0x75, 0x09, 0x00, 0x28, 0x4b, 0x51, 0x40, 0x80, + 0x04, 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, 0x08, 0x28, 0x60, 0xd5, 0x74, + 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0x40, 0x18, 0x75, + 0x09, 0x00, 0x08, 0x28, 0xa0, 0x1c, 0x53, 0x3f, 0x18, 0x75, 0x09, 0x00, + 0x08, 0x28, 0x3f, 0x40, 0x47, 0x40, 0xff, 0xb0, 0x06, 0x5d, 0xd5, 0x74, + 0x60, 0xd5, 0x18, 0x7a, 0x3f, 0xbf, 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, + 0x00, 0x08, 0x28, 0x53, 0x3f, 0x50, 0x00, 0x3f, 0x40, 0x47, 0x40, 0xff, + 0xb0, 0x08, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x3f, 0xbf, + 0xef, 0x18, 0x8f, 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, 0xef, + 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, 0x10, 0x62, + 0xe0, 0x12, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x09, 0x1c, 0x8f, 0xff, + 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x00, + 0xfd, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa0, 0x00, + 0xa1, 0x80, 0xa2, 0x80, 0xa3, 0x0c, 0xa8, 0x00, 0xa6, 0x00, 0xa7, 0x00, + 0x7c, 0x33, 0x7a, 0x00, 0x7b, 0x04, 0x79, 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, + 0xd6, 0x44, 0xcf, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xcc, 0x00, + 0xc9, 0x00, 0xd7, 0x00, 0x2b, 0x00, 0xb0, 0x00, 0xb3, 0x02, 0xb6, 0x00, + 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0xff, 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x7f, + 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0xff, 0x54, 0x00, 0x55, 0x00, 0x56, + 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0xdc, + 0x00, 0xe2, 0x00, 0xdd, 0x00, 0xd8, 0x02, 0xd9, 0x00, 0xda, 0x00, 0xdb, + 0x00, 0xdf, 0x00, 0x29, 0x00, 0x30, 0x00, 0xbd, 0x00, 0xff, 0x70, 0xef, + 0x62, 0x00, 0x00, 0x71, 0x10, 0x62, 0x00, 0x08, 0x62, 0x01, 0x92, 0x70, + 0xef, 0x62, 0x04, 0x17, 0x71, 0x10, 0x62, 0x04, 0x14, 0x62, 0x05, 0xa8, + 0x70, 0xef, 0x62, 0x08, 0x00, 0x71, 0x10, 0x62, 0x08, 0x00, 0x62, 0x09, + 0x28, 0x70, 0xef, 0x62, 0x0c, 0x00, 0x71, 0x10, 0x62, 0x0c, 0x00, 0x62, + 0x0d, 0x00, 0x70, 0xef, 0x62, 0x10, 0x00, 0x71, 0x10, 0x62, 0x10, 0x00, + 0x62, 0x11, 0x00, 0x70, 0xef, 0x62, 0x01, 0x00, 0x62, 0x05, 0x00, 0x62, + 0x09, 0x00, 0x62, 0x0d, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, 0x7f, 0x13, + 0xba, 0x55, 0x02, 0x00, 0x55, 0x03, 0x17, 0x55, 0x04, 0x00, 0x7c, 0x02, + 0x81, 0x7c, 0x02, 0x1a, 0x7f, 0x10, 0x70, 0xef, 0x50, 0x00, 0x67, 0x50, + 0x01, 0x57, 0x72, 0x7c, 0x02, 0x9c, 0x50, 0x01, 0x67, 0x50, 0x01, 0x57, + 0xf3, 0x7c, 0x02, 0x9c, 0x70, 0xef, 0x20, 0x7f, 0x38, 0x02, 0x10, 0x08, + 0x4f, 0x56, 0xfc, 0x00, 0xd0, 0x04, 0x56, 0xfc, 0x01, 0x18, 0x20, 0x70, + 0xef, 0x62, 0xe3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xff, 0xa0, 0x1f, 0x4f, + 0x48, 0xfc, 0x01, 0xa0, 0x03, 0x71, 0x10, 0x54, 0xfd, 0x18, 0x20, 0x75, + 0x09, 0x00, 0x10, 0x08, 0x28, 0x4f, 0x59, 0xfd, 0x61, 0x00, 0x18, 0x20, + 0x75, 0x09, 0x00, 0x8f, 0xd7, 0x38, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, + 0x43, 0x44, 0x45, 0x46, 0x2e, 0x02, 0x08, 0x51, 0x02, 0x60, 0x00, 0x71, + 0x10, 0x41, 0x01, 0xf7, 0x43, 0x00, 0x08, 0x70, 0xef, 0x7f, 0x62, 0xd0, + 0x00, 0x53, 0x00, 0x71, 0x10, 0x5d, 0xe0, 0x08, 0x21, 0xf8, 0x29, 0x00, + 0x70, 0xfe, 0x60, 0xe0, 0x70, 0xef, 0x4b, 0x4b, 0x4b, 0x4b, 0x51, 0x02, + 0x21, 0xf7, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, + 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, + 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, + 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, + 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x47, 0x00, 0x00, 0x49, + 0x01, 0x00, 0x29, 0x08, 0x60, 0x00, 0x57, 0x01, 0x79, 0xbf, 0xfe, 0x18, + 0x71, 0x10, 0x60, 0xe0, 0x70, 0xef, 0x71, 0x01, 0x7f, 0x08, 0x67, 0x67, + 0x67, 0x67, 0x21, 0x0f, 0xff, 0x2e, 0x9f, 0x4e, 0x18, 0x21, 0x0f, 0xff, + 0x27, 0x9f, 0x47, 0x7f, 0x08, 0x10, 0x28, 0xa0, 0x0b, 0x9f, 0x3f, 0x20, + 0x18, 0x75, 0xdf, 0xf5, 0x74, 0x8f, 0xf2, 0x38, 0xfe, 0x7f, 0x52, 0x00, + 0xa0, 0x08, 0x10, 0x9f, 0x2d, 0x20, 0x75, 0x8f, 0xf6, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x50, 0x0d, 0x9f, 0x20, 0x50, 0x0a, 0x9f, 0x1c, 0x7f, 0x70, + 0xbf, 0x62, 0xd3, 0x03, 0x4f, 0x52, 0xfb, 0xa0, 0x15, 0x7b, 0xfb, 0x52, + 0xfc, 0x59, 0xfd, 0x60, 0xd3, 0x52, 0x00, 0x9f, 0x05, 0x4f, 0x62, 0xd3, + 0x03, 0x77, 0xfd, 0x8f, 0xe9, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x3d, 0xfa, + 0x00, 0xb0, 0x06, 0x3d, 0xfb, 0x00, 0xa0, 0x18, 0x10, 0x52, 0xfc, 0x59, + 0xfd, 0x28, 0x9e, 0xe6, 0x20, 0x07, 0xfd, 0x01, 0x0f, 0xfc, 0x00, 0x17, + 0xfb, 0x01, 0x1f, 0xfa, 0x00, 0x8f, 0xe0, 0x7f, 0x7e, 0x50, 0x01, 0x80, + 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, + 0xef, 0x80, 0x04, 0x2e, 0x03, 0x10, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, + 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xfb, + 0x80, 0x04, 0x2e, 0x03, 0x04, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x10, 0x70, 0x3f, 0x71, + 0x80, 0x5d, 0xd3, 0x08, 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x51, 0x08, + 0x60, 0xd3, 0x2e, 0x05, 0x80, 0x49, 0xd7, 0x08, 0xa0, 0x09, 0x26, 0x05, + 0xf0, 0x2e, 0x05, 0x00, 0x80, 0x0b, 0x49, 0xd7, 0x20, 0xa0, 0x06, 0x41, + 0xd7, 0xdf, 0x80, 0xac, 0x51, 0x05, 0x21, 0x0e, 0xe0, 0x01, 0x80, 0x11, + 0x80, 0x6d, 0x80, 0x7f, 0x80, 0x4d, 0x80, 0x9c, 0x80, 0x9a, 0x80, 0x98, + 0x80, 0x96, 0x80, 0x9d, 0x5d, 0xd8, 0x21, 0xfe, 0x39, 0x40, 0xa0, 0x06, + 0x62, 0xd7, 0x00, 0x80, 0x90, 0x49, 0xd8, 0x01, 0xb0, 0x15, 0x55, 0x0c, + 0x02, 0x26, 0x07, 0x00, 0x26, 0x06, 0x00, 0x26, 0x05, 0xf0, 0x2e, 0x05, + 0x04, 0x62, 0xd7, 0x10, 0x80, 0x77, 0x55, 0x0c, 0x01, 0x26, 0x05, 0xf0, + 0x2e, 0x05, 0x06, 0x5f, 0x07, 0x06, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, + 0x00, 0x60, 0xd8, 0x76, 0x07, 0x62, 0xd7, 0x14, 0x80, 0x5b, 0x51, 0x0a, + 0x78, 0x3a, 0x07, 0xc0, 0x0f, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, + 0x60, 0xd8, 0x76, 0x07, 0x2e, 0x05, 0x20, 0x60, 0xd8, 0x62, 0xd7, 0x04, + 0x80, 0x3f, 0x5d, 0xd8, 0x3a, 0x0a, 0xd0, 0x2b, 0xa0, 0x29, 0x53, 0x07, + 0x53, 0x06, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x80, 0x18, 0x51, 0x0b, + 0x78, 0x3a, 0x07, 0xc0, 0x16, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x5d, 0xd8, + 0x54, 0x00, 0x2e, 0x05, 0x10, 0x76, 0x07, 0x80, 0x01, 0x62, 0xd7, 0x10, + 0x80, 0x0f, 0x62, 0xd7, 0x00, 0x80, 0x0a, 0x26, 0x05, 0xf0, 0x2e, 0x05, + 0x00, 0x55, 0x0c, 0x00, 0x18, 0x60, 0xd0, 0x18, 0x60, 0xd3, 0x20, 0x18, + 0x7e, 0x62, 0xc8, 0x00, 0x55, 0x0c, 0x00, 0x90, 0x0e, 0x90, 0x18, 0x7f, + 0x62, 0xd0, 0x00, 0x51, 0x05, 0x21, 0xb0, 0x26, 0x05, 0x4f, 0x7f, 0x08, + 0x5d, 0xe0, 0x21, 0x7f, 0x60, 0xe0, 0x43, 0xe0, 0x80, 0x18, 0x7f, 0x43, + 0xd6, 0x31, 0x7f, 0x62, 0xd0, 0x00, 0x4f, 0x52, 0xfd, 0x53, 0x0a, 0x52, + 0xfc, 0x53, 0x0b, 0x52, 0xfb, 0x53, 0x09, 0x52, 0xfa, 0x53, 0x08, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x02, 0x08, 0x02, 0x20, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x05, 0x08, 0x5d, 0xa4, 0x04, 0x1b, 0x5d, 0xa5, 0x0c, + 0x1a, 0x55, 0x1c, 0x01, 0x18, 0x7e, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, + 0xbf, 0x53, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x5b, 0x62, 0xd3, + 0x00, 0x13, 0x4b, 0x62, 0xd3, 0x00, 0x54, 0x4f, 0x62, 0xd3, 0x00, 0x52, + 0x5a, 0x62, 0xd3, 0x00, 0x1b, 0x4a, 0x62, 0xd3, 0x00, 0x54, 0x4e, 0x48, + 0x4e, 0x80, 0xb0, 0x33, 0x3d, 0x4e, 0x00, 0xb0, 0x77, 0x51, 0x0d, 0x3b, + 0x4f, 0xc0, 0x71, 0x52, 0x4f, 0x58, 0x1e, 0x01, 0x00, 0x6d, 0x62, 0xd3, + 0x00, 0x05, 0x41, 0xc0, 0x09, 0x51, 0x0f, 0x3b, 0x41, 0xd0, 0x12, 0xa0, + 0x10, 0x56, 0x41, 0x00, 0x5b, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x07, 0x4b, + 0x01, 0x0f, 0x4a, 0x00, 0x80, 0x3d, 0x3d, 0x4e, 0xff, 0xb0, 0x09, 0x50, + 0xff, 0x12, 0x0e, 0x3b, 0x4f, 0xc0, 0x1c, 0x62, 0xd3, 0x00, 0x56, 0x4f, + 0x00, 0x56, 0x4e, 0x00, 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x48, + 0x78, 0x54, 0x48, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, 0x62, 0xd3, + 0x00, 0x52, 0x5b, 0x62, 0xd3, 0x00, 0x54, 0x4b, 0x62, 0xd3, 0x00, 0x52, + 0x5a, 0x62, 0xd3, 0x00, 0x54, 0x4a, 0x51, 0x1e, 0x64, 0x5c, 0x62, 0xd3, + 0x00, 0x56, 0x4f, 0x00, 0x56, 0x4e, 0x00, 0x5b, 0x67, 0x5c, 0x62, 0xd3, + 0x00, 0x51, 0x12, 0x54, 0x48, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0xbf, + 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x08, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x43, + 0x53, 0x19, 0x55, 0x18, 0x00, 0x18, 0x08, 0x90, 0x7e, 0x62, 0xd3, 0x00, + 0x23, 0x45, 0xb0, 0x2c, 0x51, 0x10, 0x04, 0x19, 0x0e, 0x18, 0x00, 0x18, + 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x4f, 0x12, 0x19, 0x52, 0x4e, 0x1a, + 0x18, 0xc0, 0x39, 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x46, 0x78, + 0x54, 0x46, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x3e, 0x80, 0x18, 0x51, + 0x10, 0x14, 0x19, 0x1e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, 0x00, + 0x52, 0x4f, 0x12, 0x19, 0x52, 0x4e, 0x1a, 0x18, 0xc0, 0x0e, 0x5b, 0x67, + 0x90, 0x31, 0x62, 0xd3, 0x00, 0x2d, 0x45, 0x50, 0x01, 0x80, 0x24, 0x5b, + 0x67, 0x08, 0x90, 0x23, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x45, 0x62, 0xd3, + 0x00, 0x20, 0x51, 0x11, 0x54, 0x46, 0x50, 0x00, 0x80, 0x0d, 0x5b, 0x67, + 0x90, 0x0d, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x45, 0x50, 0x00, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x08, 0x67, 0x67, 0x67, 0x5c, 0x18, 0x21, 0x07, 0xf0, + 0x01, 0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x70, 0xbf, + 0x70, 0xbf, 0x62, 0xd3, 0x00, 0x50, 0x02, 0x78, 0x08, 0x5c, 0x56, 0x43, + 0x28, 0x18, 0x78, 0xdf, 0xf8, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x91, + 0x7a, 0x70, 0xbf, 0x18, 0x08, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x5b, + 0x62, 0xd3, 0x00, 0x54, 0x4b, 0x62, 0xd3, 0x00, 0x52, 0x5a, 0x62, 0xd3, + 0x00, 0x54, 0x4a, 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x62, 0xd0, 0x00, 0x55, 0x14, 0x00, 0x50, 0x02, 0x78, 0x08, 0x9f, 0x0e, + 0x39, 0x01, 0xb0, 0x04, 0x55, 0x14, 0x01, 0x18, 0x78, 0xdf, 0xf3, 0x51, + 0x14, 0x7f, 0x50, 0x02, 0x78, 0x08, 0x9e, 0x42, 0x18, 0x78, 0xdf, 0xfa, + 0x7f, 0x98, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0xd8, 0xd9, + 0xda, 0xdb, 0xdf, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, + 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x62, 0xd3, 0x00, 0x57, 0x00, 0x56, 0x45, + 0x00, 0x79, 0xdf, 0xfb, 0x62, 0xd3, 0x00, 0x50, 0x32, 0x57, 0x01, 0x54, + 0x48, 0x79, 0xdf, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x55, 0x0d, 0x1e, 0x55, + 0x0e, 0x05, 0x55, 0x0f, 0x32, 0x55, 0x10, 0x01, 0x55, 0x11, 0x01, 0x55, + 0x12, 0x32, 0x55, 0x22, 0x04, 0x55, 0x1f, 0x32, 0x43, 0x61, 0x0d, 0x57, + 0x00, 0x50, 0x02, 0x90, 0x82, 0x50, 0x04, 0xff, 0xa4, 0x29, 0x00, 0x60, + 0xa9, 0x62, 0xa0, 0x08, 0x43, 0xa2, 0x04, 0x62, 0xa3, 0x70, 0x43, 0x7a, + 0x01, 0x43, 0xdf, 0x01, 0x50, 0x01, 0x57, 0x0d, 0x90, 0x12, 0x90, 0x4d, + 0x7f, 0x53, 0x22, 0xff, 0x84, 0x29, 0x00, 0x60, 0xa9, 0x51, 0x21, 0x58, + 0x20, 0x90, 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, 0x03, 0x53, 0x21, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x29, 0x80, 0x60, 0xa1, 0x5b, 0x78, 0x21, 0x0f, + 0x29, 0x08, 0x74, 0x53, 0x20, 0x12, 0x22, 0x02, 0x21, 0x5c, 0x50, 0x00, + 0x53, 0x1d, 0x53, 0x23, 0x29, 0x01, 0x79, 0xa0, 0x08, 0x64, 0x6b, 0x1d, + 0x6b, 0x23, 0x8f, 0xf5, 0x60, 0xb5, 0x51, 0x1d, 0x60, 0xb4, 0x7f, 0x62, + 0xd0, 0x00, 0x53, 0x1f, 0x7f, 0x50, 0x02, 0x78, 0x08, 0x90, 0x07, 0x90, + 0x39, 0x18, 0x78, 0xdf, 0xf8, 0x7f, 0x64, 0x5c, 0xfd, 0x4e, 0x4b, 0x74, + 0xfd, 0x4a, 0x7f, 0x62, 0xd0, 0x00, 0x53, 0x1d, 0x10, 0x5b, 0x64, 0x64, + 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x2a, 0x1d, 0x61, 0x01, 0x36, 0x1d, 0xff, + 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x36, 0x1d, 0xff, 0x18, 0xff, 0x0e, + 0x5c, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, 0x7f, 0x62, 0xd0, + 0x00, 0x10, 0x73, 0x53, 0x1d, 0x71, 0x10, 0x5b, 0xfe, 0xf8, 0x5c, 0x5e, + 0x00, 0x22, 0x1d, 0x61, 0x00, 0x70, 0xef, 0x18, 0x64, 0x64, 0x5c, 0x71, + 0x10, 0x5e, 0x01, 0x22, 0x1d, 0x61, 0x01, 0x36, 0x1d, 0xff, 0x5e, 0x00, + 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, + 0x53, 0x1e, 0x50, 0x00, 0x53, 0x1a, 0x53, 0x1b, 0x43, 0xa0, 0x01, 0x51, + 0x1f, 0x60, 0xfd, 0x41, 0xa3, 0xdf, 0x51, 0x1e, 0x9f, 0x84, 0x9f, 0x8b, + 0x58, 0x23, 0x55, 0x1c, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x43, + 0xb3, 0x01, 0x51, 0x1c, 0xaf, 0xfd, 0x79, 0xdf, 0xee, 0x51, 0x1e, 0x9f, + 0x69, 0x9f, 0x9b, 0x43, 0xa3, 0x20, 0x41, 0xa0, 0xfe, 0x62, 0xfd, 0x00, + 0x50, 0xff, 0x4c, 0x1b, 0x14, 0x1b, 0x51, 0x20, 0x11, 0x08, 0xfe, 0x8f, + 0x4c, 0x1a, 0x1c, 0x1a, 0xd0, 0x07, 0x55, 0x1a, 0x00, 0x55, 0x1b, 0x00, + 0x51, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x1b, 0x54, 0x5b, 0x51, + 0x1a, 0x54, 0x5a, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x9f, 0x90, 0x18, + 0x78, 0xdf, 0xfa, 0x7f, 0x10, 0x7c, 0x04, 0x50, 0x7c, 0x04, 0x2d, 0x20, + 0x7c, 0x0e, 0xdd, 0x62, 0xe3, 0x38, 0x10, 0x7c, 0x02, 0xec, 0x20, 0x43, + 0x00, 0x08, 0x62, 0xd0, 0x00, 0x55, 0x24, 0x08, 0x55, 0x25, 0x04, 0x55, + 0x26, 0x03, 0x55, 0x28, 0x55, 0x55, 0x29, 0x02, 0x10, 0x50, 0x00, 0x08, + 0x50, 0x24, 0x08, 0x50, 0x05, 0x08, 0x50, 0x12, 0x08, 0x7c, 0x05, 0x7f, + 0x38, 0xfc, 0x7c, 0x05, 0x59, 0x7c, 0x05, 0x6f, 0x20, 0x71, 0x01, 0x10, + 0x7c, 0x07, 0x8c, 0x7c, 0x07, 0x12, 0x20, 0x90, 0xea, 0x80, 0xe5, 0x7c, + 0x10, 0x2e, 0x62, 0xe3, 0x38, 0x92, 0x61, 0x62, 0xd0, 0x00, 0x3c, 0x6f, + 0x01, 0xb0, 0x09, 0x50, 0x00, 0x08, 0x7c, 0x11, 0xae, 0x38, 0xff, 0x62, + 0xe3, 0x38, 0x10, 0x7c, 0x07, 0x50, 0x20, 0x39, 0x00, 0xa0, 0x86, 0x62, + 0xd0, 0x00, 0x51, 0x63, 0x11, 0x94, 0x51, 0x62, 0x19, 0x02, 0xd0, 0x12, + 0x7c, 0x0e, 0xef, 0x39, 0xe1, 0xa0, 0x10, 0x62, 0xd0, 0x00, 0x76, 0x63, + 0x0e, 0x62, 0x00, 0x80, 0x06, 0x7c, 0x13, 0x92, 0x90, 0xa1, 0x62, 0xd0, + 0x00, 0x3c, 0x72, 0xf0, 0xd0, 0x06, 0x62, 0xd0, 0x00, 0x76, 0x72, 0x62, + 0xd0, 0x00, 0x51, 0x72, 0x62, 0xd0, 0x00, 0x3a, 0x29, 0xb0, 0x82, 0x7c, + 0x0e, 0xef, 0x62, 0xd0, 0x00, 0x53, 0x73, 0x3c, 0x73, 0xe1, 0xa0, 0x35, + 0x62, 0xd0, 0x00, 0x55, 0x67, 0x00, 0x55, 0x66, 0x00, 0x62, 0xd0, 0x00, + 0x51, 0x73, 0x62, 0xd0, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x06, 0x3f, + 0x66, 0x7c, 0x13, 0x05, 0x50, 0x08, 0x3f, 0x3f, 0x7c, 0x13, 0xb3, 0x7c, + 0x0f, 0x2a, 0x62, 0xd0, 0x00, 0x55, 0x71, 0x01, 0x62, 0xd0, 0x00, 0x55, + 0x6e, 0x03, 0x80, 0x41, 0x62, 0xd0, 0x00, 0x55, 0x72, 0x00, 0x80, 0x39, + 0x7c, 0x13, 0x92, 0x7c, 0x13, 0xa3, 0xb0, 0x31, 0x62, 0xd0, 0x00, 0x7a, + 0x6e, 0x3c, 0x6e, 0x00, 0xb0, 0x27, 0x7c, 0x0f, 0xb2, 0x62, 0xd0, 0x00, + 0x55, 0x71, 0x00, 0x62, 0xd0, 0x00, 0x51, 0x73, 0x62, 0xd0, 0x00, 0x53, + 0x3f, 0x55, 0x40, 0x00, 0x06, 0x3f, 0x66, 0x7c, 0x13, 0x05, 0x50, 0x00, + 0x3f, 0x3f, 0x62, 0xd0, 0x00, 0x55, 0x72, 0x00, 0x7c, 0x10, 0x9b, 0x8f, + 0x1b, 0x8f, 0xff, 0x10, 0x4f, 0x38, 0x16, 0x10, 0x62, 0xd0, 0x00, 0x51, + 0x68, 0x7c, 0x08, 0x33, 0x20, 0x10, 0x50, 0x00, 0x7c, 0x08, 0xa7, 0x20, + 0x56, 0x0d, 0x00, 0x80, 0x86, 0x56, 0x00, 0x00, 0x80, 0x7a, 0x7c, 0x12, + 0xdb, 0x06, 0x3f, 0x68, 0x7c, 0x12, 0xf4, 0x10, 0x7c, 0x08, 0x33, 0x20, + 0x10, 0x52, 0x00, 0x7c, 0x08, 0xa7, 0x20, 0x10, 0x7c, 0x05, 0x64, 0x62, + 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x55, 0x40, 0x03, 0x5a, 0x3f, + 0x06, 0x3f, 0x01, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, 0x08, 0x51, 0x3d, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x12, 0x8e, 0x38, 0xfc, + 0x7c, 0x13, 0x0d, 0x53, 0x40, 0x52, 0x0d, 0x7c, 0x13, 0x47, 0x51, 0x3d, + 0x02, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x53, 0x40, 0x52, 0x00, + 0x7c, 0x13, 0x47, 0x06, 0x3d, 0x5a, 0x0e, 0x3e, 0x00, 0x51, 0x3e, 0x60, + 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x51, 0x40, 0x60, + 0xd5, 0x51, 0x3e, 0x3f, 0x3f, 0x51, 0x3d, 0x3f, 0x3f, 0x77, 0x00, 0x3d, + 0x00, 0x02, 0xcf, 0x83, 0x77, 0x0d, 0x3d, 0x0d, 0x03, 0xcf, 0x77, 0x56, + 0x00, 0x00, 0x80, 0xd0, 0x62, 0xd0, 0x00, 0x55, 0x40, 0x03, 0x5a, 0x3f, + 0x06, 0x3f, 0x01, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, 0x08, 0x51, 0x3d, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x12, 0x8e, 0x38, 0xfc, + 0x97, 0xef, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x54, 0x0e, 0x3e, 0x3f, 0x54, + 0x0f, 0x5a, 0x3f, 0x06, 0x3f, 0x03, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, + 0x08, 0x51, 0x3d, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x12, + 0x8e, 0x38, 0xfc, 0x97, 0xc8, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x54, 0x10, + 0x3e, 0x3f, 0x54, 0x11, 0x5a, 0x3f, 0x06, 0x3f, 0x05, 0x52, 0x00, 0x53, + 0x3d, 0x50, 0x00, 0x08, 0x51, 0x3d, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, + 0x08, 0x7c, 0x12, 0x8e, 0x38, 0xfc, 0x97, 0xa1, 0x40, 0x60, 0xd4, 0x3e, + 0x3f, 0x54, 0x12, 0x3e, 0x3f, 0x54, 0x13, 0x50, 0x03, 0x08, 0x5a, 0x3f, + 0x06, 0x3f, 0x0e, 0x08, 0x51, 0x3f, 0x08, 0x7c, 0x10, 0xe0, 0x38, 0xfd, + 0x62, 0xd0, 0x00, 0x51, 0x3f, 0x54, 0x15, 0x51, 0x40, 0x54, 0x14, 0x97, + 0x3a, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x97, 0x62, 0x40, 0x97, 0x9d, 0x40, + 0x06, 0x3f, 0x4a, 0x97, 0x60, 0x40, 0x7c, 0x13, 0xaa, 0x97, 0x24, 0x40, + 0x51, 0x3f, 0x01, 0x52, 0x97, 0x4c, 0x40, 0x97, 0x87, 0x40, 0x51, 0x3f, + 0x01, 0x56, 0x97, 0x42, 0x40, 0x97, 0x7d, 0x40, 0x06, 0x3f, 0x5e, 0x97, + 0x40, 0x40, 0x97, 0xe2, 0x40, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x2d, + 0x38, 0xea, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x06, 0x62, 0xd0, 0x00, 0x55, + 0x70, 0x00, 0x10, 0x62, 0xd0, 0x00, 0x51, 0x68, 0x7c, 0x08, 0x33, 0x20, + 0x10, 0x50, 0x00, 0x7c, 0x08, 0xa7, 0x20, 0x56, 0x00, 0x00, 0x80, 0x24, + 0x96, 0xe5, 0x40, 0x06, 0x3f, 0x68, 0x96, 0xf8, 0x40, 0x10, 0x7c, 0x08, + 0x33, 0x20, 0x10, 0x52, 0x00, 0x7c, 0x08, 0xa7, 0x20, 0x10, 0x7c, 0x05, + 0x64, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x77, 0x00, 0x3d, + 0x00, 0x02, 0xcf, 0xd9, 0x56, 0x00, 0x00, 0x81, 0xcf, 0x97, 0x79, 0x40, + 0xa0, 0xb6, 0x62, 0xd0, 0x00, 0x96, 0xa4, 0x40, 0x51, 0x3f, 0x01, 0x4a, + 0x96, 0x8c, 0x40, 0x06, 0x3f, 0x5a, 0x96, 0xae, 0x40, 0x53, 0x3f, 0x51, + 0x3d, 0x12, 0x3f, 0x51, 0x3e, 0x1a, 0x40, 0xd0, 0x19, 0x62, 0xd0, 0x00, + 0x96, 0x85, 0x40, 0x51, 0x3f, 0x01, 0x4a, 0x96, 0x6d, 0x40, 0x06, 0x3f, + 0x5a, 0x96, 0x8f, 0x40, 0x97, 0x22, 0x40, 0x80, 0x17, 0x62, 0xd0, 0x00, + 0x96, 0x6d, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x96, 0x55, 0x40, 0x06, 0x3f, + 0x4a, 0x96, 0x77, 0x40, 0x97, 0x0a, 0x40, 0x50, 0x90, 0x13, 0x03, 0x50, + 0x01, 0x1b, 0x02, 0xc0, 0x58, 0x62, 0xd0, 0x00, 0x96, 0x4d, 0x40, 0x51, + 0x3f, 0x01, 0x5e, 0x96, 0x35, 0x40, 0x06, 0x3f, 0x5a, 0x96, 0x57, 0x40, + 0x53, 0x3f, 0x51, 0x3d, 0x12, 0x3f, 0x51, 0x3e, 0x1a, 0x40, 0xd0, 0x19, + 0x62, 0xd0, 0x00, 0x96, 0x2e, 0x40, 0x51, 0x3f, 0x01, 0x5e, 0x96, 0x16, + 0x40, 0x06, 0x3f, 0x5a, 0x96, 0x38, 0x40, 0x96, 0xc0, 0x40, 0x80, 0x17, + 0x62, 0xd0, 0x00, 0x96, 0x16, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x95, 0xfe, + 0x40, 0x06, 0x3f, 0x5e, 0x96, 0x20, 0x40, 0x96, 0xa8, 0x40, 0x50, 0x90, + 0x13, 0x05, 0x50, 0x01, 0x1b, 0x04, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x76, + 0x70, 0x81, 0x13, 0x62, 0xd0, 0x00, 0x95, 0xef, 0x40, 0x51, 0x3f, 0x01, + 0x56, 0x95, 0xd7, 0x40, 0x06, 0x3f, 0x52, 0x96, 0x18, 0x40, 0x96, 0x30, + 0x40, 0x95, 0xdc, 0x40, 0x51, 0x3f, 0x01, 0x5e, 0x95, 0xc4, 0x40, 0x06, + 0x3f, 0x56, 0x96, 0x05, 0x40, 0x96, 0x1d, 0x40, 0x95, 0xc9, 0x40, 0x51, + 0x3f, 0x01, 0x5a, 0x95, 0xb1, 0x40, 0x06, 0x3f, 0x5e, 0x95, 0xf2, 0x40, + 0x96, 0x0a, 0x40, 0x95, 0xb6, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x95, 0xde, + 0x40, 0x53, 0x3e, 0x96, 0x40, 0x40, 0x53, 0x3b, 0x08, 0x51, 0x3c, 0x53, + 0x3a, 0x18, 0x53, 0x39, 0x65, 0x39, 0x6b, 0x3a, 0x06, 0x3f, 0x56, 0x95, + 0xad, 0x40, 0x53, 0x3f, 0x51, 0x39, 0x04, 0x3f, 0x51, 0x3a, 0x0c, 0x40, + 0x51, 0x3b, 0x04, 0x3f, 0x51, 0x3c, 0x0c, 0x40, 0x70, 0xfb, 0x6e, 0x40, + 0x6e, 0x3f, 0x95, 0xd5, 0x40, 0x10, 0x52, 0x00, 0x7c, 0x05, 0xb2, 0x20, + 0x62, 0xd0, 0x00, 0x95, 0x6e, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x95, 0x56, + 0x40, 0x06, 0x3f, 0x4a, 0x95, 0x78, 0x40, 0x53, 0x3f, 0x51, 0x3d, 0x12, + 0x3f, 0x51, 0x3e, 0x1a, 0x40, 0xd0, 0x1b, 0x95, 0x5e, 0x40, 0x06, 0x3f, + 0x6c, 0x95, 0x71, 0x40, 0x7a, 0x3f, 0x53, 0x3e, 0x06, 0x3e, 0x01, 0x51, + 0x40, 0x60, 0xd5, 0x51, 0x3e, 0x3f, 0x3f, 0x80, 0x0e, 0x95, 0x44, 0x40, + 0x06, 0x3f, 0x6c, 0x95, 0x68, 0x40, 0x50, 0x00, 0x3f, 0x3f, 0x95, 0x37, + 0x40, 0x06, 0x3f, 0x6c, 0x95, 0x4a, 0x40, 0x53, 0x40, 0x50, 0x05, 0x3a, + 0x40, 0xd0, 0x3b, 0x62, 0xd0, 0x00, 0x95, 0x17, 0x40, 0x51, 0x3f, 0x01, + 0x4a, 0x95, 0x3f, 0x40, 0x53, 0x3e, 0x06, 0x3f, 0x5a, 0x95, 0x1f, 0x40, + 0x53, 0x3f, 0x51, 0x3e, 0x95, 0x97, 0x40, 0x02, 0x3f, 0x53, 0x3f, 0x51, + 0x3c, 0x0a, 0x40, 0x53, 0x40, 0x95, 0x4e, 0x40, 0x52, 0x00, 0x53, 0x3f, + 0x55, 0x40, 0x00, 0x06, 0x3f, 0x6c, 0x95, 0x1d, 0x40, 0x50, 0x00, 0x3f, + 0x3f, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0x2e, 0x95, 0xa6, 0x40, 0xb0, + 0x9b, 0x56, 0x00, 0x00, 0x80, 0x91, 0x62, 0xd0, 0x00, 0x94, 0xcc, 0x40, + 0x51, 0x3f, 0x01, 0x5a, 0x94, 0xb4, 0x40, 0x06, 0x3f, 0x2e, 0x94, 0xf5, + 0x40, 0x95, 0x0d, 0x40, 0x94, 0xb9, 0x40, 0x51, 0x3f, 0x01, 0x4a, 0x94, + 0xa1, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x53, 0x3b, 0x51, 0x40, 0x95, 0x29, + 0x40, 0x51, 0x3d, 0x12, 0x3b, 0x51, 0x3e, 0x1a, 0x3c, 0xd0, 0x30, 0x62, + 0xd0, 0x00, 0x52, 0x00, 0x95, 0x0d, 0x40, 0x51, 0x3d, 0x01, 0x4a, 0x53, + 0x3b, 0x51, 0x3e, 0x95, 0x0c, 0x40, 0x06, 0x3d, 0x5a, 0x0e, 0x3e, 0x00, + 0x51, 0x3e, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x12, 0x3b, + 0x54, 0x03, 0x51, 0x3e, 0x1a, 0x3c, 0x54, 0x02, 0x80, 0x07, 0x56, 0x03, + 0x00, 0x56, 0x02, 0x00, 0x62, 0xd0, 0x00, 0x06, 0x3f, 0x2a, 0x94, 0x95, + 0x40, 0x52, 0x02, 0x3f, 0x3f, 0x52, 0x03, 0x3f, 0x3f, 0x94, 0x54, 0x40, + 0x51, 0x3f, 0x01, 0x4a, 0x94, 0x3c, 0x40, 0x06, 0x3f, 0x32, 0x94, 0x7d, + 0x40, 0x94, 0x95, 0x40, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x6c, 0x95, + 0x07, 0x40, 0xa0, 0x1a, 0x62, 0xd0, 0x00, 0x3c, 0x70, 0x00, 0xa0, 0x12, + 0x50, 0x75, 0x08, 0x50, 0x30, 0x08, 0x90, 0x0d, 0x38, 0xfe, 0x9b, 0xa7, + 0x10, 0x7c, 0x07, 0x6a, 0x20, 0x38, 0xfa, 0x20, 0x7f, 0x10, 0x4f, 0x80, + 0x02, 0x40, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, 0x3f, 0x52, 0xfb, 0x53, + 0x40, 0x51, 0x3f, 0x11, 0x01, 0x54, 0xfc, 0x51, 0x40, 0x19, 0x00, 0x54, + 0xfb, 0x3c, 0x40, 0x00, 0xbf, 0xe4, 0x3c, 0x3f, 0x00, 0xbf, 0xdf, 0x20, + 0x7f, 0x10, 0x7c, 0x04, 0x50, 0x7c, 0x04, 0x2d, 0x20, 0x7f, 0x10, 0x7c, + 0x04, 0x4c, 0x7c, 0x04, 0x29, 0x20, 0x7f, 0x62, 0xd0, 0x00, 0x50, 0x28, + 0x12, 0x4f, 0x50, 0x00, 0x1a, 0x4e, 0xd0, 0x15, 0x62, 0xd0, 0x00, 0x50, + 0x28, 0x12, 0x51, 0x50, 0x00, 0x1a, 0x50, 0xd0, 0x08, 0x62, 0xd0, 0x00, + 0x50, 0xe1, 0x80, 0x1a, 0x62, 0xd0, 0x00, 0x51, 0x51, 0x12, 0x4f, 0x51, + 0x50, 0x1a, 0x4e, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, 0x00, 0x80, 0x06, + 0x62, 0xd0, 0x00, 0x50, 0x01, 0x7f, 0x10, 0x4f, 0x38, 0x01, 0x70, 0xfe, + 0x62, 0xd0, 0x00, 0x51, 0x73, 0x01, 0x01, 0x62, 0xd0, 0x00, 0x53, 0x24, + 0x71, 0x01, 0x62, 0xd0, 0x00, 0x3c, 0x71, 0x00, 0xb0, 0x69, 0x62, 0xe3, + 0x38, 0x10, 0x7c, 0x05, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, + 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x05, 0x64, 0x62, 0xd0, 0x00, + 0x20, 0x53, 0x40, 0x47, 0x40, 0x20, 0xa0, 0x03, 0x80, 0x12, 0x50, 0x00, + 0x08, 0x50, 0x14, 0x08, 0x9f, 0x43, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, + 0xc8, 0xcf, 0xdf, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x05, 0x64, + 0x62, 0xd0, 0x00, 0x20, 0x53, 0x40, 0x47, 0x40, 0x20, 0xb0, 0x03, 0x80, + 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9f, 0x1c, 0x38, 0xfe, 0x77, + 0x00, 0x3d, 0x00, 0x1e, 0xcf, 0xdf, 0x62, 0xd0, 0x00, 0x51, 0x24, 0x29, + 0x08, 0x53, 0x24, 0x43, 0x00, 0x08, 0x38, 0xff, 0x20, 0x7f, 0x10, 0x4f, + 0x38, 0x01, 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x51, 0x73, 0x01, 0x09, 0x62, + 0xd0, 0x00, 0x53, 0x24, 0x71, 0x01, 0x93, 0xdb, 0x40, 0xb0, 0x60, 0x62, + 0xe3, 0x38, 0x10, 0x7c, 0x05, 0x64, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, + 0xf7, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x05, 0x64, 0x62, 0xd0, + 0x00, 0x20, 0x53, 0x40, 0x47, 0x40, 0x20, 0xa0, 0x03, 0x80, 0x12, 0x50, + 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0xbe, 0x38, 0xfe, 0x77, 0x00, 0x3d, + 0x00, 0xc8, 0xcf, 0xdf, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x05, + 0x64, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x40, 0x47, 0x40, 0x20, 0xb0, 0x03, + 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0x97, 0x38, 0xfe, + 0x77, 0x00, 0x3d, 0x00, 0x1e, 0xcf, 0xdf, 0x43, 0x00, 0x08, 0x38, 0xff, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x01, 0x62, 0xd0, 0x00, 0x51, 0x24, 0x54, + 0x00, 0x3d, 0x00, 0x01, 0xb0, 0x17, 0x93, 0x73, 0x40, 0x93, 0x60, 0x40, + 0xa0, 0x0a, 0x62, 0xd0, 0x00, 0x55, 0x67, 0x00, 0x55, 0x66, 0x00, 0x93, + 0x0d, 0x40, 0x80, 0x44, 0x3d, 0x00, 0x02, 0xb0, 0x1b, 0x62, 0xd0, 0x00, + 0x55, 0x75, 0x01, 0x62, 0xd0, 0x00, 0x55, 0x74, 0x00, 0x62, 0xd0, 0x00, + 0x55, 0x67, 0x08, 0x55, 0x66, 0x08, 0x92, 0xee, 0x40, 0x80, 0x25, 0x52, + 0x00, 0x21, 0xf0, 0x39, 0x40, 0xb0, 0x0c, 0x62, 0xd0, 0x00, 0x55, 0x6f, + 0x02, 0x92, 0xdb, 0x40, 0x80, 0x12, 0x52, 0x00, 0x21, 0xf0, 0x39, 0x50, + 0xb0, 0x0a, 0x62, 0xd0, 0x00, 0x55, 0x6f, 0x01, 0x92, 0xc8, 0x40, 0x38, + 0xff, 0x20, 0x7f, 0x62, 0xd0, 0x00, 0x3c, 0x75, 0x00, 0xa0, 0x13, 0x9e, + 0x38, 0x62, 0xd0, 0x00, 0x3c, 0x74, 0x00, 0xb0, 0x33, 0x55, 0x74, 0x01, + 0x7c, 0x0a, 0x53, 0x80, 0x2b, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x66, + 0xd0, 0x08, 0x10, 0x7c, 0x04, 0x50, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, + 0x4c, 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x67, 0xd0, 0x08, 0x10, + 0x7c, 0x04, 0x2d, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0x29, 0x20, 0x7f, + 0x10, 0x4f, 0x38, 0x03, 0x56, 0x02, 0x00, 0x56, 0x01, 0x00, 0x56, 0x00, + 0x00, 0x80, 0x41, 0x62, 0xd0, 0x00, 0x91, 0xdb, 0x40, 0x52, 0xfc, 0x04, + 0x3f, 0x52, 0xfb, 0x0c, 0x40, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, + 0x40, 0x3e, 0x3f, 0x53, 0x3f, 0x52, 0x02, 0x12, 0x3f, 0x52, 0x01, 0x1a, + 0x40, 0xd0, 0x1b, 0x62, 0xd0, 0x00, 0x91, 0xb7, 0x40, 0x52, 0xfc, 0x04, + 0x3f, 0x52, 0xfb, 0x0c, 0x40, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x54, + 0x01, 0x3e, 0x3f, 0x54, 0x02, 0x77, 0x00, 0x52, 0x00, 0x3b, 0xfa, 0xcf, + 0xbb, 0x62, 0xd0, 0x00, 0x52, 0x02, 0x53, 0x3f, 0x52, 0x01, 0x53, 0x40, + 0x38, 0xfd, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x71, 0x10, 0x41, 0x01, + 0xf7, 0x43, 0x00, 0x08, 0x70, 0xcf, 0x43, 0x00, 0x08, 0x50, 0x00, 0x08, + 0x50, 0x64, 0x08, 0x9d, 0x58, 0x71, 0x10, 0x41, 0x01, 0xf7, 0x41, 0x00, + 0xf7, 0x70, 0xcf, 0x43, 0x00, 0x08, 0x50, 0x00, 0x08, 0x50, 0x64, 0x08, + 0x9d, 0x43, 0x38, 0xfc, 0x5d, 0x00, 0x62, 0xd0, 0x00, 0x53, 0x40, 0x26, + 0x40, 0x08, 0x3c, 0x40, 0x08, 0xb0, 0x09, 0x56, 0x01, 0x00, 0x56, 0x00, + 0x00, 0x80, 0x07, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x52, 0x01, 0x62, + 0xd0, 0x00, 0x53, 0x6f, 0x71, 0x10, 0x43, 0x00, 0x08, 0x41, 0x01, 0xf7, + 0x70, 0xcf, 0x3c, 0x6f, 0x00, 0xb0, 0x04, 0x43, 0x00, 0x08, 0x38, 0xfe, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x01, 0x62, 0xe3, 0x38, 0x10, 0x50, 0x02, + 0x7c, 0x02, 0xfe, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x02, 0xfe, 0x20, 0x10, + 0x50, 0xff, 0x7c, 0x02, 0xfe, 0x20, 0x10, 0x50, 0x04, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x5a, 0x08, 0x7c, 0x03, 0xe3, 0x38, 0xfd, 0x20, 0x56, 0x00, + 0x00, 0x80, 0x6f, 0x62, 0xd0, 0x00, 0x90, 0xeb, 0x40, 0x51, 0x3f, 0x01, + 0x4a, 0x90, 0xd3, 0x40, 0x06, 0x3f, 0x5a, 0x90, 0xf5, 0x40, 0x53, 0x3f, + 0x51, 0x3d, 0x12, 0x3f, 0x51, 0x3e, 0x1a, 0x40, 0xd0, 0x40, 0x62, 0xd0, + 0x00, 0x90, 0xcc, 0x40, 0x51, 0x3f, 0x01, 0x4a, 0x90, 0xb4, 0x40, 0x06, + 0x3f, 0x5a, 0x90, 0xd6, 0x40, 0x91, 0x74, 0x40, 0x51, 0x40, 0x10, 0x7c, + 0x02, 0xfe, 0x20, 0x62, 0xd0, 0x00, 0x90, 0xaf, 0x40, 0x51, 0x3f, 0x01, + 0x4a, 0x90, 0x97, 0x40, 0x06, 0x3f, 0x5a, 0x90, 0xb9, 0x40, 0x91, 0x57, + 0x40, 0x26, 0x40, 0x00, 0x51, 0x3f, 0x10, 0x7c, 0x02, 0xfe, 0x20, 0x80, + 0x0f, 0x10, 0x50, 0x00, 0x7c, 0x02, 0xfe, 0x20, 0x10, 0x50, 0x00, 0x7c, + 0x02, 0xfe, 0x20, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x8e, 0x10, 0x50, + 0x00, 0x7c, 0x02, 0xfe, 0x20, 0x10, 0x50, 0x01, 0x7c, 0x02, 0xfe, 0x20, + 0x10, 0x50, 0x00, 0x7c, 0x02, 0xfe, 0x20, 0x10, 0x50, 0x01, 0x7c, 0x02, + 0xfe, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x02, 0xfe, 0x20, 0x10, 0x50, 0xff, + 0x7c, 0x02, 0xfe, 0x7c, 0x03, 0xda, 0x20, 0x50, 0x13, 0x08, 0x50, 0x88, + 0x08, 0x9c, 0x2e, 0x38, 0xfe, 0x38, 0xff, 0x20, 0x7f, 0x7f, 0x10, 0x4f, + 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, 0x00, 0x53, 0x38, 0x53, 0x37, + 0x55, 0x36, 0x10, 0x6f, 0xf9, 0x6f, 0xfa, 0xd0, 0x09, 0x52, 0xfc, 0x04, + 0x38, 0x52, 0xfb, 0x0c, 0x37, 0x66, 0xfc, 0x6c, 0xfb, 0x7a, 0x36, 0xbf, + 0xeb, 0x18, 0x60, 0xd0, 0x20, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x53, 0x3d, + 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, + 0x53, 0x3d, 0x7f, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, + 0x6b, 0x40, 0x7f, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, + 0x00, 0x7f, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, + 0x40, 0x3e, 0x3f, 0x7f, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, + 0x3f, 0x7f, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x7f, 0x0e, 0x40, 0x00, + 0x51, 0x40, 0x60, 0xd5, 0x7f, 0x51, 0x38, 0x53, 0x3d, 0x51, 0x37, 0x53, + 0x3e, 0x51, 0x3d, 0x02, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x7f, + 0x51, 0x3e, 0x3f, 0x3f, 0x51, 0x3d, 0x3f, 0x3f, 0x7f, 0x70, 0xfb, 0x6e, + 0x40, 0x6e, 0x3f, 0x51, 0x3e, 0x60, 0xd5, 0x51, 0x40, 0x3f, 0x3d, 0x51, + 0x3f, 0x3f, 0x3d, 0x7f, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x3d, 0x52, 0x15, + 0x3f, 0x3d, 0x7f, 0x53, 0x3d, 0x55, 0x3e, 0x00, 0x65, 0x3d, 0x6b, 0x3e, + 0x7f, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3b, 0x53, 0x3c, 0x3e, 0x3b, 0x53, + 0x3b, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x24, 0x00, 0x7f, 0x60, 0xd4, 0x3e, + 0x3d, 0x53, 0x3c, 0x3e, 0x3d, 0x16, 0x3d, 0x02, 0x7f, 0x12, 0x3d, 0x54, + 0x05, 0x51, 0x40, 0x1a, 0x3e, 0x54, 0x04, 0x7f, 0x12, 0x3d, 0x54, 0x03, + 0x51, 0x40, 0x1a, 0x3e, 0x54, 0x02, 0x7f, 0x53, 0x3f, 0x51, 0x3d, 0x14, + 0x3f, 0x51, 0x3e, 0x1c, 0x40, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x63, 0x00, + 0x55, 0x62, 0x00, 0x7f, 0x62, 0xd0, 0x00, 0x3c, 0x6f, 0x02, 0x7f, 0x62, + 0xd0, 0x00, 0x3c, 0x71, 0x01, 0x7f, 0x52, 0x14, 0x3f, 0x3f, 0x52, 0x15, + 0x3f, 0x3f, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x75, 0x00, 0x7f, 0x00, 0x24, + 0x00, 0x12, 0x00, 0x52, 0x00, 0x08, 0x00, 0x5e, 0x00, 0x07, 0x00, 0x65, + 0x05, 0x01, 0x08, 0x08, 0x64, 0x5a, 0x00, 0x6a, 0x00, 0x04, 0x00, 0x6e, + 0x01, 0x03, 0x00, 0x6f, 0x00, 0x05, 0x00, 0x74, 0x02, 0x01, 0x01, 0xff, + 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_Q1.h b/drivers/input/keyboard/cypressbln/touchkey_fw_Q1.h new file mode 100644 index 0000000..3c882b8 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_Q1.h @@ -0,0 +1,747 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x00, 0x68, + 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x03, 0x64, 0x7e, 0x7e, 0x30, + 0x30, 0x30, 0x7d, 0x04, 0xb7, 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, + 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x40, 0x71, 0x10, 0x62, 0xe3, 0x06, 0x70, + 0xef, 0x62, 0xe3, 0x38, 0x50, 0x80, 0x4e, 0x62, 0xe3, 0x38, 0x5d, + 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, 0xfa, 0x01, 0x40, 0x4f, 0x5b, + 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, 0x50, 0x06, 0x00, 0x40, + 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, 0x70, 0xef, 0x18, 0x60, + 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, 0x71, 0x10, 0x62, 0xe0, + 0x1a, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x71, 0x10, 0x41, 0xe1, 0xfe, + 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, 0x03, 0x50, 0x80, 0x4e, + 0x62, 0xd3, 0x03, 0x62, 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, + 0x00, 0x71, 0xc0, 0x7c, 0x02, 0xb3, 0x62, 0xd0, 0x00, 0x50, 0x02, + 0x57, 0x5c, 0x08, 0x28, 0x53, 0x48, 0x18, 0x75, 0x09, 0x00, 0x28, + 0x4b, 0x51, 0x48, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, + 0x08, 0x28, 0x60, 0xd5, 0x74, 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, + 0x08, 0x28, 0x53, 0x48, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xa0, + 0x1c, 0x53, 0x47, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x3f, 0x48, + 0x47, 0x48, 0xff, 0xb0, 0x06, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x18, + 0x7a, 0x47, 0xbf, 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, 0x00, 0x08, + 0x28, 0x53, 0x47, 0x50, 0x00, 0x3f, 0x48, 0x47, 0x48, 0xff, 0xb0, + 0x08, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x47, 0xbf, + 0xef, 0x18, 0x8f, 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, + 0x10, 0x62, 0xe0, 0x1a, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x17, + 0x9a, 0x8f, 0xff, 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x01, 0x80, 0x03, 0x00, 0x06, + 0x00, 0x0b, 0xff, 0x1b, 0x33, 0x2f, 0xff, 0x5f, 0xff, 0xbf, 0xff, + 0x01, 0x66, 0x02, 0xcc, 0x05, 0x99, 0x0b, 0x32, 0x16, 0x66, 0x2c, + 0xcc, 0x59, 0x98, 0xb3, 0x32, 0x01, 0x4c, 0x02, 0x99, 0x05, 0x33, + 0x0a, 0x65, 0x14, 0xcc, 0x29, 0x98, 0x53, 0x32, 0xa6, 0x65, 0x01, + 0x33, 0x02, 0x66, 0x04, 0xcc, 0x09, 0x99, 0x13, 0x33, 0x26, 0x65, + 0x4c, 0xcc, 0x99, 0x99, 0x61, 0x00, 0xfd, 0x00, 0xcd, 0x00, 0xce, + 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0xa1, 0x80, 0xa2, 0xc0, + 0xa3, 0x0c, 0xa8, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x7c, 0x33, 0x7a, + 0x00, 0x7b, 0x00, 0x79, 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, 0xca, 0x20, 0xd6, 0x44, 0xcf, 0x00, 0xcb, 0x00, 0xc8, + 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xd7, 0x00, 0xa9, 0x00, 0x2b, 0x00, + 0xb0, 0x00, 0xb3, 0x02, 0xb6, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, + 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0x33, 0x00, 0x34, 0x00, + 0x35, 0x00, 0xff, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0xdc, 0x00, 0xe2, + 0x00, 0xdd, 0x00, 0xd8, 0x02, 0xd9, 0x00, 0xda, 0x28, 0xdb, 0x00, + 0xdf, 0x00, 0x29, 0x00, 0x30, 0x00, 0xbd, 0x00, 0xff, 0x1a, 0x69, + 0x70, 0xef, 0x62, 0x00, 0x08, 0x71, 0x10, 0x62, 0x00, 0x00, 0x62, + 0x01, 0x92, 0x70, 0xef, 0x62, 0x04, 0x03, 0x71, 0x10, 0x62, 0x04, + 0x17, 0x62, 0x05, 0xab, 0x70, 0xef, 0x62, 0x08, 0x00, 0x71, 0x10, + 0x62, 0x08, 0x00, 0x62, 0x09, 0x28, 0x70, 0xef, 0x62, 0x0c, 0x00, + 0x71, 0x10, 0x62, 0x0c, 0x00, 0x62, 0x0d, 0x00, 0x70, 0xef, 0x62, + 0x10, 0x00, 0x71, 0x10, 0x62, 0x10, 0x00, 0x62, 0x11, 0x00, 0x70, + 0xef, 0x62, 0x01, 0x00, 0x62, 0x05, 0x00, 0x62, 0x09, 0x00, 0x62, + 0x0d, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, 0x7f, 0x55, 0x00, 0x08, + 0x55, 0x01, 0x03, 0x55, 0x02, 0x00, 0x7c, 0x02, 0xc3, 0x7c, 0x02, + 0x5e, 0x7f, 0x10, 0x70, 0xef, 0x50, 0x00, 0x67, 0x50, 0x01, 0x57, + 0xb2, 0x7c, 0x02, 0xde, 0x50, 0x01, 0x67, 0x50, 0x02, 0x57, 0x35, + 0x7c, 0x02, 0xde, 0x70, 0xef, 0x20, 0x7f, 0x38, 0x02, 0x10, 0x08, + 0x4f, 0x56, 0xfc, 0x00, 0xd0, 0x04, 0x56, 0xfc, 0x01, 0x18, 0x20, + 0x70, 0xef, 0x62, 0xe3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xff, 0xa0, + 0x1f, 0x4f, 0x48, 0xfc, 0x01, 0xa0, 0x03, 0x71, 0x10, 0x54, 0xfd, + 0x18, 0x20, 0x75, 0x09, 0x00, 0x10, 0x08, 0x28, 0x4f, 0x59, 0xfd, + 0x61, 0x00, 0x18, 0x20, 0x75, 0x09, 0x00, 0x8f, 0xd7, 0x38, 0xfc, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, + 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x01, 0xef, 0x80, + 0x04, 0x2e, 0x01, 0x10, 0x51, 0x01, 0x60, 0x04, 0x70, 0x3f, 0x71, + 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, 0x80, 0x03, + 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x01, + 0xfb, 0x80, 0x04, 0x2e, 0x01, 0x04, 0x51, 0x01, 0x60, 0x04, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x10, + 0x70, 0x3f, 0x71, 0x80, 0x5d, 0xd3, 0x08, 0x5d, 0xd0, 0x08, 0x62, + 0xd0, 0x00, 0x51, 0x06, 0x60, 0xd3, 0x2e, 0x03, 0x80, 0x49, 0xd7, + 0x08, 0xa0, 0x09, 0x26, 0x03, 0xf0, 0x2e, 0x03, 0x00, 0x80, 0x08, + 0x49, 0xd7, 0x20, 0xa0, 0x03, 0x80, 0xac, 0x51, 0x03, 0x21, 0x0e, + 0xe0, 0x01, 0x80, 0x11, 0x80, 0x6d, 0x80, 0x7f, 0x80, 0x4d, 0x80, + 0x9c, 0x80, 0x9a, 0x80, 0x98, 0x80, 0x96, 0x80, 0x9d, 0x5d, 0xd8, + 0x21, 0xfe, 0x39, 0x40, 0xa0, 0x06, 0x62, 0xd7, 0x00, 0x80, 0x90, + 0x49, 0xd8, 0x01, 0xb0, 0x15, 0x55, 0x0a, 0x02, 0x26, 0x05, 0x00, + 0x26, 0x04, 0x00, 0x26, 0x03, 0xf0, 0x2e, 0x03, 0x04, 0x62, 0xd7, + 0x10, 0x80, 0x77, 0x55, 0x0a, 0x01, 0x26, 0x03, 0xf0, 0x2e, 0x03, + 0x06, 0x5f, 0x05, 0x04, 0x51, 0x07, 0x02, 0x05, 0x5c, 0x52, 0x00, + 0x60, 0xd8, 0x76, 0x05, 0x62, 0xd7, 0x14, 0x80, 0x5b, 0x51, 0x08, + 0x78, 0x3a, 0x05, 0xc0, 0x0f, 0x51, 0x07, 0x02, 0x05, 0x5c, 0x52, + 0x00, 0x60, 0xd8, 0x76, 0x05, 0x2e, 0x03, 0x20, 0x60, 0xd8, 0x62, + 0xd7, 0x04, 0x80, 0x3f, 0x5d, 0xd8, 0x3a, 0x08, 0xd0, 0x2b, 0xa0, + 0x29, 0x53, 0x05, 0x53, 0x04, 0x26, 0x03, 0xf0, 0x2e, 0x03, 0x04, + 0x80, 0x18, 0x51, 0x09, 0x78, 0x3a, 0x05, 0xc0, 0x16, 0x51, 0x07, + 0x02, 0x05, 0x5c, 0x5d, 0xd8, 0x54, 0x00, 0x2e, 0x03, 0x10, 0x76, + 0x05, 0x80, 0x01, 0x62, 0xd7, 0x10, 0x80, 0x0f, 0x62, 0xd7, 0x00, + 0x80, 0x0a, 0x26, 0x03, 0xf0, 0x2e, 0x03, 0x00, 0x55, 0x0a, 0x00, + 0x18, 0x60, 0xd0, 0x18, 0x60, 0xd3, 0x20, 0x18, 0x7e, 0x62, 0xd0, + 0x00, 0x71, 0x10, 0x41, 0x04, 0xfc, 0x43, 0x05, 0x03, 0x70, 0xef, + 0x26, 0x01, 0xfc, 0x51, 0x01, 0x60, 0x04, 0x55, 0x0a, 0x00, 0x90, + 0x28, 0x90, 0x2d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x50, 0x00, 0x53, + 0x04, 0x71, 0x10, 0x43, 0x04, 0x03, 0x43, 0x05, 0x03, 0x70, 0xef, + 0x2e, 0x01, 0x03, 0x51, 0x01, 0x60, 0x04, 0x7f, 0x62, 0xd0, 0x00, + 0x51, 0x03, 0x21, 0xb0, 0x26, 0x03, 0x4f, 0x7f, 0x41, 0xe0, 0x7f, + 0x43, 0xe0, 0x80, 0x7f, 0x43, 0xd6, 0x31, 0x7f, 0x41, 0xe0, 0x7f, + 0x41, 0xd6, 0xfe, 0x7f, 0x62, 0xd0, 0x00, 0x4f, 0x52, 0xfd, 0x53, + 0x08, 0x52, 0xfc, 0x53, 0x09, 0x52, 0xfb, 0x53, 0x07, 0x52, 0xfa, + 0x53, 0x06, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x5d, 0xa4, 0x04, + 0x19, 0x5d, 0xa5, 0x0c, 0x18, 0x55, 0x1a, 0x01, 0x18, 0x7e, 0x70, + 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x53, 0x1c, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x52, 0x87, 0x62, 0xd3, 0x00, 0x13, 0x6b, 0x62, 0xd3, + 0x00, 0x54, 0x6f, 0x62, 0xd3, 0x00, 0x52, 0x86, 0x62, 0xd3, 0x00, + 0x1b, 0x6a, 0x62, 0xd3, 0x00, 0x54, 0x6e, 0x48, 0x6e, 0x80, 0xb0, + 0x33, 0x3d, 0x6e, 0x00, 0xb0, 0x7b, 0x51, 0x0b, 0x3b, 0x6f, 0xc0, + 0x75, 0x52, 0x6f, 0x58, 0x1c, 0x01, 0x00, 0x6d, 0x62, 0xd3, 0x00, + 0x05, 0x49, 0xc0, 0x09, 0x51, 0x0d, 0x3b, 0x49, 0xd0, 0x12, 0xa0, + 0x10, 0x56, 0x49, 0x00, 0x5b, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x07, + 0x6b, 0x01, 0x0f, 0x6a, 0x00, 0x80, 0x41, 0x3d, 0x6e, 0xff, 0xb0, + 0x09, 0x50, 0xff, 0x12, 0x0c, 0x3b, 0x6f, 0xc0, 0x20, 0x62, 0xd3, + 0x00, 0x56, 0x6f, 0x00, 0x56, 0x6e, 0x00, 0x5b, 0x67, 0x5c, 0x62, + 0xd3, 0x00, 0x52, 0x50, 0x78, 0xd0, 0x03, 0x50, 0x00, 0x54, 0x50, + 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, 0x62, 0xd3, 0x00, 0x52, + 0x87, 0x62, 0xd3, 0x00, 0x54, 0x6b, 0x62, 0xd3, 0x00, 0x52, 0x86, + 0x62, 0xd3, 0x00, 0x54, 0x6a, 0x51, 0x1c, 0x64, 0x5c, 0x62, 0xd3, + 0x00, 0x56, 0x6f, 0x00, 0x56, 0x6e, 0x00, 0x5b, 0x67, 0x5c, 0x62, + 0xd3, 0x00, 0x51, 0x10, 0x54, 0x50, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x08, 0x5c, 0x62, 0xd3, + 0x00, 0x52, 0x4b, 0x53, 0x17, 0x55, 0x16, 0x00, 0x18, 0x08, 0x90, + 0x7e, 0x62, 0xd3, 0x00, 0x23, 0x4d, 0xb0, 0x2c, 0x51, 0x0e, 0x04, + 0x17, 0x0e, 0x16, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, + 0x6f, 0x12, 0x17, 0x52, 0x6e, 0x1a, 0x16, 0xc0, 0x39, 0x5b, 0x67, + 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x4e, 0x78, 0x54, 0x4e, 0x08, 0x5b, + 0x64, 0x5c, 0x18, 0xb0, 0x3e, 0x80, 0x18, 0x51, 0x0e, 0x14, 0x17, + 0x1e, 0x16, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x6f, + 0x12, 0x17, 0x52, 0x6e, 0x1a, 0x16, 0xc0, 0x0e, 0x5b, 0x67, 0x90, + 0x31, 0x62, 0xd3, 0x00, 0x2d, 0x4d, 0x50, 0x01, 0x80, 0x24, 0x5b, + 0x67, 0x08, 0x90, 0x23, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x4d, 0x62, + 0xd3, 0x00, 0x20, 0x51, 0x0f, 0x54, 0x4e, 0x50, 0x00, 0x80, 0x0d, + 0x5b, 0x67, 0x90, 0x0d, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x4d, 0x50, + 0x00, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x67, 0x67, 0x67, 0x5c, + 0x18, 0x21, 0x07, 0xf0, 0x01, 0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x80, 0x70, 0xbf, 0x70, 0xbf, 0x62, 0xd3, 0x00, 0x50, + 0x02, 0x78, 0x08, 0x5c, 0x56, 0x4b, 0x1e, 0x18, 0x78, 0xdf, 0xf8, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x91, 0xb2, 0x70, 0xbf, 0x18, + 0x08, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x87, 0x62, 0xd3, 0x00, + 0x54, 0x6b, 0x62, 0xd3, 0x00, 0x52, 0x86, 0x62, 0xd3, 0x00, 0x54, + 0x6a, 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, + 0xd0, 0x00, 0x55, 0x12, 0x00, 0x50, 0x02, 0x78, 0x08, 0x9f, 0x0e, + 0x39, 0x01, 0xb0, 0x04, 0x55, 0x12, 0x01, 0x18, 0x78, 0xdf, 0xf3, + 0x51, 0x12, 0x7f, 0x50, 0x02, 0x78, 0x08, 0x9e, 0x3e, 0x18, 0x78, + 0xdf, 0xfa, 0x7f, 0x98, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0xd8, 0xd9, 0xda, 0xdb, 0xdf, 0x00, 0x01, 0x03, 0x07, 0x0f, + 0x1f, 0x3f, 0x7f, 0xff, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x62, 0xd3, + 0x00, 0x57, 0x00, 0x56, 0x4d, 0x00, 0x79, 0xdf, 0xfb, 0x62, 0xd3, + 0x00, 0x57, 0x01, 0x50, 0x03, 0x54, 0x4e, 0x79, 0xdf, 0xfc, 0x62, + 0xd3, 0x00, 0x50, 0x32, 0x57, 0x01, 0x54, 0x50, 0x79, 0xdf, 0xfc, + 0x70, 0x3f, 0x71, 0xc0, 0x55, 0x0b, 0x19, 0x55, 0x0c, 0x05, 0x55, + 0x0d, 0x14, 0x55, 0x0e, 0x01, 0x55, 0x0f, 0x03, 0x55, 0x10, 0x32, + 0x55, 0x20, 0x04, 0x55, 0x1d, 0x14, 0x43, 0x61, 0x0d, 0x57, 0x00, + 0x50, 0x02, 0x90, 0xae, 0x50, 0x04, 0xff, 0x98, 0x29, 0x00, 0x60, + 0xa9, 0x62, 0xa0, 0x08, 0x43, 0xa2, 0x04, 0x62, 0xa3, 0x70, 0x43, + 0x7a, 0x01, 0x43, 0xaa, 0x02, 0x43, 0xdf, 0x01, 0x50, 0x01, 0x57, + 0x09, 0x90, 0x20, 0x90, 0x55, 0x57, 0x01, 0x50, 0xb3, 0x91, 0x5d, + 0x50, 0x00, 0x57, 0x0d, 0x90, 0x12, 0x90, 0x47, 0x7f, 0x53, 0x20, + 0xff, 0x67, 0x29, 0x00, 0x60, 0xa9, 0x51, 0x1f, 0x58, 0x1e, 0x90, + 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, 0x03, 0x53, 0x1f, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x29, 0x80, 0x60, 0xa1, 0x5b, 0x78, 0x21, 0x0f, + 0x29, 0x08, 0x74, 0x53, 0x1e, 0x12, 0x20, 0x02, 0x1f, 0x5c, 0x50, + 0x00, 0x53, 0x1b, 0x53, 0x21, 0x29, 0x01, 0x79, 0xa0, 0x08, 0x64, + 0x6b, 0x1b, 0x6b, 0x21, 0x8f, 0xf5, 0x60, 0xb5, 0x51, 0x1b, 0x60, + 0xb4, 0x7f, 0x50, 0x02, 0x78, 0x08, 0x90, 0x28, 0x90, 0x5a, 0x18, + 0x78, 0xdf, 0xf8, 0x7f, 0x41, 0xdf, 0xfe, 0x71, 0x10, 0x41, 0xd8, + 0xfd, 0x70, 0xef, 0x41, 0x61, 0xf3, 0x41, 0xa2, 0xfb, 0x41, 0xa0, + 0xf7, 0x62, 0xa3, 0x00, 0x62, 0xa9, 0x00, 0x41, 0xaa, 0xfd, 0x7f, + 0x02, 0x20, 0x02, 0x08, 0x64, 0x5c, 0xff, 0xf8, 0x4b, 0x74, 0xff, + 0xf4, 0x7f, 0x62, 0xd0, 0x00, 0x53, 0x1b, 0x10, 0x5b, 0x64, 0x64, + 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x2a, 0x1b, 0x61, 0x01, 0x36, 0x1b, + 0xff, 0x5e, 0x00, 0x22, 0x1b, 0x61, 0x00, 0x36, 0x1b, 0xff, 0x18, + 0xfe, 0xd6, 0x5c, 0x5e, 0x00, 0x2a, 0x1b, 0x61, 0x00, 0x70, 0xef, + 0x7f, 0x62, 0xd0, 0x00, 0x10, 0x73, 0x53, 0x1b, 0x71, 0x10, 0x5b, + 0xfe, 0xc0, 0x5c, 0x5e, 0x00, 0x22, 0x1b, 0x61, 0x00, 0x70, 0xef, + 0x18, 0x64, 0x64, 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x22, 0x1b, 0x61, + 0x01, 0x36, 0x1b, 0xff, 0x5e, 0x00, 0x2a, 0x1b, 0x61, 0x00, 0x70, + 0xef, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, 0x1c, 0x50, 0x00, + 0x53, 0x18, 0x53, 0x19, 0x51, 0x1c, 0x5c, 0x62, 0xd3, 0x00, 0x52, + 0x22, 0x53, 0x1d, 0x43, 0xa0, 0x01, 0x51, 0x1d, 0x60, 0xfd, 0x41, + 0xa3, 0xdf, 0x51, 0x1c, 0x9f, 0x7a, 0x9f, 0x81, 0x58, 0x21, 0x55, + 0x1a, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x43, 0xb3, 0x01, + 0x51, 0x1a, 0xaf, 0xfd, 0x79, 0xdf, 0xee, 0x51, 0x1c, 0x9f, 0x5f, + 0x9f, 0x91, 0x43, 0xa3, 0x20, 0x41, 0xa0, 0xfe, 0x62, 0xfd, 0x00, + 0x50, 0xff, 0x4c, 0x19, 0x14, 0x19, 0x51, 0x1e, 0x11, 0x08, 0xfe, + 0x4d, 0x4c, 0x18, 0x1c, 0x18, 0xd0, 0x07, 0x55, 0x18, 0x00, 0x55, + 0x19, 0x00, 0x51, 0x1c, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x19, + 0x54, 0x87, 0x51, 0x18, 0x54, 0x86, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x08, 0x9f, 0x86, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x70, 0xbf, 0x62, + 0xd0, 0x00, 0x53, 0x25, 0x5a, 0x24, 0x55, 0x1c, 0x01, 0x62, 0xd3, + 0x00, 0x58, 0x1c, 0x56, 0x22, 0x80, 0x55, 0x27, 0x08, 0x55, 0x26, + 0x80, 0x51, 0x1c, 0x9f, 0x63, 0x51, 0x1c, 0x9f, 0x5f, 0x70, 0xbf, + 0x58, 0x1c, 0x62, 0xd3, 0x00, 0x51, 0x19, 0x3a, 0x25, 0x51, 0x18, + 0x1a, 0x24, 0xd0, 0x06, 0x51, 0x26, 0x73, 0x25, 0x22, 0x68, 0x26, + 0x26, 0x26, 0x7f, 0x51, 0x26, 0x2d, 0x22, 0x7a, 0x27, 0xbf, 0xd6, + 0x7a, 0x1c, 0xdf, 0xc4, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, 0xd0, + 0x00, 0x51, 0x8b, 0x11, 0xb0, 0x51, 0x8a, 0x19, 0x04, 0xd0, 0x12, + 0x7c, 0x13, 0x75, 0x39, 0x0f, 0xa0, 0x16, 0x62, 0xd0, 0x00, 0x76, + 0x8b, 0x0e, 0x8a, 0x00, 0x80, 0x0c, 0x62, 0xd0, 0x00, 0x55, 0x8b, + 0x00, 0x55, 0x8a, 0x00, 0x90, 0xa1, 0x7f, 0x62, 0xd0, 0x00, 0x3c, + 0x94, 0xf0, 0xd0, 0x03, 0x76, 0x94, 0x62, 0xd0, 0x00, 0x51, 0x2d, + 0x21, 0x7f, 0x53, 0x48, 0x51, 0x94, 0x3a, 0x48, 0xb0, 0x50, 0x7c, + 0x13, 0x75, 0x62, 0xd0, 0x00, 0x53, 0x95, 0x3c, 0x95, 0x0f, 0xa0, + 0x3d, 0x3c, 0x8c, 0x00, 0xb0, 0x1c, 0x55, 0x82, 0x00, 0x55, 0x83, + 0x00, 0x51, 0x95, 0x53, 0x47, 0x55, 0x48, 0x00, 0x06, 0x47, 0x82, + 0x0e, 0x48, 0x00, 0x51, 0x48, 0x60, 0xd5, 0x50, 0x08, 0x3f, 0x47, + 0x62, 0xd0, 0x00, 0x55, 0x8f, 0x00, 0x3c, 0x93, 0x00, 0xb0, 0x0a, + 0x7c, 0x14, 0x06, 0x62, 0xd0, 0x00, 0x55, 0x93, 0x01, 0x62, 0xd0, + 0x00, 0x55, 0x8e, 0x03, 0x80, 0x07, 0x62, 0xd0, 0x00, 0x55, 0x94, + 0x00, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x8b, 0x00, 0x55, 0x8a, 0x00, + 0x3c, 0x93, 0x01, 0xb0, 0x29, 0x7a, 0x8e, 0x3c, 0x8e, 0x00, 0xb0, + 0x22, 0x7c, 0x14, 0xa2, 0x62, 0xd0, 0x00, 0x55, 0x93, 0x00, 0x3c, + 0x8c, 0x00, 0xb0, 0x0e, 0x51, 0x95, 0x53, 0x47, 0x55, 0x48, 0x00, + 0x06, 0x47, 0x82, 0x7c, 0x1a, 0x37, 0x62, 0xd0, 0x00, 0x55, 0x94, + 0x00, 0x7f, 0x10, 0x4f, 0x38, 0x16, 0x62, 0xd0, 0x00, 0x3c, 0x91, + 0x00, 0xb0, 0x05, 0x51, 0x7a, 0x53, 0x22, 0x56, 0x0d, 0x00, 0x81, + 0x09, 0x56, 0x00, 0x00, 0x80, 0xfd, 0x62, 0xd0, 0x00, 0x3c, 0x91, + 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x47, 0x55, 0x48, 0x00, 0x06, + 0x47, 0x7a, 0x7c, 0x19, 0x0f, 0x52, 0x00, 0x53, 0x45, 0x55, 0x46, + 0x00, 0x06, 0x45, 0x22, 0x7c, 0x1a, 0x01, 0x10, 0x52, 0x00, 0x7c, + 0x07, 0xf6, 0x20, 0x10, 0x7c, 0x04, 0x81, 0x62, 0xd0, 0x00, 0x20, + 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, 0x00, 0xb0, 0x3a, 0x7c, 0x18, + 0xb9, 0x7c, 0x18, 0xe1, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x51, + 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, 0x50, 0x00, 0x08, 0x50, + 0x07, 0x08, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x7c, 0x18, 0x45, + 0x18, 0x53, 0x45, 0x18, 0x53, 0x46, 0x38, 0xfe, 0x7c, 0x18, 0xf7, + 0x3d, 0x00, 0x01, 0xb0, 0x3d, 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, + 0x7c, 0x18, 0xe1, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x03, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x51, 0x40, + 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, 0x50, 0x00, 0x08, 0x50, 0x07, + 0x08, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x7c, 0x18, 0x45, 0x18, + 0x53, 0x45, 0x18, 0x53, 0x46, 0x38, 0xfe, 0x7c, 0x18, 0xf7, 0x62, + 0xd0, 0x00, 0x55, 0x48, 0x03, 0x5a, 0x47, 0x06, 0x47, 0x01, 0x52, + 0x00, 0x53, 0x45, 0x50, 0x00, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x7c, 0x19, + 0x9e, 0x53, 0x48, 0x52, 0x0d, 0x53, 0x45, 0x55, 0x46, 0x00, 0x65, + 0x45, 0x6b, 0x46, 0x7c, 0x19, 0xb1, 0x53, 0x48, 0x7c, 0x19, 0xf5, + 0x06, 0x45, 0x86, 0x0e, 0x46, 0x00, 0x51, 0x46, 0x7c, 0x19, 0x04, + 0x7c, 0x18, 0xf7, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x00, 0x77, + 0x0d, 0x3d, 0x0d, 0x03, 0xce, 0xf4, 0x56, 0x00, 0x00, 0x80, 0xc0, + 0x62, 0xd0, 0x00, 0x55, 0x48, 0x03, 0x5a, 0x47, 0x06, 0x47, 0x01, + 0x52, 0x00, 0x53, 0x45, 0x50, 0x00, 0x08, 0x51, 0x45, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x7c, + 0x19, 0x9e, 0x60, 0xd4, 0x3e, 0x47, 0x54, 0x0e, 0x3e, 0x47, 0x54, + 0x0f, 0x5a, 0x47, 0x06, 0x47, 0x03, 0x52, 0x00, 0x53, 0x45, 0x50, + 0x00, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, + 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x7c, 0x19, 0x9e, 0x60, 0xd4, 0x3e, + 0x47, 0x54, 0x10, 0x3e, 0x47, 0x54, 0x11, 0x5a, 0x47, 0x06, 0x47, + 0x05, 0x52, 0x00, 0x53, 0x45, 0x50, 0x00, 0x08, 0x51, 0x45, 0x08, + 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, + 0x7c, 0x19, 0x9e, 0x60, 0xd4, 0x3e, 0x47, 0x54, 0x12, 0x3e, 0x47, + 0x54, 0x13, 0x50, 0x03, 0x08, 0x5a, 0x47, 0x06, 0x47, 0x0e, 0x08, + 0x51, 0x47, 0x08, 0x7c, 0x17, 0x38, 0x38, 0xfd, 0x62, 0xd0, 0x00, + 0x51, 0x47, 0x54, 0x15, 0x51, 0x48, 0x54, 0x14, 0x7c, 0x18, 0xb9, + 0x7c, 0x19, 0x93, 0x7c, 0x19, 0xda, 0x06, 0x47, 0x6a, 0x7c, 0x19, + 0xe5, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x62, 0x7c, 0x19, 0xbc, + 0x51, 0x47, 0x01, 0x52, 0x7c, 0x19, 0xbc, 0x06, 0x47, 0x5a, 0x7c, + 0x19, 0xe5, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x3d, 0x38, 0xea, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x16, 0x62, 0xd0, 0x00, 0x50, 0x01, + 0x10, 0x08, 0x57, 0x8a, 0x28, 0x53, 0x48, 0x18, 0x75, 0x09, 0x00, + 0x28, 0x53, 0x47, 0x20, 0x10, 0x51, 0x48, 0x08, 0x51, 0x47, 0x20, + 0x7c, 0x08, 0x75, 0x20, 0x62, 0xd0, 0x00, 0x3c, 0x91, 0x01, 0xb0, + 0x0b, 0x51, 0x22, 0x53, 0x2e, 0x51, 0x23, 0x53, 0x2f, 0x80, 0x0c, + 0x62, 0xd0, 0x00, 0x51, 0x7a, 0x53, 0x22, 0x51, 0x7b, 0x53, 0x23, + 0x10, 0x50, 0x00, 0x7c, 0x07, 0xf6, 0x20, 0x56, 0x0d, 0x00, 0x81, + 0x09, 0x56, 0x00, 0x00, 0x80, 0xfd, 0x62, 0xd0, 0x00, 0x3c, 0x91, + 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x47, 0x55, 0x48, 0x00, 0x06, + 0x47, 0x7a, 0x7c, 0x19, 0x0f, 0x52, 0x00, 0x53, 0x45, 0x55, 0x46, + 0x00, 0x06, 0x45, 0x22, 0x7c, 0x1a, 0x01, 0x10, 0x52, 0x00, 0x7c, + 0x07, 0xf6, 0x20, 0x10, 0x7c, 0x04, 0x81, 0x62, 0xd0, 0x00, 0x20, + 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, 0x00, 0xb0, 0x3a, 0x7c, 0x18, + 0xb9, 0x7c, 0x18, 0xe1, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x51, + 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, 0x50, 0x00, 0x08, 0x50, + 0x07, 0x08, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x7c, 0x18, 0x45, + 0x18, 0x53, 0x45, 0x18, 0x53, 0x46, 0x38, 0xfe, 0x7c, 0x18, 0xf7, + 0x3d, 0x00, 0x01, 0xb0, 0x3d, 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, + 0x7c, 0x18, 0xe1, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x03, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x51, 0x40, + 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, 0x50, 0x00, 0x08, 0x50, 0x07, + 0x08, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, 0x7c, 0x18, 0x45, 0x18, + 0x53, 0x45, 0x18, 0x53, 0x46, 0x38, 0xfe, 0x7c, 0x18, 0xf7, 0x62, + 0xd0, 0x00, 0x55, 0x48, 0x03, 0x5a, 0x47, 0x06, 0x47, 0x01, 0x52, + 0x00, 0x53, 0x45, 0x50, 0x00, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x7c, 0x19, + 0x9e, 0x53, 0x48, 0x52, 0x0d, 0x53, 0x45, 0x55, 0x46, 0x00, 0x65, + 0x45, 0x6b, 0x46, 0x7c, 0x19, 0xb1, 0x53, 0x48, 0x7c, 0x19, 0xf5, + 0x06, 0x45, 0x86, 0x0e, 0x46, 0x00, 0x51, 0x46, 0x7c, 0x19, 0x04, + 0x7c, 0x18, 0xf7, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x00, 0x77, + 0x0d, 0x3d, 0x0d, 0x03, 0xce, 0xf4, 0x56, 0x00, 0x00, 0x80, 0xc0, + 0x62, 0xd0, 0x00, 0x55, 0x48, 0x03, 0x5a, 0x47, 0x06, 0x47, 0x01, + 0x52, 0x00, 0x53, 0x45, 0x50, 0x00, 0x08, 0x51, 0x45, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x7c, + 0x19, 0x9e, 0x60, 0xd4, 0x3e, 0x47, 0x54, 0x0e, 0x3e, 0x47, 0x54, + 0x0f, 0x5a, 0x47, 0x06, 0x47, 0x03, 0x52, 0x00, 0x53, 0x45, 0x50, + 0x00, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, + 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x7c, 0x19, 0x9e, 0x60, 0xd4, 0x3e, + 0x47, 0x54, 0x10, 0x3e, 0x47, 0x54, 0x11, 0x5a, 0x47, 0x06, 0x47, + 0x05, 0x52, 0x00, 0x53, 0x45, 0x50, 0x00, 0x08, 0x51, 0x45, 0x08, + 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x18, 0x89, 0x38, 0xfc, + 0x7c, 0x19, 0x9e, 0x60, 0xd4, 0x3e, 0x47, 0x54, 0x12, 0x3e, 0x47, + 0x54, 0x13, 0x50, 0x03, 0x08, 0x5a, 0x47, 0x06, 0x47, 0x0e, 0x08, + 0x51, 0x47, 0x08, 0x7c, 0x17, 0x38, 0x38, 0xfd, 0x62, 0xd0, 0x00, + 0x51, 0x47, 0x54, 0x15, 0x51, 0x48, 0x54, 0x14, 0x7c, 0x18, 0xb9, + 0x7c, 0x19, 0x93, 0x7c, 0x19, 0xda, 0x06, 0x47, 0x6a, 0x7c, 0x19, + 0xe5, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x62, 0x7c, 0x19, 0xbc, + 0x51, 0x47, 0x01, 0x52, 0x7c, 0x19, 0xbc, 0x06, 0x47, 0x5a, 0x7c, + 0x19, 0xe5, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x3d, 0x56, 0x00, + 0x00, 0x80, 0x19, 0x7c, 0x18, 0xd6, 0x06, 0x47, 0x22, 0x7c, 0x19, + 0x0f, 0x52, 0x00, 0x53, 0x45, 0x55, 0x46, 0x00, 0x06, 0x45, 0x2e, + 0x7c, 0x1a, 0x01, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0xe4, 0x38, + 0xea, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x0a, 0x62, 0xd0, 0x00, 0x55, + 0x8d, 0x00, 0x3c, 0x91, 0x00, 0xb0, 0x05, 0x51, 0x7a, 0x53, 0x22, + 0x10, 0x50, 0x00, 0x7c, 0x07, 0xf6, 0x20, 0x56, 0x00, 0x00, 0x80, + 0xb7, 0x62, 0xd0, 0x00, 0x3c, 0x91, 0x00, 0xb0, 0x1b, 0x52, 0x00, + 0x53, 0x47, 0x55, 0x48, 0x00, 0x06, 0x47, 0x7a, 0x7c, 0x19, 0x0f, + 0x52, 0x00, 0x53, 0x45, 0x55, 0x46, 0x00, 0x06, 0x45, 0x22, 0x7c, + 0x1a, 0x01, 0x10, 0x52, 0x00, 0x7c, 0x07, 0xf6, 0x20, 0x10, 0x7c, + 0x04, 0x81, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x3d, + 0x00, 0x00, 0xb0, 0x3a, 0x7c, 0x18, 0xb9, 0x7c, 0x18, 0xe1, 0x51, + 0x46, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, + 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, + 0x53, 0x46, 0x50, 0x00, 0x08, 0x50, 0x07, 0x08, 0x51, 0x46, 0x08, + 0x51, 0x45, 0x08, 0x7c, 0x18, 0x45, 0x18, 0x53, 0x45, 0x18, 0x53, + 0x46, 0x38, 0xfe, 0x7c, 0x18, 0xf7, 0x3d, 0x00, 0x01, 0xb0, 0x3d, + 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, 0x7c, 0x18, 0xe1, 0x51, 0x46, + 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x7c, + 0x18, 0x89, 0x38, 0xfc, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, + 0x46, 0x50, 0x00, 0x08, 0x50, 0x07, 0x08, 0x51, 0x46, 0x08, 0x51, + 0x45, 0x08, 0x7c, 0x18, 0x45, 0x18, 0x53, 0x45, 0x18, 0x53, 0x46, + 0x38, 0xfe, 0x7c, 0x18, 0xf7, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, + 0x46, 0x56, 0x00, 0x00, 0x82, 0xf8, 0x62, 0xd0, 0x00, 0x3c, 0x96, + 0x02, 0xa0, 0x9f, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x6a, 0x7c, + 0x18, 0xc5, 0x06, 0x47, 0x86, 0x7c, 0x19, 0x0f, 0x7c, 0x19, 0xcd, + 0xd0, 0x16, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x6a, 0x7c, 0x18, + 0xc5, 0x06, 0x47, 0x86, 0x7c, 0x19, 0x0f, 0x7c, 0x1a, 0x50, 0x80, + 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, + 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x6a, 0x7c, 0x19, 0x0f, 0x7c, 0x1a, + 0x50, 0x50, 0x90, 0x13, 0x07, 0x50, 0x01, 0x1b, 0x06, 0xc0, 0x4e, + 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x5a, 0x7c, + 0x18, 0xc5, 0x06, 0x47, 0x86, 0x7c, 0x19, 0x0f, 0x7c, 0x19, 0xcd, + 0xd0, 0x16, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x5a, 0x7c, 0x18, + 0xc5, 0x06, 0x47, 0x86, 0x7c, 0x19, 0x0f, 0x7c, 0x1a, 0x43, 0x80, + 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, + 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x5a, 0x7c, 0x19, 0x0f, 0x7c, 0x1a, + 0x43, 0x50, 0x90, 0x13, 0x09, 0x50, 0x01, 0x1b, 0x08, 0xd0, 0x08, + 0x62, 0xd0, 0x00, 0x76, 0x8d, 0x82, 0x50, 0x62, 0xd0, 0x00, 0x7c, + 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, 0x7c, 0x18, 0xc5, 0x06, 0x47, + 0x5a, 0x7c, 0x19, 0x0f, 0x7c, 0x19, 0xcd, 0xd0, 0x7e, 0x7c, 0x18, + 0xb9, 0x7c, 0x18, 0xe1, 0x06, 0x45, 0x01, 0x0e, 0x46, 0x00, 0x7c, + 0x18, 0xf7, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, 0x7c, 0x18, + 0xc5, 0x06, 0x47, 0x5a, 0x7c, 0x19, 0x0f, 0x7c, 0x19, 0xcd, 0xd1, + 0x01, 0x7c, 0x18, 0xb9, 0x7c, 0x18, 0xe1, 0x06, 0x45, 0x01, 0x0e, + 0x46, 0x00, 0x7c, 0x18, 0xf7, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, + 0x86, 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x5a, 0x7c, 0x19, 0x0f, 0x7c, + 0x19, 0xcd, 0xd0, 0xdd, 0x7c, 0x18, 0xb9, 0x7c, 0x18, 0xe1, 0x06, + 0x45, 0x01, 0x0e, 0x46, 0x00, 0x7c, 0x18, 0xf7, 0x7c, 0x18, 0xb9, + 0x51, 0x47, 0x01, 0x86, 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x5a, 0x7c, + 0x19, 0x0f, 0x7c, 0x19, 0xcd, 0xd0, 0xb9, 0x7c, 0x18, 0xb9, 0x7c, + 0x18, 0xe1, 0x06, 0x45, 0x01, 0x0e, 0x46, 0x00, 0x7c, 0x18, 0xf7, + 0x80, 0xa8, 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, + 0x86, 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x5a, 0x7c, 0x19, 0x0f, 0x3e, + 0x47, 0x12, 0x45, 0x51, 0x48, 0x1a, 0x46, 0xd0, 0x8b, 0x7c, 0x18, + 0xb9, 0x7c, 0x18, 0xe1, 0x16, 0x45, 0x01, 0x1e, 0x46, 0x00, 0x7c, + 0x18, 0xf7, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, 0x7c, 0x18, + 0xc5, 0x06, 0x47, 0x5a, 0x7c, 0x19, 0x0f, 0x3e, 0x47, 0x12, 0x45, + 0x51, 0x48, 0x1a, 0x46, 0xd0, 0x62, 0x7c, 0x18, 0xb9, 0x7c, 0x18, + 0xe1, 0x16, 0x45, 0x01, 0x1e, 0x46, 0x00, 0x7c, 0x18, 0xf7, 0x7c, + 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, 0x7c, 0x18, 0xc5, 0x06, 0x47, + 0x5a, 0x7c, 0x19, 0x0f, 0x3e, 0x47, 0x12, 0x45, 0x51, 0x48, 0x1a, + 0x46, 0xd0, 0x39, 0x7c, 0x18, 0xb9, 0x7c, 0x18, 0xe1, 0x16, 0x45, + 0x01, 0x1e, 0x46, 0x00, 0x7c, 0x18, 0xf7, 0x7c, 0x18, 0xb9, 0x51, + 0x47, 0x01, 0x86, 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x5a, 0x7c, 0x19, + 0x0f, 0x3e, 0x47, 0x12, 0x45, 0x51, 0x48, 0x1a, 0x46, 0xd0, 0x10, + 0x7c, 0x18, 0xb9, 0x7c, 0x18, 0xe1, 0x16, 0x45, 0x01, 0x1e, 0x46, + 0x00, 0x7c, 0x18, 0xf7, 0x62, 0xd0, 0x00, 0x7c, 0x18, 0xb9, 0x51, + 0x47, 0x01, 0x52, 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x62, 0x0e, 0x48, + 0x00, 0x7c, 0x18, 0xf7, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x5a, + 0x7c, 0x18, 0xc5, 0x06, 0x47, 0x52, 0x0e, 0x48, 0x00, 0x7c, 0x18, + 0xf7, 0x7c, 0x18, 0xb9, 0x51, 0x47, 0x01, 0x86, 0x7c, 0x18, 0xc5, + 0x06, 0x47, 0x5a, 0x0e, 0x48, 0x00, 0x7c, 0x18, 0xf7, 0x97, 0xf6, + 0x40, 0x7c, 0x19, 0x93, 0x53, 0x46, 0x7c, 0x1a, 0x5d, 0x53, 0x43, + 0x08, 0x51, 0x44, 0x53, 0x42, 0x18, 0x53, 0x41, 0x65, 0x41, 0x6b, + 0x42, 0x06, 0x47, 0x52, 0x7c, 0x19, 0x0f, 0x3e, 0x47, 0x53, 0x47, + 0x51, 0x41, 0x04, 0x47, 0x51, 0x42, 0x0c, 0x48, 0x51, 0x43, 0x04, + 0x47, 0x51, 0x44, 0x0c, 0x48, 0x70, 0xfb, 0x6e, 0x48, 0x6e, 0x47, + 0x7c, 0x1a, 0x24, 0x10, 0x52, 0x00, 0x7c, 0x04, 0xc5, 0x20, 0x62, + 0xd0, 0x00, 0x97, 0xb0, 0x40, 0x51, 0x47, 0x01, 0x86, 0x97, 0xb5, + 0x40, 0x06, 0x47, 0x6a, 0x97, 0xf9, 0x40, 0x7c, 0x19, 0xcd, 0xd0, + 0x25, 0x52, 0x00, 0x53, 0x47, 0x55, 0x48, 0x00, 0x06, 0x47, 0x76, + 0x0e, 0x48, 0x00, 0x51, 0x48, 0x60, 0xd4, 0x3e, 0x47, 0x7a, 0x47, + 0x53, 0x46, 0x06, 0x46, 0x01, 0x51, 0x48, 0x60, 0xd5, 0x51, 0x46, + 0x3f, 0x47, 0x80, 0x0a, 0x97, 0x94, 0x40, 0x06, 0x47, 0x76, 0x7c, + 0x1a, 0x37, 0x97, 0x8b, 0x40, 0x06, 0x47, 0x76, 0x97, 0xbe, 0x40, + 0x50, 0x05, 0x3a, 0x48, 0xd0, 0x41, 0x97, 0x5f, 0x40, 0x51, 0x47, + 0x01, 0x6a, 0x53, 0x45, 0x51, 0x48, 0x09, 0x00, 0x53, 0x46, 0x06, + 0x47, 0x86, 0x97, 0xa3, 0x40, 0x3e, 0x47, 0x53, 0x47, 0x51, 0x46, + 0x7c, 0x1a, 0x5d, 0x02, 0x47, 0x53, 0x47, 0x51, 0x44, 0x0a, 0x48, + 0x53, 0x48, 0x7c, 0x1a, 0x24, 0x52, 0x00, 0x53, 0x47, 0x55, 0x48, + 0x00, 0x06, 0x47, 0x76, 0x0e, 0x48, 0x00, 0x51, 0x48, 0x60, 0xd5, + 0x50, 0x00, 0x3f, 0x47, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcd, 0x05, + 0x62, 0xd0, 0x00, 0x3c, 0x96, 0x02, 0xb1, 0x40, 0x56, 0x00, 0x00, + 0x81, 0x36, 0x3d, 0x00, 0x00, 0xb0, 0x99, 0x62, 0xd0, 0x00, 0x97, + 0x03, 0x40, 0x51, 0x47, 0x01, 0x86, 0x97, 0x08, 0x40, 0x51, 0x46, + 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x07, 0x08, 0x7c, + 0x18, 0x89, 0x38, 0xfc, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, + 0x46, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x51, 0x46, 0x08, 0x51, + 0x45, 0x08, 0x7c, 0x18, 0x45, 0x18, 0x53, 0x45, 0x18, 0x53, 0x46, + 0x38, 0xfe, 0x06, 0x47, 0x36, 0x0e, 0x48, 0x00, 0x97, 0x01, 0x40, + 0x96, 0xc0, 0x40, 0x51, 0x47, 0x01, 0x6a, 0x96, 0xc5, 0x40, 0x51, + 0x46, 0x08, 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x07, 0x08, + 0x7c, 0x18, 0x89, 0x38, 0xfc, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, + 0x53, 0x46, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x51, 0x46, 0x08, + 0x51, 0x45, 0x08, 0x7c, 0x18, 0x45, 0x18, 0x53, 0x45, 0x18, 0x53, + 0x46, 0x38, 0xfe, 0x06, 0x47, 0x3a, 0x0e, 0x48, 0x00, 0x96, 0xbe, + 0x40, 0x96, 0x7d, 0x40, 0x51, 0x47, 0x01, 0x6a, 0x96, 0x82, 0x40, + 0x96, 0xd5, 0x40, 0x80, 0x97, 0x62, 0xd0, 0x00, 0x96, 0x6b, 0x40, + 0x51, 0x47, 0x01, 0x86, 0x96, 0x70, 0x40, 0x51, 0x46, 0x08, 0x51, + 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x07, 0x08, 0x7c, 0x18, 0x89, + 0x38, 0xfc, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, 0x50, + 0x00, 0x08, 0x50, 0x03, 0x08, 0x51, 0x46, 0x08, 0x51, 0x45, 0x08, + 0x7c, 0x18, 0x45, 0x18, 0x53, 0x45, 0x18, 0x53, 0x46, 0x38, 0xfe, + 0x06, 0x47, 0x36, 0x0e, 0x48, 0x00, 0x96, 0x69, 0x40, 0x96, 0x28, + 0x40, 0x51, 0x47, 0x01, 0x6a, 0x96, 0x2d, 0x40, 0x51, 0x46, 0x08, + 0x51, 0x45, 0x08, 0x50, 0x00, 0x08, 0x50, 0x07, 0x08, 0x7c, 0x18, + 0x89, 0x38, 0xfc, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, + 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x51, 0x46, 0x08, 0x51, 0x45, + 0x08, 0x7c, 0x18, 0x45, 0x18, 0x53, 0x45, 0x18, 0x53, 0x46, 0x38, + 0xfe, 0x06, 0x47, 0x3a, 0x0e, 0x48, 0x00, 0x96, 0x26, 0x40, 0x95, + 0xe5, 0x40, 0x51, 0x47, 0x01, 0x6a, 0x95, 0xea, 0x40, 0x96, 0x3d, + 0x40, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0xc7, 0x62, 0xd0, 0x00, + 0x3c, 0x96, 0x02, 0xa0, 0x18, 0x3c, 0x8d, 0x00, 0xa0, 0x13, 0x50, + 0x75, 0x08, 0x50, 0x30, 0x08, 0x90, 0x0e, 0x38, 0xfe, 0x7c, 0x09, + 0x8d, 0x10, 0x7c, 0x06, 0x81, 0x20, 0x38, 0xf6, 0x20, 0x7f, 0x10, + 0x4f, 0x80, 0x02, 0x40, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, 0x47, + 0x52, 0xfb, 0x53, 0x48, 0x51, 0x47, 0x11, 0x01, 0x54, 0xfc, 0x51, + 0x48, 0x19, 0x00, 0x54, 0xfb, 0x3c, 0x48, 0x00, 0xbf, 0xe4, 0x3c, + 0x47, 0x00, 0xbf, 0xdf, 0x20, 0x7f, 0x10, 0x7c, 0x03, 0x45, 0x7c, + 0x03, 0x22, 0x20, 0x7f, 0x10, 0x7c, 0x03, 0x41, 0x7c, 0x03, 0x1e, + 0x20, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0x4b, 0x12, 0x6f, 0x50, 0x00, + 0x1a, 0x6e, 0xd0, 0x0f, 0x51, 0x4c, 0x12, 0x71, 0x50, 0x00, 0x1a, + 0x70, 0xd0, 0x05, 0x50, 0x0f, 0x80, 0x17, 0x62, 0xd0, 0x00, 0x51, + 0x6f, 0x12, 0x71, 0x51, 0x6e, 0x1a, 0x70, 0xd0, 0x05, 0x50, 0x00, + 0x80, 0x06, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x7f, 0x10, 0x4f, 0x38, + 0x05, 0x62, 0xd0, 0x00, 0x51, 0x6f, 0x54, 0x02, 0x51, 0x6e, 0x54, + 0x01, 0x56, 0x04, 0x00, 0x56, 0x00, 0x00, 0x56, 0x03, 0x00, 0x80, + 0x5d, 0x95, 0x45, 0x40, 0x06, 0x47, 0x4b, 0x0e, 0x48, 0x00, 0x51, + 0x48, 0x60, 0xd4, 0x3e, 0x47, 0x53, 0x47, 0x96, 0x53, 0x40, 0x06, + 0x45, 0x6e, 0x0e, 0x46, 0x00, 0x51, 0x46, 0x95, 0x57, 0x40, 0x51, + 0x47, 0x12, 0x45, 0x50, 0x00, 0x1a, 0x46, 0xd0, 0x03, 0x77, 0x03, + 0x62, 0xd0, 0x00, 0x94, 0xfa, 0x40, 0x06, 0x47, 0x6e, 0x95, 0x4a, + 0x40, 0x3e, 0x47, 0x13, 0x02, 0x51, 0x48, 0x1b, 0x01, 0xd0, 0x1a, + 0x94, 0xe7, 0x40, 0x06, 0x47, 0x6e, 0x0e, 0x48, 0x00, 0x51, 0x48, + 0x60, 0xd4, 0x3e, 0x47, 0x54, 0x01, 0x3e, 0x47, 0x54, 0x02, 0x52, + 0x00, 0x54, 0x04, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0xa0, 0x50, + 0x01, 0x3b, 0x03, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, 0x0f, 0x80, + 0x06, 0x52, 0x04, 0x62, 0xd0, 0x00, 0x38, 0xfb, 0x20, 0x7f, 0x10, + 0x4f, 0x38, 0x02, 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x26, 0x28, 0xf0, + 0x51, 0x95, 0x01, 0x01, 0x53, 0x48, 0x51, 0x28, 0x2a, 0x48, 0x53, + 0x28, 0x71, 0x01, 0x62, 0xe3, 0x38, 0x10, 0x7c, 0x04, 0x81, 0x62, + 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, 0x01, 0x00, 0x56, 0x00, + 0x00, 0x80, 0x21, 0x10, 0x7c, 0x04, 0x81, 0x62, 0xd0, 0x00, 0x20, + 0x53, 0x48, 0x47, 0x48, 0x20, 0xa0, 0x03, 0x80, 0x1a, 0x50, 0x00, + 0x08, 0x50, 0x14, 0x08, 0x9e, 0xba, 0x38, 0xfe, 0x77, 0x01, 0x0f, + 0x00, 0x00, 0x52, 0x01, 0x11, 0x2c, 0x52, 0x00, 0x19, 0x01, 0xcf, + 0xd7, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, 0x7c, + 0x04, 0x81, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x48, 0x47, 0x48, 0x20, + 0xb0, 0x03, 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, + 0x88, 0x38, 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, 0x11, + 0x3c, 0x52, 0x00, 0x19, 0x00, 0xcf, 0xd7, 0x62, 0xd0, 0x00, 0x51, + 0x28, 0x29, 0x08, 0x53, 0x28, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, + 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x26, + 0x28, 0xf0, 0x51, 0x95, 0x01, 0x09, 0x53, 0x48, 0x51, 0x28, 0x2a, + 0x48, 0x53, 0x28, 0x71, 0x01, 0x62, 0xe3, 0x38, 0x10, 0x7c, 0x04, + 0x81, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, 0x01, 0x00, + 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, 0x7c, 0x04, 0x81, 0x62, 0xd0, + 0x00, 0x20, 0x53, 0x48, 0x47, 0x48, 0x20, 0xa0, 0x03, 0x80, 0x1a, + 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, 0x1e, 0x38, 0xfe, 0x77, + 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, 0x11, 0x2c, 0x52, 0x00, 0x19, + 0x01, 0xcf, 0xd7, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, + 0x10, 0x7c, 0x04, 0x81, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x48, 0x47, + 0x48, 0x20, 0xb0, 0x03, 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x14, + 0x08, 0x9d, 0xec, 0x38, 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, + 0x01, 0x11, 0x3c, 0x52, 0x00, 0x19, 0x00, 0xcf, 0xd7, 0x43, 0x00, + 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x04, 0x62, 0xd0, + 0x00, 0x51, 0x28, 0x21, 0xf0, 0x54, 0x00, 0x51, 0x2b, 0x54, 0x01, + 0x3d, 0x00, 0x10, 0xb0, 0x12, 0x55, 0x8f, 0x00, 0x55, 0x82, 0x00, + 0x55, 0x83, 0x00, 0x56, 0x00, 0x00, 0x26, 0x28, 0x0f, 0x81, 0x94, + 0x3d, 0x00, 0x20, 0xb0, 0x18, 0x62, 0xd0, 0x00, 0x55, 0x8f, 0x01, + 0x55, 0x90, 0x00, 0x55, 0x82, 0x08, 0x55, 0x83, 0x08, 0x56, 0x00, + 0x00, 0x26, 0x28, 0x0f, 0x81, 0x78, 0x3d, 0x00, 0x40, 0xb0, 0x0f, + 0x62, 0xd0, 0x00, 0x55, 0x96, 0x02, 0x56, 0x00, 0x00, 0x26, 0x28, + 0x0f, 0x81, 0x65, 0x3d, 0x00, 0x50, 0xb0, 0xa7, 0x52, 0x01, 0x54, + 0x03, 0x56, 0x02, 0x00, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, + 0x01, 0xa0, 0x21, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x02, + 0xa0, 0x28, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x04, 0xa0, + 0x36, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x08, 0xa0, 0x48, + 0x80, 0x62, 0x62, 0xd0, 0x00, 0x55, 0x91, 0x01, 0x51, 0x2d, 0x29, + 0x80, 0x53, 0x2d, 0x7c, 0x0b, 0x7c, 0x80, 0x51, 0x62, 0xd0, 0x00, + 0x51, 0x29, 0x53, 0x4b, 0x51, 0x29, 0x53, 0x4c, 0x51, 0x29, 0x53, + 0x2c, 0x51, 0x2a, 0x53, 0x0c, 0x55, 0x0b, 0x00, 0x80, 0x39, 0x62, + 0xd0, 0x00, 0x51, 0x29, 0x53, 0x2d, 0x3c, 0x91, 0x00, 0xa0, 0x09, + 0x51, 0x2d, 0x29, 0x80, 0x53, 0x2d, 0x80, 0x25, 0x62, 0xd0, 0x00, + 0x26, 0x2d, 0x7f, 0x80, 0x1d, 0x62, 0xd0, 0x00, 0x55, 0x91, 0x00, + 0x26, 0x2d, 0x7f, 0x51, 0x7a, 0x53, 0x22, 0x51, 0x22, 0x53, 0x2e, + 0x51, 0x7b, 0x53, 0x23, 0x51, 0x23, 0x53, 0x2f, 0x7c, 0x0b, 0x7c, + 0x56, 0x00, 0x00, 0x62, 0xd0, 0x00, 0x26, 0x28, 0x0f, 0x55, 0x29, + 0x12, 0x55, 0x2a, 0x11, 0x55, 0x2b, 0x00, 0x80, 0xba, 0x3d, 0x00, + 0x60, 0xb0, 0x0f, 0x62, 0xd0, 0x00, 0x55, 0x8c, 0x01, 0x56, 0x00, + 0x00, 0x26, 0x28, 0x0f, 0x80, 0xa7, 0x3d, 0x00, 0x70, 0xb0, 0x0f, + 0x62, 0xd0, 0x00, 0x55, 0x8c, 0x00, 0x56, 0x00, 0x00, 0x26, 0x28, + 0x0f, 0x80, 0x94, 0x3d, 0x00, 0x80, 0xb0, 0x8f, 0x56, 0x00, 0x00, + 0x62, 0xd0, 0x00, 0x26, 0x28, 0x0f, 0x9c, 0xc5, 0x10, 0x7c, 0x03, + 0x45, 0x7c, 0x03, 0x22, 0x7c, 0x07, 0x74, 0x7c, 0x04, 0x97, 0x20, + 0x93, 0x91, 0x40, 0x70, 0xfe, 0x71, 0x10, 0x43, 0x00, 0x08, 0x43, + 0x01, 0x08, 0x70, 0xcf, 0x43, 0x00, 0x08, 0x62, 0xda, 0x00, 0x71, + 0x10, 0x41, 0xdc, 0xfe, 0x70, 0xcf, 0x43, 0x01, 0x08, 0x43, 0x00, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x1e, 0x08, 0x9c, 0x65, 0x38, 0xfe, + 0x71, 0x01, 0x43, 0xe0, 0x10, 0x41, 0x7a, 0xef, 0x41, 0x7a, 0xfe, + 0x71, 0x10, 0x41, 0xdc, 0xfd, 0x41, 0xec, 0xfd, 0x43, 0xe0, 0x40, + 0x41, 0xe0, 0xdf, 0x70, 0xcf, 0x43, 0xff, 0x08, 0x43, 0x7a, 0x10, + 0x43, 0x7a, 0x01, 0x70, 0xfe, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x10, 0x7c, 0x06, 0xa3, 0x7c, 0x04, 0x4b, 0x7c, 0x04, 0x8c, + 0x20, 0x93, 0x2d, 0x40, 0x71, 0x01, 0x62, 0xe3, 0x38, 0x56, 0x00, + 0x00, 0x62, 0xd0, 0x00, 0x26, 0x28, 0x0f, 0x38, 0xfc, 0x20, 0x7f, + 0x62, 0xd0, 0x00, 0x3c, 0x8f, 0x00, 0xa0, 0x13, 0x9c, 0x34, 0x62, + 0xd0, 0x00, 0x3c, 0x90, 0x00, 0xb0, 0x33, 0x55, 0x90, 0x01, 0x7c, + 0x09, 0x8d, 0x80, 0x2b, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x82, + 0xd0, 0x08, 0x10, 0x7c, 0x03, 0x45, 0x20, 0x80, 0x06, 0x10, 0x7c, + 0x03, 0x41, 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x83, 0xd0, + 0x08, 0x10, 0x7c, 0x03, 0x22, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x03, + 0x1e, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x03, 0x56, 0x02, 0x00, 0x56, + 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x3e, 0x62, 0xd0, 0x00, 0x91, + 0x6d, 0x40, 0x52, 0xfc, 0x04, 0x47, 0x52, 0xfb, 0x0c, 0x48, 0x51, + 0x48, 0x60, 0xd4, 0x3e, 0x47, 0x53, 0x48, 0x3e, 0x47, 0x53, 0x47, + 0x52, 0x02, 0x12, 0x47, 0x52, 0x01, 0x1a, 0x48, 0xd0, 0x18, 0x91, + 0x4c, 0x40, 0x52, 0xfc, 0x04, 0x47, 0x52, 0xfb, 0x0c, 0x48, 0x51, + 0x48, 0x60, 0xd4, 0x3e, 0x47, 0x54, 0x01, 0x3e, 0x47, 0x54, 0x02, + 0x77, 0x00, 0x52, 0x00, 0x3b, 0xfa, 0xcf, 0xbe, 0x62, 0xd0, 0x00, + 0x52, 0x02, 0x53, 0x47, 0x52, 0x01, 0x53, 0x48, 0x38, 0xfd, 0x20, + 0x7f, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x91, 0x00, 0x55, 0x8c, 0x01, + 0x10, 0x7c, 0x03, 0x45, 0x7c, 0x03, 0x22, 0x20, 0x9b, 0x84, 0x71, + 0x10, 0x41, 0x00, 0xf7, 0x41, 0x01, 0xf7, 0x70, 0xcf, 0x43, 0x00, + 0x08, 0x43, 0x00, 0x08, 0x62, 0xd0, 0x00, 0x55, 0x28, 0x08, 0x55, + 0x29, 0x12, 0x55, 0x2a, 0x11, 0x55, 0x2c, 0x1e, 0x55, 0x2d, 0x02, + 0x3c, 0x91, 0x00, 0xa0, 0x09, 0x51, 0x2d, 0x29, 0x80, 0x53, 0x2d, + 0x80, 0x07, 0x62, 0xd0, 0x00, 0x26, 0x2d, 0x7f, 0x10, 0x50, 0x00, + 0x08, 0x50, 0x28, 0x08, 0x50, 0x06, 0x08, 0x50, 0x16, 0x08, 0x7c, + 0x04, 0x9e, 0x38, 0xfc, 0x7c, 0x04, 0x4b, 0x7c, 0x04, 0x8c, 0x20, + 0x71, 0x10, 0x41, 0x04, 0xfe, 0x41, 0x05, 0xfe, 0x41, 0x04, 0xfd, + 0x41, 0x05, 0xfd, 0x70, 0xcf, 0x43, 0x04, 0x01, 0x43, 0x04, 0x02, + 0x71, 0x01, 0x10, 0x7c, 0x06, 0xa3, 0x7c, 0x06, 0x29, 0x20, 0x7c, + 0x0b, 0x7c, 0x80, 0x22, 0x62, 0xe3, 0x38, 0x7c, 0x0d, 0xc4, 0x10, + 0x7c, 0x06, 0x67, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xa0, 0x09, + 0x7c, 0x08, 0xc3, 0x7c, 0x08, 0xed, 0x80, 0x04, 0x7c, 0x09, 0x56, + 0x9c, 0xf6, 0x9e, 0xb2, 0x8f, 0xde, 0x8f, 0xff, 0x10, 0x4f, 0x7c, + 0x18, 0x50, 0x20, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x5d, 0xd0, 0x08, + 0x62, 0xd0, 0x00, 0x50, 0x00, 0x53, 0x3f, 0x53, 0x40, 0x55, 0x3e, + 0x10, 0x66, 0xfc, 0x6c, 0xfb, 0x6b, 0x3f, 0x6b, 0x40, 0x51, 0x3f, + 0x1b, 0xfa, 0x51, 0x40, 0x1b, 0xf9, 0xc0, 0x09, 0x53, 0x40, 0x52, + 0xfa, 0x1c, 0x3f, 0x77, 0xfc, 0x7a, 0x3e, 0xbf, 0xe3, 0x51, 0x3f, + 0x54, 0xfa, 0x51, 0x40, 0x54, 0xf9, 0x18, 0x60, 0xd0, 0x7f, 0x10, + 0x4f, 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, 0x00, 0x53, 0x40, + 0x53, 0x3f, 0x55, 0x3e, 0x10, 0x6f, 0xf9, 0x6f, 0xfa, 0xd0, 0x09, + 0x52, 0xfc, 0x04, 0x40, 0x52, 0xfb, 0x0c, 0x3f, 0x66, 0xfc, 0x6c, + 0xfb, 0x7a, 0x3e, 0xbf, 0xeb, 0x18, 0x60, 0xd0, 0x20, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x52, 0x00, 0x53, 0x47, 0x55, 0x48, 0x00, 0x65, + 0x47, 0x6b, 0x48, 0x7f, 0x53, 0x45, 0x51, 0x48, 0x09, 0x00, 0x60, + 0xd4, 0x3e, 0x45, 0x53, 0x46, 0x3e, 0x45, 0x53, 0x45, 0x7f, 0x62, + 0xd0, 0x00, 0x52, 0x00, 0x53, 0x47, 0x55, 0x48, 0x00, 0x7f, 0x06, + 0x47, 0x86, 0x0e, 0x48, 0x00, 0x51, 0x48, 0x60, 0xd4, 0x3e, 0x47, + 0x53, 0x46, 0x3e, 0x47, 0x16, 0x47, 0x02, 0x53, 0x45, 0x7f, 0x51, + 0x48, 0x60, 0xd5, 0x51, 0x46, 0x3f, 0x47, 0x51, 0x45, 0x3f, 0x47, + 0x7f, 0x60, 0xd4, 0x3e, 0x45, 0x53, 0x46, 0x3e, 0x45, 0x53, 0x45, + 0x7f, 0x0e, 0x48, 0x00, 0x51, 0x48, 0x60, 0xd4, 0x3e, 0x47, 0x53, + 0x48, 0x7f, 0x51, 0x47, 0x01, 0x86, 0x53, 0x43, 0x51, 0x48, 0x09, + 0x00, 0x60, 0xd4, 0x3e, 0x43, 0x53, 0x44, 0x3e, 0x43, 0x53, 0x43, + 0x51, 0x45, 0x12, 0x43, 0x51, 0x46, 0x1a, 0x44, 0xd0, 0x3f, 0x62, + 0xd0, 0x00, 0x52, 0x00, 0x53, 0x45, 0x55, 0x46, 0x00, 0x65, 0x45, + 0x6b, 0x46, 0x51, 0x45, 0x01, 0x6a, 0x53, 0x43, 0x51, 0x46, 0x09, + 0x00, 0x60, 0xd4, 0x3e, 0x43, 0x53, 0x44, 0x3e, 0x43, 0x53, 0x43, + 0x06, 0x45, 0x86, 0x0e, 0x46, 0x00, 0x51, 0x46, 0x60, 0xd4, 0x3e, + 0x45, 0x53, 0x46, 0x3e, 0x45, 0x12, 0x43, 0x54, 0x07, 0x51, 0x46, + 0x1a, 0x44, 0x54, 0x06, 0x80, 0x07, 0x56, 0x07, 0x00, 0x56, 0x06, + 0x00, 0x62, 0xd0, 0x00, 0x06, 0x47, 0x32, 0x0e, 0x48, 0x00, 0x51, + 0x48, 0x60, 0xd5, 0x52, 0x06, 0x3f, 0x47, 0x52, 0x07, 0x3f, 0x47, + 0x7f, 0x51, 0x47, 0x01, 0x86, 0x53, 0x45, 0x51, 0x48, 0x09, 0x00, + 0x7f, 0x51, 0x40, 0x53, 0x45, 0x51, 0x3f, 0x53, 0x46, 0x51, 0x45, + 0x02, 0x47, 0x53, 0x47, 0x51, 0x46, 0x0a, 0x48, 0x7f, 0x51, 0x45, + 0x02, 0x47, 0x53, 0x47, 0x51, 0x46, 0x0a, 0x48, 0x7f, 0x53, 0x45, + 0x51, 0x48, 0x09, 0x00, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x45, 0x52, + 0x15, 0x3f, 0x45, 0x7f, 0x3e, 0x47, 0x53, 0x47, 0x51, 0x45, 0x12, + 0x47, 0x51, 0x46, 0x1a, 0x48, 0x7f, 0x60, 0xd5, 0x52, 0x14, 0x3f, + 0x45, 0x52, 0x15, 0x3f, 0x45, 0x7f, 0x0e, 0x48, 0x00, 0x51, 0x48, + 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x47, 0x52, 0x15, 0x3f, 0x47, 0x7f, + 0x52, 0x00, 0x53, 0x45, 0x55, 0x46, 0x00, 0x65, 0x45, 0x6b, 0x46, + 0x7f, 0x0e, 0x46, 0x00, 0x51, 0x46, 0x60, 0xd5, 0x51, 0x48, 0x3f, + 0x45, 0x7f, 0x71, 0x10, 0x43, 0x04, 0x01, 0x43, 0x05, 0x01, 0x43, + 0x04, 0x02, 0x43, 0x05, 0x02, 0x70, 0xcf, 0x43, 0x04, 0x01, 0x43, + 0x04, 0x02, 0x7f, 0x70, 0xfb, 0x6e, 0x48, 0x6e, 0x47, 0x51, 0x46, + 0x60, 0xd5, 0x51, 0x48, 0x3f, 0x45, 0x51, 0x47, 0x3f, 0x45, 0x7f, + 0x0e, 0x48, 0x00, 0x51, 0x48, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x47, + 0x7f, 0x3e, 0x47, 0x12, 0x45, 0x54, 0x09, 0x51, 0x48, 0x1a, 0x46, + 0x54, 0x08, 0x7f, 0x3e, 0x47, 0x12, 0x45, 0x54, 0x07, 0x51, 0x48, + 0x1a, 0x46, 0x54, 0x06, 0x7f, 0x60, 0xd4, 0x3e, 0x45, 0x53, 0x44, + 0x3e, 0x45, 0x16, 0x45, 0x02, 0x7f, 0x00, 0x28, 0x00, 0x16, 0x00, + 0x52, 0x00, 0x18, 0x00, 0x72, 0x00, 0x08, 0x00, 0x7a, 0x04, 0x2e, + 0x18, 0x0d, 0x0f, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x82, 0x04, 0x08, + 0x08, 0x08, 0x08, 0x00, 0x8a, 0x09, 0x00, 0x00, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x00, 0x02, 0x00, 0x93, 0x00, 0x04, 0xff, 0x00, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_T0.h b/drivers/input/keyboard/cypressbln/touchkey_fw_T0.h new file mode 100644 index 0000000..671d265 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_T0.h @@ -0,0 +1,770 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x00, 0x68, + 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x04, 0xae, 0x7e, 0x7e, 0x30, + 0x30, 0x30, 0x7d, 0x05, 0xfb, 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x40, 0x71, 0x10, 0x62, 0xe3, + 0x06, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x50, 0x80, 0x4e, 0x62, 0xe3, + 0x38, 0x5d, 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, + 0xfa, 0x01, 0x40, 0x4f, 0x5b, 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, + 0x50, 0x06, 0x00, 0x40, 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, + 0x70, 0xef, 0x18, 0x60, 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, + 0x71, 0x10, 0x62, 0xe0, 0x1a, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x71, + 0x10, 0x41, 0xe1, 0xfe, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, + 0x03, 0x50, 0x80, 0x4e, 0x62, 0xd3, 0x03, 0x62, + 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, 0x00, 0x71, 0xc0, 0x7c, 0x03, + 0x1b, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x57, 0xc2, 0x08, 0x28, 0x53, + 0x51, 0x18, 0x75, 0x09, 0x00, 0x28, 0x4b, 0x51, 0x51, 0x80, 0x04, + 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, 0x08, 0x28, 0x60, 0xd5, 0x74, + 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0x51, 0x18, + 0x75, 0x09, 0x00, 0x08, 0x28, 0xa0, 0x1c, 0x53, + 0x50, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x3f, 0x51, 0x47, 0x51, 0xff, + 0xb0, 0x06, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x18, 0x7a, 0x50, 0xbf, + 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0x50, + 0x50, 0x00, 0x3f, 0x51, 0x47, 0x51, 0xff, 0xb0, 0x08, 0x5d, 0xd5, + 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x50, 0xbf, 0xef, 0x18, 0x8f, + 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, 0x10, + 0x62, 0xe0, 0x1a, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x1b, 0x4b, + 0x8f, 0xff, 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x01, 0x99, 0x03, 0x33, 0x06, 0x66, + 0x0c, 0xcc, 0x19, 0x99, 0x33, 0x33, 0x66, 0x66, + 0xcc, 0xcc, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0b, 0xff, 0x18, 0x00, + 0x2f, 0xff, 0x5f, 0xff, 0xbf, 0xff, 0x01, 0x66, 0x02, 0xcc, 0x05, + 0x99, 0x0b, 0x32, 0x16, 0x66, 0x2c, 0xcc, 0x59, 0x98, 0xb3, 0x32, + 0x01, 0x4c, 0x02, 0x99, 0x05, 0x33, 0x0a, 0x65, 0x14, 0xcc, 0x29, + 0x98, 0x53, 0x32, 0xa6, 0x65, 0x01, 0x33, 0x02, 0x66, 0x04, 0xcc, + 0x09, 0x99, 0x13, 0x33, 0x26, 0x65, 0x4c, 0xcc, + 0x99, 0x99, 0x1d, 0xea, 0x70, 0xef, 0x62, 0x61, 0x00, 0x62, 0xfd, 0x00, + 0x62, 0xcd, 0x00, 0x62, 0xce, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, + 0x00, 0x62, 0xa0, 0x00, 0x62, 0xa1, 0x80, 0x62, 0xa2, 0xc0, 0x62, + 0xa3, 0x0c, 0x62, 0xa8, 0x00, 0x62, 0xa6, 0x00, 0x62, 0xa7, 0x00, + 0x62, 0x7c, 0x33, 0x62, 0x7a, 0x00, 0x62, 0x7b, 0x00, 0x62, 0x79, + 0x00, 0x62, 0x36, 0x00, 0x62, 0x37, 0x00, 0x62, + 0x38, 0x00, 0x62, 0x39, 0x00, 0x62, 0x3a, 0x00, 0x62, 0x3b, 0x00, 0x62, + 0x3c, 0x00, 0x62, 0x3d, 0x00, 0x62, 0x3e, 0x00, 0x62, 0x3f, 0x00, + 0x62, 0x40, 0x00, 0x62, 0x41, 0x00, 0x62, 0x42, 0x00, 0x62, 0x43, + 0x00, 0x62, 0x44, 0x00, 0x62, 0x45, 0x00, 0x62, 0x46, 0x00, 0x62, + 0x47, 0x00, 0x62, 0x48, 0x00, 0x62, 0x49, 0x00, 0x62, 0x4a, 0x00, + 0x62, 0x4b, 0x00, 0x62, 0x4c, 0x00, 0x62, 0x4d, + 0x00, 0x62, 0x4e, 0x00, 0x62, 0x4f, 0x00, 0x62, 0xca, 0x20, 0x62, 0xd6, + 0x44, 0x62, 0xcf, 0x00, 0x62, 0xcb, 0x00, 0x62, 0xc8, 0x00, 0x62, + 0xcc, 0x00, 0x62, 0xc9, 0x00, 0x62, 0xd7, 0x00, 0x62, 0xa9, 0x00, + 0x62, 0x2b, 0x00, 0x62, 0xb0, 0x00, 0x62, 0xb3, 0x02, 0x62, 0xb6, + 0x00, 0x62, 0xb2, 0x00, 0x62, 0xb5, 0x00, 0x62, 0xb8, 0x00, 0x62, + 0xb1, 0x00, 0x62, 0xb4, 0x00, 0x62, 0xb7, 0x00, + 0x62, 0x33, 0x00, 0x62, 0x34, 0x00, 0x62, 0x35, 0x00, 0x71, 0x10, 0x62, + 0x54, 0x00, 0x62, 0x55, 0x00, 0x62, 0x56, 0x00, 0x62, 0x57, 0x00, + 0x62, 0x58, 0x00, 0x62, 0x59, 0x00, 0x62, 0x5a, 0x00, 0x62, 0x5b, + 0x00, 0x62, 0xdc, 0x00, 0x62, 0xe2, 0x00, 0x62, 0xdd, 0x00, 0x62, + 0xd8, 0x02, 0x62, 0xd9, 0x00, 0x62, 0xda, 0x28, 0x62, 0xdb, 0x00, + 0x62, 0xdf, 0x00, 0x62, 0x29, 0x00, 0x62, 0x30, + 0x00, 0x62, 0xbd, 0x00, 0x70, 0xef, 0x70, 0xef, 0x62, 0x00, 0x08, 0x71, + 0x10, 0x62, 0x00, 0x08, 0x62, 0x01, 0x92, 0x70, 0xef, 0x62, 0x04, + 0x17, 0x71, 0x10, 0x62, 0x04, 0x17, 0x62, 0x05, 0xab, 0x70, 0xef, + 0x62, 0x08, 0x00, 0x71, 0x10, 0x62, 0x08, 0x00, 0x62, 0x09, 0x28, + 0x70, 0xef, 0x62, 0x0c, 0x00, 0x71, 0x10, 0x62, 0x0c, 0x00, 0x62, + 0x0d, 0x00, 0x70, 0xef, 0x62, 0x10, 0x00, 0x71, + 0x10, 0x62, 0x10, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, 0x62, 0x01, 0x00, + 0x62, 0x05, 0x00, 0x62, 0x09, 0x00, 0x62, 0x0d, 0x00, 0x62, 0x11, + 0x00, 0x70, 0xef, 0x7f, 0x55, 0x02, 0x08, 0x55, 0x03, 0x17, 0x55, + 0x04, 0x00, 0x7c, 0x03, 0x28, 0x7f, 0x7c, 0x01, 0xc4, 0x70, 0xef, + 0x7f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x62, 0xd0, + 0x00, 0x53, 0x00, 0x71, 0x10, 0x5d, 0xe0, 0x08, 0x21, 0xf8, 0x29, 0x00, + 0x70, 0xfe, 0x60, 0xe0, 0x70, 0xef, 0x4b, 0x4b, 0x4b, 0x4b, 0x51, + 0x02, 0x21, 0xf7, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, + 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, + 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, + 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, + 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x47, 0x00, 0x00, 0x49, 0x01, 0x00, 0x29, 0x08, 0x60, 0x00, 0x57, + 0x01, 0x79, 0xbf, 0xfe, 0x18, 0x71, 0x10, 0x60, 0xe0, 0x70, 0xef, + 0x71, 0x01, 0x7f, 0x08, 0x67, 0x67, 0x67, 0x67, 0x21, 0x0f, 0xff, + 0x40, 0x9f, 0x4e, 0x18, 0x21, 0x0f, 0xff, 0x39, 0x9f, 0x47, 0x7f, + 0x08, 0x10, 0x28, 0xa0, 0x0b, 0x9f, 0x3f, 0x20, + 0x18, 0x75, 0xdf, 0xf5, 0x74, 0x8f, 0xf2, 0x38, 0xfe, 0x7f, 0x52, 0x00, + 0xa0, 0x08, 0x10, 0x9f, 0x2d, 0x20, 0x75, 0x8f, 0xf6, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x50, 0x0d, 0x9f, 0x20, 0x50, 0x0a, 0x9f, 0x1c, + 0x7f, 0x70, 0xbf, 0x62, 0xd3, 0x03, 0x4f, 0x52, 0xfb, 0xa0, 0x15, + 0x7b, 0xfb, 0x52, 0xfc, 0x59, 0xfd, 0x60, 0xd3, 0x52, 0x00, 0x9f, + 0x05, 0x4f, 0x62, 0xd3, 0x03, 0x77, 0xfd, 0x8f, + 0xe9, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x3d, 0xfa, 0x00, 0xb0, 0x06, 0x3d, + 0xfb, 0x00, 0xa0, 0x18, 0x10, 0x52, 0xfc, 0x59, 0xfd, 0x28, 0x9e, + 0xe6, 0x20, 0x07, 0xfd, 0x01, 0x0f, 0xfc, 0x00, 0x17, 0xfb, 0x01, + 0x1f, 0xfa, 0x00, 0x8f, 0xe0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, + 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xef, + 0x80, 0x04, 0x2e, 0x03, 0x10, 0x51, 0x03, 0x60, + 0x04, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, + 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, + 0x06, 0x26, 0x03, 0xfb, 0x80, 0x04, 0x2e, 0x03, 0x04, 0x51, 0x03, + 0x60, 0x04, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0x3f, 0x71, 0xc0, + 0x7f, 0x08, 0x10, 0x70, 0x3f, 0x71, 0x80, 0x5d, 0xd3, 0x08, 0x5d, + 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x51, 0x08, 0x60, + 0xd3, 0x2e, 0x05, 0x80, 0x49, 0xd7, 0x08, 0xa0, 0x09, 0x26, 0x05, 0xf0, + 0x2e, 0x05, 0x00, 0x80, 0x08, 0x49, 0xd7, 0x20, 0xa0, 0x03, 0x80, + 0xa6, 0x51, 0x05, 0x21, 0x0e, 0xe0, 0x01, 0x80, 0x11, 0x80, 0x67, + 0x80, 0x79, 0x80, 0x47, 0x80, 0x96, 0x80, 0x94, 0x80, 0x92, 0x80, + 0x90, 0x80, 0x97, 0x5d, 0xd8, 0x21, 0xfe, 0x39, 0x40, 0xa0, 0x06, + 0x62, 0xd7, 0x00, 0x80, 0x8a, 0x49, 0xd8, 0x01, + 0xb0, 0x0f, 0x55, 0x0c, 0x02, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x02, 0x62, + 0xd7, 0x10, 0x80, 0x77, 0x55, 0x0c, 0x01, 0x26, 0x05, 0xf0, 0x2e, + 0x05, 0x06, 0x5f, 0x07, 0x06, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, + 0x00, 0x60, 0xd8, 0x76, 0x07, 0x62, 0xd7, 0x14, 0x80, 0x5b, 0x51, + 0x0a, 0x78, 0x3a, 0x07, 0xc0, 0x0f, 0x51, 0x09, 0x02, 0x07, 0x5c, + 0x52, 0x00, 0x60, 0xd8, 0x76, 0x07, 0x2e, 0x05, + 0x20, 0x60, 0xd8, 0x62, 0xd7, 0x04, 0x80, 0x3f, 0x5d, 0xd8, 0x3a, 0x0a, + 0xd0, 0x2b, 0xa0, 0x29, 0x53, 0x07, 0x53, 0x06, 0x26, 0x05, 0xf0, + 0x2e, 0x05, 0x04, 0x80, 0x18, 0x51, 0x0b, 0x78, 0x3a, 0x07, 0xc0, + 0x16, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x5d, 0xd8, 0x54, 0x00, 0x2e, + 0x05, 0x10, 0x76, 0x07, 0x80, 0x01, 0x62, 0xd7, 0x10, 0x80, 0x0f, + 0x62, 0xd7, 0x00, 0x80, 0x0a, 0x26, 0x05, 0xf0, + 0x2e, 0x05, 0x00, 0x55, 0x0c, 0x00, 0x18, 0x60, 0xd0, 0x18, 0x60, 0xd3, + 0x20, 0x18, 0x7e, 0x62, 0xd0, 0x00, 0x71, 0x10, 0x41, 0x04, 0xfc, + 0x43, 0x05, 0x03, 0x70, 0xef, 0x26, 0x03, 0xfc, 0x51, 0x03, 0x60, + 0x04, 0x55, 0x0c, 0x00, 0x90, 0x28, 0x90, 0x2d, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x50, 0x00, 0x53, 0x06, 0x71, 0x10, 0x43, 0x04, 0x03, + 0x43, 0x05, 0x03, 0x70, 0xef, 0x2e, 0x03, 0x03, + 0x51, 0x03, 0x60, 0x04, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0x05, 0x21, 0xb0, + 0x26, 0x05, 0x4f, 0x7f, 0x41, 0xe0, 0x7f, 0x43, 0xe0, 0x80, 0x7f, + 0x43, 0xd6, 0x31, 0x7f, 0x41, 0xe0, 0x7f, 0x41, 0xd6, 0xfe, 0x7f, + 0x62, 0xd0, 0x00, 0x4f, 0x52, 0xfd, 0x53, 0x0a, 0x52, 0xfc, 0x53, + 0x0b, 0x52, 0xfb, 0x53, 0x09, 0x52, 0xfa, 0x53, 0x08, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x08, 0x5d, 0xa4, 0x04, 0x1b, + 0x5d, 0xa5, 0x0c, 0x1a, 0x55, 0x1c, 0x01, 0x18, 0x7e, 0x70, 0xbf, 0x62, + 0xd0, 0x00, 0x70, 0xbf, 0x53, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, + 0x52, 0x8d, 0x62, 0xd3, 0x00, 0x13, 0x63, 0x62, 0xd3, 0x00, 0x54, + 0x67, 0x62, 0xd3, 0x00, 0x52, 0x8c, 0x62, 0xd3, 0x00, 0x1b, 0x62, + 0x62, 0xd3, 0x00, 0x54, 0x66, 0x48, 0x66, 0x80, 0xb0, 0x33, 0x3d, + 0x66, 0x00, 0xb0, 0x7b, 0x51, 0x0d, 0x3b, 0x67, + 0xc0, 0x75, 0x52, 0x67, 0x58, 0x1e, 0x01, 0x00, 0x6d, 0x62, 0xd3, 0x00, + 0x05, 0x40, 0xc0, 0x09, 0x51, 0x0f, 0x3b, 0x40, 0xd0, 0x12, 0xa0, + 0x10, 0x56, 0x40, 0x00, 0x5b, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x07, + 0x63, 0x01, 0x0f, 0x62, 0x00, 0x80, 0x41, 0x3d, 0x66, 0xff, 0xb0, + 0x09, 0x50, 0xff, 0x12, 0x0e, 0x3b, 0x67, 0xc0, 0x20, 0x62, 0xd3, + 0x00, 0x56, 0x67, 0x00, 0x56, 0x66, 0x00, 0x5b, + 0x67, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x47, 0x78, 0xd0, 0x03, 0x50, 0x00, + 0x54, 0x47, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, 0x62, 0xd3, + 0x00, 0x52, 0x8d, 0x62, 0xd3, 0x00, 0x54, 0x63, 0x62, 0xd3, 0x00, + 0x52, 0x8c, 0x62, 0xd3, 0x00, 0x54, 0x62, 0x51, 0x1e, 0x64, 0x5c, + 0x62, 0xd3, 0x00, 0x56, 0x67, 0x00, 0x56, 0x66, 0x00, 0x5b, 0x67, + 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x12, 0x54, 0x47, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, + 0x08, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x42, 0x53, 0x19, 0x55, 0x18, + 0x00, 0x18, 0x08, 0x90, 0x7e, 0x62, 0xd3, 0x00, 0x23, 0x44, 0xb0, + 0x2c, 0x51, 0x10, 0x04, 0x19, 0x0e, 0x18, 0x00, 0x18, 0x64, 0x5c, + 0x62, 0xd3, 0x00, 0x52, 0x67, 0x12, 0x19, 0x52, 0x66, 0x1a, 0x18, + 0xc0, 0x39, 0x5b, 0x67, 0x5c, 0x62, 0xd3, 0x00, + 0x52, 0x45, 0x78, 0x54, 0x45, 0x08, 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x3e, + 0x80, 0x18, 0x51, 0x10, 0x14, 0x19, 0x1e, 0x18, 0x00, 0x18, 0x64, + 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x67, 0x12, 0x19, 0x52, 0x66, 0x1a, + 0x18, 0xc0, 0x0e, 0x5b, 0x67, 0x90, 0x31, 0x62, 0xd3, 0x00, 0x2d, + 0x44, 0x50, 0x01, 0x80, 0x24, 0x5b, 0x67, 0x08, 0x90, 0x23, 0x73, + 0x62, 0xd3, 0x00, 0x25, 0x44, 0x62, 0xd3, 0x00, + 0x20, 0x51, 0x11, 0x54, 0x45, 0x50, 0x00, 0x80, 0x0d, 0x5b, 0x67, 0x90, + 0x0d, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x44, 0x50, 0x00, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x08, 0x67, 0x67, 0x67, 0x5c, 0x18, 0x21, 0x07, + 0xf0, 0x01, 0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x70, 0xbf, 0x70, 0xbf, 0x62, 0xd3, 0x00, 0x50, 0x02, 0x78, 0x08, + 0x5c, 0x56, 0x42, 0x1e, 0x18, 0x78, 0xdf, 0xf8, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x91, 0xb2, 0x70, 0xbf, 0x18, 0x08, + 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x8d, 0x62, 0xd3, 0x00, 0x54, + 0x63, 0x62, 0xd3, 0x00, 0x52, 0x8c, 0x62, 0xd3, 0x00, 0x54, 0x62, + 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, 0xd0, + 0x00, 0x55, 0x14, 0x00, 0x50, 0x02, 0x78, 0x08, 0x9f, 0x0e, 0x39, + 0x01, 0xb0, 0x04, 0x55, 0x14, 0x01, 0x18, 0x78, + 0xdf, 0xf3, 0x51, 0x14, 0x7f, 0x50, 0x02, 0x78, 0x08, 0x9e, 0x3e, 0x18, + 0x78, 0xdf, 0xfa, 0x7f, 0x98, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x97, 0xd8, 0xd9, 0xda, 0xdb, 0xdf, 0x00, 0x01, 0x03, 0x07, + 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x62, + 0xd3, 0x00, 0x57, 0x00, 0x56, 0x44, 0x00, 0x79, 0xdf, 0xfb, 0x62, + 0xd3, 0x00, 0x57, 0x01, 0x50, 0x03, 0x54, 0x45, + 0x79, 0xdf, 0xfc, 0x62, 0xd3, 0x00, 0x50, 0x14, 0x57, 0x01, 0x54, 0x47, + 0x79, 0xdf, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x55, 0x0d, 0x19, 0x55, + 0x0e, 0x05, 0x55, 0x0f, 0x14, 0x55, 0x10, 0x01, 0x55, 0x11, 0x03, + 0x55, 0x12, 0x14, 0x55, 0x22, 0x04, 0x55, 0x1f, 0x14, 0x43, 0x61, + 0x0d, 0x57, 0x00, 0x50, 0x02, 0x90, 0xae, 0x50, 0x04, 0xff, 0x98, + 0x29, 0x00, 0x60, 0xa9, 0x62, 0xa0, 0x08, 0x43, + 0xa2, 0x04, 0x62, 0xa3, 0x70, 0x43, 0x7a, 0x01, 0x43, 0xaa, 0x02, 0x43, + 0xdf, 0x01, 0x50, 0x01, 0x57, 0x09, 0x90, 0x20, 0x90, 0x55, 0x57, + 0x01, 0x50, 0xb3, 0x91, 0x5d, 0x50, 0x01, 0x57, 0x0e, 0x90, 0x12, + 0x90, 0x47, 0x7f, 0x53, 0x22, 0xff, 0x67, 0x29, 0x00, 0x60, 0xa9, + 0x51, 0x21, 0x58, 0x20, 0x90, 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, + 0x03, 0x53, 0x21, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x29, 0x80, 0x60, 0xa1, 0x5b, 0x78, 0x21, 0x0f, 0x29, 0x08, 0x74, 0x53, + 0x20, 0x12, 0x22, 0x02, 0x21, 0x5c, 0x50, 0x00, 0x53, 0x1d, 0x53, + 0x23, 0x29, 0x01, 0x79, 0xa0, 0x08, 0x64, 0x6b, 0x1d, 0x6b, 0x23, + 0x8f, 0xf5, 0x60, 0xb5, 0x51, 0x1d, 0x60, 0xb4, 0x7f, 0x50, 0x02, + 0x78, 0x08, 0x90, 0x28, 0x90, 0x5a, 0x18, 0x78, 0xdf, 0xf8, 0x7f, + 0x41, 0xdf, 0xfe, 0x71, 0x10, 0x41, 0xd8, 0xfd, + 0x70, 0xef, 0x41, 0x61, 0xf3, 0x41, 0xa2, 0xfb, 0x41, 0xa0, 0xf7, 0x62, + 0xa3, 0x00, 0x62, 0xa9, 0x00, 0x41, 0xaa, 0xfd, 0x7f, 0x02, 0x08, + 0x02, 0x20, 0x64, 0x5c, 0xff, 0xf8, 0x4b, 0x74, 0xff, 0xf4, 0x7f, + 0x62, 0xd0, 0x00, 0x53, 0x1d, 0x10, 0x5b, 0x64, 0x64, 0x5c, 0x71, + 0x10, 0x5e, 0x01, 0x2a, 0x1d, 0x61, 0x01, 0x36, 0x1d, 0xff, 0x5e, + 0x00, 0x22, 0x1d, 0x61, 0x00, 0x36, 0x1d, 0xff, + 0x18, 0xfe, 0xd6, 0x5c, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, + 0x7f, 0x62, 0xd0, 0x00, 0x10, 0x73, 0x53, 0x1d, 0x71, 0x10, 0x5b, + 0xfe, 0xc0, 0x5c, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x70, 0xef, + 0x18, 0x64, 0x64, 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x22, 0x1d, 0x61, + 0x01, 0x36, 0x1d, 0xff, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, + 0xef, 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, + 0x1e, 0x50, 0x00, 0x53, 0x1a, 0x53, 0x1b, 0x51, 0x1e, 0x5c, 0x62, 0xd3, + 0x00, 0x52, 0x24, 0x53, 0x1f, 0x43, 0xa0, 0x01, 0x51, 0x1f, 0x60, + 0xfd, 0x41, 0xa3, 0xdf, 0x51, 0x1e, 0x9f, 0x7a, 0x9f, 0x81, 0x58, + 0x23, 0x55, 0x1c, 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x43, + 0xb3, 0x01, 0x51, 0x1c, 0xaf, 0xfd, 0x79, 0xdf, 0xee, 0x51, 0x1e, + 0x9f, 0x5f, 0x9f, 0x91, 0x43, 0xa3, 0x20, 0x41, + 0xa0, 0xfe, 0x62, 0xfd, 0x00, 0x50, 0xff, 0x4c, 0x1b, 0x14, 0x1b, 0x51, + 0x20, 0x11, 0x08, 0xfe, 0x4d, 0x4c, 0x1a, 0x1c, 0x1a, 0xd0, 0x07, + 0x55, 0x1a, 0x00, 0x55, 0x1b, 0x00, 0x51, 0x1e, 0x64, 0x5c, 0x62, + 0xd3, 0x00, 0x51, 0x1b, 0x54, 0x8d, 0x51, 0x1a, 0x54, 0x8c, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x9f, 0x86, 0x18, 0x78, 0xdf, 0xfa, + 0x7f, 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, 0x27, + 0x5a, 0x26, 0x55, 0x1e, 0x01, 0x62, 0xd3, 0x00, 0x58, 0x1e, 0x56, 0x24, + 0x80, 0x55, 0x29, 0x08, 0x55, 0x28, 0x80, 0x51, 0x1e, 0x9f, 0x63, + 0x51, 0x1e, 0x9f, 0x5f, 0x70, 0xbf, 0x58, 0x1e, 0x62, 0xd3, 0x00, + 0x51, 0x1b, 0x3a, 0x27, 0x51, 0x1a, 0x1a, 0x26, 0xd0, 0x06, 0x51, + 0x28, 0x73, 0x25, 0x24, 0x68, 0x28, 0x26, 0x28, 0x7f, 0x51, 0x28, + 0x2d, 0x24, 0x7a, 0x29, 0xbf, 0xd6, 0x7a, 0x1e, + 0xdf, 0xc4, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0xa5, + 0x11, 0xdc, 0x51, 0xa4, 0x19, 0x05, 0xd0, 0x12, 0x7c, 0x16, 0xe7, + 0x39, 0x0f, 0xa0, 0x16, 0x62, 0xd0, 0x00, 0x76, 0xa5, 0x0e, 0xa4, + 0x00, 0x80, 0x0c, 0x62, 0xd0, 0x00, 0x55, 0xa5, 0x00, 0x55, 0xa4, + 0x00, 0x90, 0xbe, 0x7f, 0x62, 0xd0, 0x00, 0x3c, 0xaf, 0xf0, 0xd0, + 0x03, 0x76, 0xaf, 0x62, 0xd0, 0x00, 0x51, 0x2f, + 0x21, 0x7f, 0x53, 0x51, 0x51, 0xaf, 0x3a, 0x51, 0xb0, 0x55, 0x7c, 0x16, + 0xe7, 0x62, 0xd0, 0x00, 0x53, 0xb0, 0x3c, 0xb0, 0x0f, 0xa0, 0x3d, + 0x3c, 0xab, 0x00, 0xb0, 0x1c, 0x55, 0x94, 0x00, 0x55, 0x95, 0x00, + 0x51, 0xb0, 0x53, 0x50, 0x55, 0x51, 0x00, 0x06, 0x50, 0x94, 0x0e, + 0x51, 0x00, 0x51, 0x51, 0x60, 0xd5, 0x50, 0x08, 0x3f, 0x50, 0x62, + 0xd0, 0x00, 0x55, 0xa7, 0x00, 0x3c, 0xae, 0x00, + 0xb0, 0x0a, 0x7c, 0x17, 0x7c, 0x62, 0xd0, 0x00, 0x55, 0xae, 0x01, 0x62, + 0xd0, 0x00, 0x55, 0xa6, 0x03, 0x80, 0x0c, 0x62, 0xd0, 0x00, 0x3c, + 0xa9, 0x00, 0xb0, 0x04, 0x55, 0xa9, 0x01, 0x7f, 0x62, 0xd0, 0x00, + 0x55, 0xa5, 0x00, 0x55, 0xa4, 0x00, 0x3c, 0xae, 0x01, 0xb0, 0x33, + 0x7a, 0xa6, 0x3c, 0xa6, 0x00, 0xb0, 0x3a, 0x3c, 0xae, 0x01, 0xb0, + 0x0a, 0x7c, 0x18, 0x0f, 0x62, 0xd0, 0x00, 0x55, + 0xae, 0x00, 0x62, 0xd0, 0x00, 0x3c, 0xab, 0x00, 0xb0, 0x0e, 0x51, 0xb0, + 0x53, 0x50, 0x55, 0x51, 0x00, 0x06, 0x50, 0x94, 0x7c, 0x1d, 0xb9, + 0x62, 0xd0, 0x00, 0x55, 0xaf, 0x00, 0x80, 0x0f, 0x62, 0xd0, 0x00, + 0x3c, 0xa9, 0x01, 0xb0, 0x07, 0x55, 0xa9, 0x00, 0x55, 0xaf, 0x00, + 0x7f, 0x10, 0x4f, 0x38, 0x16, 0x62, 0xd0, 0x00, 0x3c, 0xaa, 0x00, + 0xb0, 0x05, 0x51, 0x9c, 0x53, 0x24, 0x56, 0x0d, + 0x00, 0x80, 0xff, 0x56, 0x00, 0x00, 0x80, 0xf3, 0x62, 0xd0, 0x00, 0x3c, + 0xaa, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x50, 0x55, 0x51, 0x00, + 0x06, 0x50, 0x9c, 0x7c, 0x1d, 0x06, 0x52, 0x00, 0x53, 0x4e, 0x55, + 0x4f, 0x00, 0x06, 0x4e, 0x24, 0x7c, 0x1d, 0xa1, 0x10, 0x52, 0x00, + 0x7c, 0x09, 0x3a, 0x20, 0x10, 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, + 0x20, 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, 0x00, + 0xb0, 0x12, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x55, 0x4c, 0x01, 0x7c, + 0x1c, 0x2a, 0x7c, 0x1c, 0x88, 0x80, 0x80, 0x3d, 0x00, 0x01, 0xb0, + 0x2a, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x65, + 0x4e, 0x6b, 0x4f, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x4f, + 0x08, 0x51, 0x4e, 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, + 0x53, 0x4f, 0x38, 0xfe, 0x7c, 0x1c, 0x88, 0x80, + 0x52, 0x3d, 0x00, 0x02, 0xb0, 0x21, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, + 0x7c, 0x1c, 0x95, 0x55, 0x4c, 0x03, 0x7c, 0x1c, 0x2a, 0x70, 0xfb, + 0x6e, 0x4f, 0x6e, 0x4e, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0x7c, + 0x1c, 0x88, 0x80, 0x2d, 0x3d, 0x00, 0x03, 0xb0, 0x28, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x65, 0x4e, 0x6b, 0x4f, + 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x4f, + 0x08, 0x51, 0x4e, 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, 0x53, + 0x4f, 0x38, 0xfe, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x6c, 0x7c, 0x1c, + 0xc1, 0x52, 0x0d, 0x7c, 0x1d, 0xad, 0x02, 0x50, 0x53, 0x50, 0x51, + 0x4f, 0x0a, 0x51, 0x53, 0x51, 0x7c, 0x1d, 0x6f, 0x06, 0x4e, 0x8c, + 0x0e, 0x4f, 0x00, 0x51, 0x4f, 0x7c, 0x1c, 0xab, 0x7c, 0x1c, 0x88, + 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x0a, 0x77, + 0x0d, 0x3d, 0x0d, 0x03, 0xce, 0xfe, 0x56, 0x00, 0x00, 0x81, 0x06, 0x7c, + 0x1c, 0x6c, 0x7c, 0x1c, 0xc1, 0x51, 0x51, 0x60, 0xd4, 0x3e, 0x50, + 0x54, 0x0e, 0x3e, 0x50, 0x54, 0x0f, 0x52, 0x00, 0x53, 0x50, 0x55, + 0x51, 0x00, 0x55, 0x4e, 0x06, 0x55, 0x4f, 0x00, 0x55, 0x4b, 0x00, + 0x55, 0x4a, 0x00, 0x3c, 0x4f, 0x00, 0xb0, 0x06, 0x3c, 0x4e, 0x00, + 0xa0, 0x1a, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, + 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x50, 0x04, 0x4b, 0x51, 0x51, 0x0c, + 0x4a, 0x65, 0x50, 0x6b, 0x51, 0x8f, 0xde, 0x5f, 0x50, 0x4b, 0x5f, + 0x51, 0x4a, 0x62, 0xd0, 0x00, 0x5a, 0x4e, 0x06, 0x4e, 0x03, 0x51, + 0x4e, 0x04, 0x50, 0x0e, 0x51, 0x03, 0x51, 0x51, 0x60, 0xd4, 0x3e, + 0x50, 0x54, 0x10, 0x3e, 0x50, 0x54, 0x11, 0x52, 0x00, 0x53, 0x50, + 0x55, 0x51, 0x00, 0x55, 0x4e, 0x06, 0x55, 0x4f, + 0x00, 0x55, 0x4b, 0x00, 0x55, 0x4a, 0x00, 0x3c, 0x4f, 0x00, 0xb0, 0x06, + 0x3c, 0x4e, 0x00, 0xa0, 0x1a, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, + 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x50, 0x04, 0x4b, 0x51, 0x51, + 0x0c, 0x4a, 0x65, 0x50, 0x6b, 0x51, 0x8f, 0xde, 0x5f, 0x50, 0x4b, + 0x5f, 0x51, 0x4a, 0x62, 0xd0, 0x00, 0x5a, 0x4e, 0x06, 0x4e, 0x05, + 0x51, 0x4e, 0x04, 0x50, 0x0e, 0x51, 0x03, 0x51, + 0x51, 0x60, 0xd4, 0x3e, 0x50, 0x54, 0x12, 0x3e, 0x50, 0x54, 0x13, 0x50, + 0x03, 0x08, 0x5a, 0x50, 0x06, 0x50, 0x0e, 0x08, 0x51, 0x50, 0x08, + 0x7c, 0x1a, 0x9f, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, 0x50, 0x54, + 0x15, 0x51, 0x51, 0x54, 0x14, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, + 0x8c, 0x7c, 0x1d, 0x1d, 0x06, 0x50, 0x62, 0x7c, 0x1d, 0x5f, 0x7c, + 0x1c, 0x60, 0x51, 0x50, 0x01, 0x6a, 0x7c, 0x1d, + 0x1d, 0x51, 0x50, 0x01, 0x72, 0x7c, 0x1d, 0x1d, 0x06, 0x50, 0x7a, 0x7c, + 0x1d, 0x5f, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0xf7, 0x38, 0xea, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x16, 0x10, 0x57, 0x09, 0x50, 0x01, + 0x7c, 0x08, 0x74, 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x10, 0x08, + 0x57, 0x92, 0x28, 0x53, 0x51, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, + 0x50, 0x20, 0x10, 0x51, 0x51, 0x08, 0x51, 0x50, + 0x20, 0x7c, 0x09, 0xb9, 0x20, 0x10, 0x57, 0x0e, 0x50, 0x01, 0x7c, 0x08, + 0x74, 0x20, 0x62, 0xd0, 0x00, 0x3c, 0xaa, 0x01, 0xb0, 0x0b, 0x51, + 0x24, 0x53, 0x30, 0x51, 0x25, 0x53, 0x31, 0x80, 0x0c, 0x62, 0xd0, + 0x00, 0x51, 0x9c, 0x53, 0x24, 0x51, 0x9d, 0x53, 0x25, 0x10, 0x50, + 0x00, 0x7c, 0x09, 0x3a, 0x20, 0x56, 0x0d, 0x00, 0x80, 0xff, 0x56, + 0x00, 0x00, 0x80, 0xf3, 0x62, 0xd0, 0x00, 0x3c, + 0xaa, 0x00, 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x50, 0x55, 0x51, 0x00, 0x06, + 0x50, 0x9c, 0x7c, 0x1d, 0x06, 0x52, 0x00, 0x53, 0x4e, 0x55, 0x4f, + 0x00, 0x06, 0x4e, 0x24, 0x7c, 0x1d, 0xa1, 0x10, 0x52, 0x00, 0x7c, + 0x09, 0x3a, 0x20, 0x10, 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, 0x20, + 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, 0x00, 0xb0, 0x12, 0x7c, 0x1c, + 0x60, 0x7c, 0x1c, 0x95, 0x55, 0x4c, 0x01, 0x7c, + 0x1c, 0x2a, 0x7c, 0x1c, 0x88, 0x80, 0x80, 0x3d, 0x00, 0x01, 0xb0, 0x2a, + 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x65, 0x4e, + 0x6b, 0x4f, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x4f, 0x08, + 0x51, 0x4e, 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, 0x53, + 0x4f, 0x38, 0xfe, 0x7c, 0x1c, 0x88, 0x80, 0x52, 0x3d, 0x00, 0x02, + 0xb0, 0x21, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, + 0x7c, 0x1c, 0x95, 0x55, 0x4c, 0x03, 0x7c, 0x1c, 0x2a, 0x70, 0xfb, 0x6e, + 0x4f, 0x6e, 0x4e, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0x7c, 0x1c, + 0x88, 0x80, 0x2d, 0x3d, 0x00, 0x03, 0xb0, 0x28, 0x62, 0xd0, 0x00, + 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x65, 0x4e, 0x6b, 0x4f, 0x50, + 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x4f, 0x08, 0x51, 0x4e, 0x08, + 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, 0x53, + 0x4f, 0x38, 0xfe, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x6c, 0x7c, 0x1c, 0xc1, + 0x52, 0x0d, 0x7c, 0x1d, 0xad, 0x02, 0x50, 0x53, 0x50, 0x51, 0x4f, + 0x0a, 0x51, 0x53, 0x51, 0x7c, 0x1d, 0x6f, 0x06, 0x4e, 0x8c, 0x0e, + 0x4f, 0x00, 0x51, 0x4f, 0x7c, 0x1c, 0xab, 0x7c, 0x1c, 0x88, 0x77, + 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x0a, 0x77, 0x0d, 0x3d, 0x0d, 0x03, + 0xce, 0xfe, 0x56, 0x00, 0x00, 0x81, 0x06, 0x7c, + 0x1c, 0x6c, 0x7c, 0x1c, 0xc1, 0x51, 0x51, 0x60, 0xd4, 0x3e, 0x50, 0x54, + 0x0e, 0x3e, 0x50, 0x54, 0x0f, 0x52, 0x00, 0x53, 0x50, 0x55, 0x51, + 0x00, 0x55, 0x4e, 0x06, 0x55, 0x4f, 0x00, 0x55, 0x4b, 0x00, 0x55, + 0x4a, 0x00, 0x3c, 0x4f, 0x00, 0xb0, 0x06, 0x3c, 0x4e, 0x00, 0xa0, + 0x1a, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0xd0, 0x0c, 0x62, 0xd0, + 0x00, 0x51, 0x50, 0x04, 0x4b, 0x51, 0x51, 0x0c, + 0x4a, 0x65, 0x50, 0x6b, 0x51, 0x8f, 0xde, 0x5f, 0x50, 0x4b, 0x5f, 0x51, + 0x4a, 0x62, 0xd0, 0x00, 0x5a, 0x4e, 0x06, 0x4e, 0x03, 0x51, 0x4e, + 0x04, 0x50, 0x0e, 0x51, 0x03, 0x51, 0x51, 0x60, 0xd4, 0x3e, 0x50, + 0x54, 0x10, 0x3e, 0x50, 0x54, 0x11, 0x52, 0x00, 0x53, 0x50, 0x55, + 0x51, 0x00, 0x55, 0x4e, 0x06, 0x55, 0x4f, 0x00, 0x55, 0x4b, 0x00, + 0x55, 0x4a, 0x00, 0x3c, 0x4f, 0x00, 0xb0, 0x06, + 0x3c, 0x4e, 0x00, 0xa0, 0x1a, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0xd0, + 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x50, 0x04, 0x4b, 0x51, 0x51, 0x0c, + 0x4a, 0x65, 0x50, 0x6b, 0x51, 0x8f, 0xde, 0x5f, 0x50, 0x4b, 0x5f, + 0x51, 0x4a, 0x62, 0xd0, 0x00, 0x5a, 0x4e, 0x06, 0x4e, 0x05, 0x51, + 0x4e, 0x04, 0x50, 0x0e, 0x51, 0x03, 0x51, 0x51, 0x60, 0xd4, 0x3e, + 0x50, 0x54, 0x12, 0x3e, 0x50, 0x54, 0x13, 0x50, + 0x03, 0x08, 0x5a, 0x50, 0x06, 0x50, 0x0e, 0x08, 0x51, 0x50, 0x08, 0x7c, + 0x1a, 0x9f, 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, 0x50, 0x54, 0x15, + 0x51, 0x51, 0x54, 0x14, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, + 0x7c, 0x1d, 0x1d, 0x06, 0x50, 0x62, 0x7c, 0x1d, 0x5f, 0x7c, 0x1c, + 0x60, 0x51, 0x50, 0x01, 0x6a, 0x7c, 0x1d, 0x1d, 0x51, 0x50, 0x01, + 0x72, 0x7c, 0x1d, 0x1d, 0x06, 0x50, 0x7a, 0x7c, + 0x1d, 0x5f, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0xf7, 0x56, 0x00, 0x00, + 0x80, 0x19, 0x7c, 0x1c, 0x6c, 0x06, 0x50, 0x24, 0x7c, 0x1d, 0x06, + 0x52, 0x00, 0x53, 0x4e, 0x55, 0x4f, 0x00, 0x06, 0x4e, 0x30, 0x7c, + 0x1d, 0xa1, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0xe4, 0x38, 0xea, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x62, 0xd0, 0x00, 0x52, 0xfc, + 0x01, 0x02, 0x53, 0x50, 0x52, 0xfb, 0x09, 0x00, + 0x7c, 0x1d, 0x12, 0x52, 0xfc, 0x01, 0x04, 0x53, 0x4e, 0x52, 0xfb, 0x7c, + 0x1c, 0xb6, 0x12, 0x50, 0x51, 0x4f, 0x1a, 0x51, 0xc0, 0x6f, 0x52, + 0xfc, 0x53, 0x50, 0x52, 0xfb, 0x7c, 0x1d, 0x12, 0x52, 0xfc, 0x01, + 0x02, 0x53, 0x4e, 0x52, 0xfb, 0x7c, 0x1c, 0xb6, 0x12, 0x50, 0x51, + 0x4f, 0x1a, 0x51, 0xc0, 0x10, 0x52, 0xfc, 0x01, 0x02, 0x7c, 0x1d, + 0x3b, 0x54, 0x00, 0x3e, 0x50, 0x54, 0x01, 0x80, + 0xb3, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x01, 0x04, 0x53, 0x50, 0x52, 0xfb, + 0x09, 0x00, 0x7c, 0x1d, 0x12, 0x52, 0xfc, 0x53, 0x4e, 0x52, 0xfb, + 0x60, 0xd4, 0x3e, 0x4e, 0x53, 0x4f, 0x3e, 0x4e, 0x12, 0x50, 0x51, + 0x4f, 0x1a, 0x51, 0xc0, 0x10, 0x52, 0xfc, 0x01, 0x04, 0x7c, 0x1d, + 0x3b, 0x54, 0x00, 0x3e, 0x50, 0x54, 0x01, 0x80, 0x7e, 0x62, 0xd0, + 0x00, 0x52, 0xfc, 0x53, 0x50, 0x52, 0xfb, 0x7c, + 0x1d, 0x54, 0x80, 0x70, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, 0x50, 0x52, + 0xfb, 0x7c, 0x1d, 0x12, 0x52, 0xfc, 0x01, 0x04, 0x53, 0x4e, 0x52, + 0xfb, 0x7c, 0x1c, 0xb6, 0x12, 0x50, 0x51, 0x4f, 0x1a, 0x51, 0xc0, + 0x10, 0x52, 0xfc, 0x01, 0x04, 0x7c, 0x1d, 0x3b, 0x54, 0x00, 0x3e, + 0x50, 0x54, 0x01, 0x80, 0x42, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x01, + 0x02, 0x53, 0x50, 0x52, 0xfb, 0x09, 0x00, 0x7c, + 0x1d, 0x12, 0x52, 0xfc, 0x53, 0x4e, 0x52, 0xfb, 0x60, 0xd4, 0x3e, 0x4e, + 0x53, 0x4f, 0x3e, 0x4e, 0x12, 0x50, 0x51, 0x4f, 0x1a, 0x51, 0xc0, + 0x10, 0x52, 0xfc, 0x01, 0x02, 0x7c, 0x1d, 0x3b, 0x54, 0x00, 0x3e, + 0x50, 0x54, 0x01, 0x80, 0x0d, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, + 0x50, 0x52, 0xfb, 0x7c, 0x1d, 0x54, 0x62, 0xd0, 0x00, 0x52, 0x01, + 0x53, 0x50, 0x52, 0x00, 0x53, 0x51, 0x38, 0xfe, + 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x05, 0x62, 0xd0, 0x00, 0x55, 0xb2, 0x00, + 0x56, 0x00, 0x00, 0x80, 0xcd, 0x62, 0xd0, 0x00, 0x3c, 0xaa, 0x00, + 0xb0, 0x1b, 0x52, 0x00, 0x53, 0x50, 0x55, 0x51, 0x00, 0x06, 0x50, + 0x9c, 0x7c, 0x1d, 0x06, 0x52, 0x00, 0x53, 0x4e, 0x55, 0x4f, 0x00, + 0x06, 0x4e, 0x24, 0x7c, 0x1d, 0xa1, 0x10, 0x52, 0x00, 0x7c, 0x09, + 0x3a, 0x20, 0x10, 0x7c, 0x05, 0xc5, 0x62, 0xd0, + 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, 0x3d, 0x00, 0x00, 0xb0, 0x12, 0x7c, + 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x55, 0x4c, 0x01, 0x7c, 0x1c, 0x2a, + 0x7c, 0x1c, 0x88, 0x80, 0x80, 0x3d, 0x00, 0x01, 0xb0, 0x2a, 0x62, + 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x65, 0x4e, 0x6b, + 0x4f, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x4f, 0x08, 0x51, + 0x4e, 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, + 0x18, 0x53, 0x4f, 0x38, 0xfe, 0x7c, 0x1c, 0x88, 0x80, 0x52, 0x3d, 0x00, + 0x02, 0xb0, 0x21, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, + 0x95, 0x55, 0x4c, 0x03, 0x7c, 0x1c, 0x2a, 0x70, 0xfb, 0x6e, 0x4f, + 0x6e, 0x4e, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0x7c, 0x1c, 0x88, + 0x80, 0x2d, 0x3d, 0x00, 0x03, 0xb0, 0x28, 0x62, 0xd0, 0x00, 0x7c, + 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x65, 0x4e, 0x6b, + 0x4f, 0x50, 0x00, 0x08, 0x50, 0x05, 0x08, 0x51, 0x4f, 0x08, 0x51, 0x4e, + 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, 0x53, 0x4f, 0x38, + 0xfe, 0x7c, 0x1c, 0x88, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0x30, + 0x56, 0x00, 0x00, 0x82, 0x86, 0x62, 0xd0, 0x00, 0x3c, 0xb1, 0x02, + 0xa0, 0x9f, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x62, 0x7c, 0x1c, + 0x77, 0x06, 0x50, 0x8c, 0x7c, 0x1d, 0x06, 0x7c, + 0x1d, 0x2e, 0xd0, 0x16, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x62, 0x7c, + 0x1c, 0x77, 0x06, 0x50, 0x8c, 0x7c, 0x1d, 0x06, 0x7c, 0x1d, 0xd2, + 0x80, 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, + 0x8c, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x62, 0x7c, 0x1d, 0x06, 0x7c, + 0x1d, 0xd2, 0x50, 0x90, 0x13, 0x02, 0x50, 0x01, 0x1b, 0x01, 0xc0, + 0x4e, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x51, + 0x50, 0x01, 0x7a, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x8c, 0x7c, 0x1d, 0x06, + 0x7c, 0x1d, 0x2e, 0xd0, 0x16, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, + 0x7a, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x8c, 0x7c, 0x1d, 0x06, 0x7c, + 0x1d, 0xc5, 0x80, 0x17, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x51, + 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x7a, 0x7c, 0x1d, + 0x06, 0x7c, 0x1d, 0xc5, 0x50, 0x90, 0x13, 0x04, + 0x50, 0x01, 0x1b, 0x03, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x76, 0xb2, 0x81, + 0xde, 0x62, 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, + 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x7a, 0x7c, 0x1d, 0x06, 0x7c, 0x1d, + 0x2e, 0xd0, 0x5a, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x06, 0x4e, + 0x01, 0x0e, 0x4f, 0x00, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x60, 0x51, + 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, 0x06, 0x50, + 0x7a, 0x7c, 0x1d, 0x06, 0x7c, 0x1d, 0x2e, 0xd0, 0xb4, 0x7c, 0x1c, 0x60, + 0x7c, 0x1c, 0x95, 0x06, 0x4e, 0x01, 0x0e, 0x4f, 0x00, 0x7c, 0x1c, + 0x88, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, + 0x06, 0x50, 0x7a, 0x7c, 0x1d, 0x06, 0x7c, 0x1d, 0x2e, 0xd0, 0x90, + 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x06, 0x4e, 0x01, 0x0e, 0x4f, + 0x00, 0x7c, 0x1c, 0x88, 0x80, 0x7f, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, 0x06, + 0x50, 0x7a, 0x7c, 0x1d, 0x06, 0x3e, 0x50, 0x12, 0x4e, 0x51, 0x51, + 0x1a, 0x4f, 0xd0, 0x62, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x16, + 0x4e, 0x01, 0x1e, 0x4f, 0x00, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x60, + 0x51, 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x7a, 0x7c, + 0x1d, 0x06, 0x3e, 0x50, 0x12, 0x4e, 0x51, 0x51, + 0x1a, 0x4f, 0xd0, 0x39, 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x16, 0x4e, + 0x01, 0x1e, 0x4f, 0x00, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x60, 0x51, + 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x7a, 0x7c, 0x1d, + 0x06, 0x3e, 0x50, 0x12, 0x4e, 0x51, 0x51, 0x1a, 0x4f, 0xd0, 0x10, + 0x7c, 0x1c, 0x60, 0x7c, 0x1c, 0x95, 0x16, 0x4e, 0x01, 0x1e, 0x4f, + 0x00, 0x7c, 0x1c, 0x88, 0x62, 0xd0, 0x00, 0x7c, + 0x1c, 0x60, 0x51, 0x50, 0x01, 0x72, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x6a, + 0x0e, 0x51, 0x00, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x60, 0x51, 0x50, + 0x01, 0x7a, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x72, 0x0e, 0x51, 0x00, + 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, 0x7c, + 0x1c, 0x77, 0x06, 0x50, 0x7a, 0x0e, 0x51, 0x00, 0x7c, 0x1c, 0x88, + 0x10, 0x52, 0x00, 0x7c, 0x06, 0x09, 0x20, 0x62, + 0xd0, 0x00, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, + 0x06, 0x50, 0x62, 0x7c, 0x1d, 0x06, 0x7c, 0x1d, 0x2e, 0xd0, 0x25, + 0x52, 0x00, 0x53, 0x50, 0x55, 0x51, 0x00, 0x06, 0x50, 0x98, 0x0e, + 0x51, 0x00, 0x51, 0x51, 0x60, 0xd4, 0x3e, 0x50, 0x7a, 0x50, 0x53, + 0x4f, 0x06, 0x4f, 0x01, 0x51, 0x51, 0x60, 0xd5, 0x51, 0x4f, 0x3f, + 0x50, 0x80, 0x0a, 0x7c, 0x1c, 0x6c, 0x06, 0x50, + 0x98, 0x7c, 0x1d, 0xb9, 0x7c, 0x1c, 0x6c, 0x06, 0x50, 0x98, 0x7c, 0x1d, + 0x06, 0x50, 0x05, 0x3a, 0x51, 0xd0, 0x58, 0x7c, 0x1c, 0x60, 0x51, + 0x50, 0x01, 0x62, 0x53, 0x4e, 0x51, 0x51, 0x09, 0x00, 0x53, 0x4f, + 0x06, 0x50, 0x8c, 0x7c, 0x1d, 0x06, 0x3e, 0x50, 0x53, 0x50, 0x51, + 0x4f, 0x60, 0xd4, 0x3e, 0x4e, 0x53, 0x4d, 0x3e, 0x4e, 0x16, 0x4e, + 0x02, 0x02, 0x50, 0x53, 0x50, 0x51, 0x4d, 0x0a, + 0x51, 0x53, 0x51, 0x70, 0xfb, 0x6e, 0x51, 0x6e, 0x50, 0x51, 0x4f, 0x60, + 0xd5, 0x51, 0x51, 0x3f, 0x4e, 0x51, 0x50, 0x3f, 0x4e, 0x52, 0x00, + 0x53, 0x50, 0x55, 0x51, 0x00, 0x06, 0x50, 0x98, 0x0e, 0x51, 0x00, + 0x51, 0x51, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x50, 0x77, 0x00, 0x3d, + 0x00, 0x02, 0xcd, 0x77, 0x62, 0xd0, 0x00, 0x3c, 0xb1, 0x02, 0xb2, + 0x20, 0x56, 0x00, 0x00, 0x82, 0x16, 0x62, 0xd0, + 0x00, 0x7c, 0x1c, 0x60, 0x51, 0x50, 0x01, 0x8c, 0x7c, 0x1c, 0x77, 0x06, + 0x50, 0x38, 0x0e, 0x51, 0x00, 0x7c, 0x1c, 0x88, 0x7c, 0x1c, 0x60, + 0x51, 0x50, 0x01, 0x62, 0x7c, 0x1c, 0x77, 0x06, 0x50, 0x3c, 0x0e, + 0x51, 0x00, 0x7c, 0x1c, 0x88, 0x97, 0xf7, 0x40, 0x51, 0x50, 0x01, + 0x62, 0x7c, 0x1c, 0x77, 0x51, 0x50, 0x01, 0x8c, 0x53, 0x4c, 0x51, + 0x51, 0x7c, 0x1d, 0x7b, 0x51, 0x4e, 0x12, 0x4c, + 0x51, 0x4f, 0x1a, 0x4d, 0xd0, 0x21, 0x7c, 0x1d, 0x6f, 0x51, 0x4e, 0x01, + 0x62, 0x53, 0x4c, 0x51, 0x4f, 0x7c, 0x1d, 0x7b, 0x06, 0x4e, 0x8c, + 0x7c, 0x1d, 0x46, 0x12, 0x4c, 0x54, 0x02, 0x51, 0x4f, 0x1a, 0x4d, + 0x54, 0x01, 0x80, 0x07, 0x56, 0x02, 0x00, 0x56, 0x01, 0x00, 0x62, + 0xd0, 0x00, 0x06, 0x50, 0x34, 0x0e, 0x51, 0x00, 0x51, 0x51, 0x60, + 0xd5, 0x52, 0x01, 0x3f, 0x50, 0x52, 0x02, 0x3f, + 0x50, 0x3d, 0x00, 0x00, 0xb0, 0x35, 0x97, 0x98, 0x40, 0x51, 0x50, 0x01, + 0x8c, 0x97, 0xa8, 0x40, 0x55, 0x4c, 0x01, 0x97, 0x55, 0x40, 0x06, + 0x50, 0x38, 0x0e, 0x51, 0x00, 0x97, 0xaa, 0x40, 0x97, 0x7f, 0x40, + 0x51, 0x50, 0x01, 0x62, 0x97, 0x8f, 0x40, 0x55, 0x4c, 0x01, 0x97, + 0x3c, 0x40, 0x06, 0x50, 0x3c, 0x0e, 0x51, 0x00, 0x97, 0x91, 0x40, + 0x80, 0xfb, 0x3d, 0x00, 0x01, 0xb0, 0x44, 0x62, + 0xd0, 0x00, 0x97, 0x5c, 0x40, 0x51, 0x50, 0x01, 0x8c, 0x97, 0x6c, 0x40, + 0x55, 0x4c, 0x05, 0x97, 0x19, 0x40, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, + 0x4e, 0x06, 0x50, 0x38, 0x0e, 0x51, 0x00, 0x97, 0x68, 0x40, 0x97, + 0x3d, 0x40, 0x51, 0x50, 0x01, 0x62, 0x97, 0x4d, 0x40, 0x55, 0x4c, + 0x05, 0x96, 0xfa, 0x40, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0x06, + 0x50, 0x3c, 0x0e, 0x51, 0x00, 0x97, 0x49, 0x40, + 0x80, 0xb3, 0x3d, 0x00, 0x02, 0xb0, 0x68, 0x62, 0xd0, 0x00, 0x97, 0x14, + 0x40, 0x51, 0x50, 0x01, 0x8c, 0x97, 0x24, 0x40, 0x55, 0x4c, 0x03, + 0x96, 0xd1, 0x40, 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x51, 0x4f, + 0x08, 0x51, 0x4e, 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, + 0x53, 0x4f, 0x38, 0xfe, 0x06, 0x50, 0x38, 0x0e, 0x51, 0x00, 0x97, + 0x0f, 0x40, 0x96, 0xe4, 0x40, 0x51, 0x50, 0x01, + 0x62, 0x96, 0xf4, 0x40, 0x65, 0x4e, 0x6b, 0x4f, 0x65, 0x4e, 0x6b, 0x4f, + 0x50, 0x00, 0x08, 0x50, 0x03, 0x08, 0x51, 0x4f, 0x08, 0x51, 0x4e, + 0x08, 0x7c, 0x1b, 0xe6, 0x18, 0x53, 0x4e, 0x18, 0x53, 0x4f, 0x38, + 0xfe, 0x06, 0x50, 0x3c, 0x0e, 0x51, 0x00, 0x96, 0xdd, 0x40, 0x80, + 0x47, 0x3d, 0x00, 0x03, 0xb0, 0x42, 0x62, 0xd0, 0x00, 0x96, 0xa8, + 0x40, 0x51, 0x50, 0x01, 0x8c, 0x96, 0xb8, 0x40, + 0x55, 0x4c, 0x05, 0x96, 0x65, 0x40, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, + 0x06, 0x50, 0x38, 0x0e, 0x51, 0x00, 0x96, 0xb4, 0x40, 0x96, 0x89, + 0x40, 0x51, 0x50, 0x01, 0x62, 0x96, 0x99, 0x40, 0x55, 0x4c, 0x05, + 0x96, 0x46, 0x40, 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0x06, 0x50, + 0x3c, 0x0e, 0x51, 0x00, 0x96, 0x95, 0x40, 0x62, 0xd0, 0x00, 0x96, + 0x67, 0x40, 0x51, 0x50, 0x01, 0x62, 0x96, 0x77, + 0x40, 0x51, 0x50, 0x01, 0x8c, 0x53, 0x4c, 0x51, 0x51, 0x97, 0x70, 0x40, + 0x51, 0x4e, 0x12, 0x4c, 0x51, 0x4f, 0x1a, 0x4d, 0xd0, 0x21, 0x97, + 0x57, 0x40, 0x51, 0x4e, 0x01, 0x62, 0x53, 0x4c, 0x51, 0x4f, 0x97, + 0x58, 0x40, 0x06, 0x4e, 0x8c, 0x97, 0x1d, 0x40, 0x12, 0x4c, 0x54, + 0x04, 0x51, 0x4f, 0x1a, 0x4d, 0x54, 0x03, 0x80, 0x07, 0x56, 0x04, + 0x00, 0x56, 0x03, 0x00, 0x62, 0xd0, 0x00, 0x06, + 0x50, 0x34, 0x0e, 0x51, 0x00, 0x51, 0x51, 0x60, 0xd5, 0x52, 0x03, 0x3f, + 0x50, 0x52, 0x04, 0x3f, 0x50, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcd, + 0xe7, 0x62, 0xd0, 0x00, 0x3c, 0xb1, 0x02, 0xa0, 0x18, 0x3c, 0xb2, + 0x00, 0xa0, 0x13, 0x50, 0x01, 0x08, 0x50, 0x2c, 0x08, 0x90, 0x0e, + 0x38, 0xfe, 0x7c, 0x0a, 0xee, 0x10, 0x7c, 0x07, 0xc5, 0x20, 0x38, + 0xfb, 0x20, 0x7f, 0x10, 0x4f, 0x80, 0x02, 0x40, + 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, 0x50, 0x52, 0xfb, 0x53, 0x51, 0x51, + 0x50, 0x11, 0x01, 0x54, 0xfc, 0x51, 0x51, 0x19, 0x00, 0x54, 0xfb, + 0x3c, 0x51, 0x00, 0xbf, 0xe4, 0x3c, 0x50, 0x00, 0xbf, 0xdf, 0x20, + 0x7f, 0x10, 0x7c, 0x04, 0x8f, 0x7c, 0x04, 0x6c, 0x20, 0x7f, 0x10, + 0x7c, 0x04, 0x8b, 0x7c, 0x04, 0x68, 0x20, 0x7f, 0x62, 0xd0, 0x00, + 0x51, 0x42, 0x12, 0x67, 0x50, 0x00, 0x1a, 0x66, + 0xd0, 0x0f, 0x51, 0x43, 0x12, 0x69, 0x50, 0x00, 0x1a, 0x68, 0xd0, 0x05, + 0x50, 0x0f, 0x80, 0x17, 0x62, 0xd0, 0x00, 0x51, 0x69, 0x12, 0x67, + 0x51, 0x68, 0x1a, 0x66, 0xd0, 0x05, 0x50, 0x00, 0x80, 0x06, 0x62, + 0xd0, 0x00, 0x50, 0x01, 0x7f, 0x10, 0x4f, 0x38, 0x05, 0x62, 0xd0, + 0x00, 0x51, 0x67, 0x54, 0x02, 0x51, 0x66, 0x54, 0x01, 0x56, 0x04, + 0x00, 0x56, 0x00, 0x00, 0x56, 0x03, 0x00, 0x80, + 0x61, 0x95, 0x69, 0x40, 0x06, 0x50, 0x42, 0x0e, 0x51, 0x00, 0x51, 0x51, + 0x60, 0xd4, 0x3e, 0x50, 0x53, 0x50, 0x96, 0x5b, 0x40, 0x06, 0x4e, + 0x66, 0x0e, 0x4f, 0x00, 0x51, 0x4f, 0x95, 0x8c, 0x40, 0x51, 0x50, + 0x12, 0x4e, 0x50, 0x00, 0x1a, 0x4f, 0xd0, 0x03, 0x77, 0x03, 0x62, + 0xd0, 0x00, 0x95, 0x2f, 0x40, 0x06, 0x50, 0x66, 0x95, 0xcf, 0x40, + 0x3e, 0x50, 0x53, 0x50, 0x52, 0x02, 0x12, 0x50, + 0x52, 0x01, 0x1a, 0x51, 0xd0, 0x1a, 0x95, 0x18, 0x40, 0x06, 0x50, 0x66, + 0x0e, 0x51, 0x00, 0x51, 0x51, 0x60, 0xd4, 0x3e, 0x50, 0x54, 0x01, + 0x3e, 0x50, 0x54, 0x02, 0x52, 0x00, 0x54, 0x04, 0x77, 0x00, 0x3d, + 0x00, 0x02, 0xcf, 0x9c, 0x50, 0x01, 0x3b, 0x03, 0xd0, 0x08, 0x62, + 0xd0, 0x00, 0x50, 0x0f, 0x80, 0x06, 0x52, 0x04, 0x62, 0xd0, 0x00, + 0x38, 0xfb, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, + 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x26, 0x2a, 0xf0, 0x51, 0xb0, 0x01, 0x01, + 0x53, 0x51, 0x51, 0x2a, 0x2a, 0x51, 0x53, 0x2a, 0x71, 0x01, 0x62, + 0xe3, 0x38, 0x10, 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, 0x20, 0x41, + 0x00, 0xf7, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, + 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x51, 0x47, 0x51, + 0x20, 0xa0, 0x03, 0x80, 0x1a, 0x50, 0x00, 0x08, + 0x50, 0x04, 0x08, 0x9e, 0xb6, 0x38, 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, + 0x52, 0x01, 0x11, 0xdc, 0x52, 0x00, 0x19, 0x05, 0xcf, 0xd7, 0x56, + 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, 0x7c, 0x05, 0xc5, + 0x62, 0xd0, 0x00, 0x20, 0x53, 0x51, 0x47, 0x51, 0x20, 0xb0, 0x03, + 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x04, 0x08, 0x9e, 0x84, 0x38, + 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, + 0x11, 0x2c, 0x52, 0x00, 0x19, 0x01, 0xcf, 0xd7, 0x43, 0x00, 0x08, 0x38, + 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x02, 0x70, 0xfe, 0x62, 0xd0, + 0x00, 0x26, 0x2a, 0xf0, 0x51, 0xb0, 0x01, 0x09, 0x53, 0x51, 0x51, + 0x2a, 0x2a, 0x51, 0x53, 0x2a, 0x71, 0x01, 0x62, 0xe3, 0x38, 0x10, + 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, + 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x21, 0x10, + 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x51, 0x47, 0x51, 0x20, + 0xa0, 0x03, 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x04, 0x08, 0x9e, + 0x23, 0x38, 0xfe, 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, 0x11, + 0xdc, 0x52, 0x00, 0x19, 0x05, 0xcf, 0xd7, 0x56, 0x01, 0x00, 0x56, + 0x00, 0x00, 0x80, 0x21, 0x10, 0x7c, 0x05, 0xc5, 0x62, 0xd0, 0x00, + 0x20, 0x53, 0x51, 0x47, 0x51, 0x20, 0xb0, 0x03, + 0x80, 0x1a, 0x50, 0x00, 0x08, 0x50, 0x04, 0x08, 0x9d, 0xf1, 0x38, 0xfe, + 0x77, 0x01, 0x0f, 0x00, 0x00, 0x52, 0x01, 0x11, 0x2c, 0x52, 0x00, + 0x19, 0x01, 0xcf, 0xd7, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, + 0x10, 0x4f, 0x38, 0x04, 0x62, 0xd0, 0x00, 0x51, 0x2a, 0x21, 0xf0, + 0x54, 0x00, 0x51, 0x2d, 0x54, 0x01, 0x3d, 0x00, 0x10, 0xb0, 0x2a, + 0x55, 0xa7, 0x00, 0x3c, 0xab, 0x01, 0xb0, 0x09, + 0x55, 0x94, 0x00, 0x55, 0x95, 0x00, 0x80, 0x0f, 0x62, 0xd0, 0x00, 0x3c, + 0xae, 0x01, 0xa0, 0x07, 0x55, 0x94, 0x00, 0x55, 0x95, 0x00, 0x56, + 0x00, 0x00, 0x62, 0xd0, 0x00, 0x26, 0x2a, 0x0f, 0x81, 0x76, 0x3d, + 0x00, 0x20, 0xb0, 0x18, 0x62, 0xd0, 0x00, 0x55, 0xa7, 0x01, 0x55, + 0xa8, 0x00, 0x55, 0x94, 0x08, 0x55, 0x95, 0x08, 0x56, 0x00, 0x00, + 0x26, 0x2a, 0x0f, 0x81, 0x5a, 0x3d, 0x00, 0x30, + 0xb0, 0x0f, 0x62, 0xd0, 0x00, 0x55, 0xb1, 0x00, 0x56, 0x00, 0x00, 0x26, + 0x2a, 0x0f, 0x81, 0x47, 0x3d, 0x00, 0x40, 0xb0, 0x0f, 0x62, 0xd0, + 0x00, 0x55, 0xb1, 0x02, 0x56, 0x00, 0x00, 0x26, 0x2a, 0x0f, 0x81, + 0x34, 0x3d, 0x00, 0x50, 0xb0, 0xa7, 0x52, 0x01, 0x54, 0x03, 0x56, + 0x02, 0x00, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x01, 0xa0, + 0x21, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, + 0x02, 0xa0, 0x28, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x04, 0xa0, + 0x36, 0x3d, 0x02, 0x00, 0xb0, 0x06, 0x3d, 0x03, 0x08, 0xa0, 0x48, + 0x80, 0x62, 0x62, 0xd0, 0x00, 0x55, 0xaa, 0x01, 0x51, 0x2f, 0x29, + 0x80, 0x53, 0x2f, 0x7c, 0x0d, 0x19, 0x80, 0x51, 0x62, 0xd0, 0x00, + 0x51, 0x2b, 0x53, 0x42, 0x51, 0x2b, 0x53, 0x43, 0x51, 0x2b, 0x53, + 0x2e, 0x51, 0x2c, 0x53, 0x0e, 0x55, 0x0d, 0x00, + 0x80, 0x39, 0x62, 0xd0, 0x00, 0x51, 0x2b, 0x53, 0x2f, 0x3c, 0xaa, 0x00, + 0xa0, 0x09, 0x51, 0x2f, 0x29, 0x80, 0x53, 0x2f, 0x80, 0x25, 0x62, + 0xd0, 0x00, 0x26, 0x2f, 0x7f, 0x80, 0x1d, 0x62, 0xd0, 0x00, 0x55, + 0xaa, 0x00, 0x26, 0x2f, 0x7f, 0x51, 0x9c, 0x53, 0x24, 0x51, 0x24, + 0x53, 0x30, 0x51, 0x9d, 0x53, 0x25, 0x51, 0x25, 0x53, 0x31, 0x7c, + 0x0d, 0x19, 0x56, 0x00, 0x00, 0x62, 0xd0, 0x00, + 0x26, 0x2a, 0x0f, 0x55, 0x2b, 0x11, 0x55, 0x2c, 0x08, 0x55, 0x2d, 0x00, + 0x80, 0x89, 0x3d, 0x00, 0x60, 0xb0, 0x0f, 0x62, 0xd0, 0x00, 0x55, + 0xab, 0x01, 0x56, 0x00, 0x00, 0x26, 0x2a, 0x0f, 0x80, 0x76, 0x3d, + 0x00, 0x70, 0xb0, 0x0f, 0x62, 0xd0, 0x00, 0x55, 0xab, 0x00, 0x56, + 0x00, 0x00, 0x26, 0x2a, 0x0f, 0x80, 0x63, 0x3d, 0x00, 0x80, 0xb0, + 0x5e, 0x56, 0x00, 0x00, 0x62, 0xd0, 0x00, 0x26, + 0x2a, 0x0f, 0x9c, 0x9f, 0x10, 0x7c, 0x08, 0xb8, 0x7c, 0x05, 0xdb, 0x20, + 0x70, 0xfe, 0x93, 0xcf, 0x40, 0x62, 0xda, 0x00, 0x71, 0x10, 0x41, + 0xdc, 0xfe, 0x70, 0xcf, 0x43, 0x01, 0x08, 0x43, 0x00, 0x08, 0x50, + 0x00, 0x08, 0x50, 0x1e, 0x08, 0x9c, 0x52, 0x38, 0xfe, 0x71, 0x01, + 0x43, 0xe0, 0x10, 0x43, 0xff, 0x08, 0x70, 0xfe, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x10, 0x7c, 0x07, 0xe7, + 0x7c, 0x05, 0x8f, 0x7c, 0x05, 0xd0, 0x20, 0x93, 0x3f, 0x40, 0x62, 0xe3, + 0x38, 0x56, 0x00, 0x00, 0x62, 0xd0, 0x00, 0x26, 0x2a, 0x0f, 0x38, + 0xfc, 0x20, 0x7f, 0x62, 0xd0, 0x00, 0x3c, 0xa7, 0x00, 0xa0, 0x13, + 0x9c, 0x3f, 0x62, 0xd0, 0x00, 0x3c, 0xa8, 0x00, 0xb0, 0x33, 0x55, + 0xa8, 0x01, 0x7c, 0x0a, 0xee, 0x80, 0x2b, 0x62, 0xd0, 0x00, 0x50, + 0x01, 0x3a, 0x94, 0xd0, 0x08, 0x10, 0x7c, 0x04, + 0x8f, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0x8b, 0x20, 0x62, 0xd0, 0x00, + 0x50, 0x01, 0x3a, 0x95, 0xd0, 0x08, 0x10, 0x7c, 0x04, 0x6c, 0x20, + 0x80, 0x06, 0x10, 0x7c, 0x04, 0x68, 0x20, 0x7f, 0x10, 0x4f, 0x38, + 0x03, 0x56, 0x02, 0x00, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, + 0x3e, 0x62, 0xd0, 0x00, 0x91, 0xad, 0x40, 0x52, 0xfc, 0x04, 0x50, + 0x52, 0xfb, 0x0c, 0x51, 0x51, 0x51, 0x60, 0xd4, + 0x3e, 0x50, 0x53, 0x51, 0x3e, 0x50, 0x53, 0x50, 0x52, 0x02, 0x12, 0x50, + 0x52, 0x01, 0x1a, 0x51, 0xd0, 0x18, 0x91, 0x8c, 0x40, 0x52, 0xfc, + 0x04, 0x50, 0x52, 0xfb, 0x0c, 0x51, 0x51, 0x51, 0x60, 0xd4, 0x3e, + 0x50, 0x54, 0x01, 0x3e, 0x50, 0x54, 0x02, 0x77, 0x00, 0x52, 0x00, + 0x3b, 0xfa, 0xcf, 0xbe, 0x62, 0xd0, 0x00, 0x52, 0x02, 0x53, 0x50, + 0x52, 0x01, 0x53, 0x51, 0x38, 0xfd, 0x20, 0x7f, + 0x10, 0x7c, 0x04, 0x1a, 0x20, 0x10, 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, + 0x50, 0x8c, 0x08, 0x7c, 0x04, 0x23, 0x38, 0xfd, 0x20, 0x10, 0x50, + 0x04, 0x08, 0x50, 0x00, 0x08, 0x50, 0x62, 0x08, 0x7c, 0x04, 0x23, + 0x38, 0xfd, 0x20, 0x10, 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, 0x50, + 0x66, 0x08, 0x7c, 0x04, 0x23, 0x38, 0xfd, 0x20, 0x10, 0x50, 0x00, + 0x7c, 0x03, 0x3e, 0x20, 0x10, 0x50, 0xff, 0x7c, + 0x03, 0x3e, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x3e, 0x20, 0x7f, 0x62, + 0xd0, 0x00, 0x55, 0xaa, 0x00, 0x55, 0xab, 0x01, 0x10, 0x7c, 0x04, + 0x8f, 0x7c, 0x04, 0x6c, 0x20, 0x9b, 0x45, 0x62, 0xe3, 0x38, 0x92, + 0x7c, 0x40, 0x43, 0x00, 0x08, 0x62, 0xd0, 0x00, 0x55, 0x2a, 0x08, + 0x55, 0x2b, 0x11, 0x55, 0x2c, 0x08, 0x55, 0x2e, 0x1e, 0x55, 0x2f, + 0x03, 0x55, 0x30, 0x5f, 0x55, 0x31, 0x28, 0x55, + 0x32, 0x00, 0x55, 0x33, 0x00, 0x3c, 0xaa, 0x00, 0xa0, 0x09, 0x51, 0x2f, + 0x29, 0x80, 0x53, 0x2f, 0x80, 0x07, 0x62, 0xd0, 0x00, 0x26, 0x2f, + 0x7f, 0x10, 0x50, 0x00, 0x08, 0x50, 0x2a, 0x08, 0x50, 0x06, 0x08, + 0x50, 0x16, 0x08, 0x7c, 0x05, 0xe2, 0x38, 0xfc, 0x7c, 0x05, 0x8f, + 0x7c, 0x05, 0xd0, 0x20, 0x91, 0xd5, 0x40, 0x10, 0x7c, 0x07, 0xe7, + 0x7c, 0x07, 0x6d, 0x20, 0x7c, 0x0d, 0x19, 0x80, + 0x22, 0x62, 0xe3, 0x38, 0x7c, 0x10, 0xc2, 0x10, 0x7c, 0x07, 0xab, 0x62, + 0xd0, 0x00, 0x20, 0x39, 0x00, 0xa0, 0x09, 0x7c, 0x0a, 0x07, 0x7c, + 0x0a, 0x31, 0x80, 0x04, 0x7c, 0x0a, 0x9f, 0x9c, 0xc2, 0x9e, 0x78, + 0x8f, 0xde, 0x8f, 0xff, 0x10, 0x4f, 0x7c, 0x1b, 0xf1, 0x20, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, + 0x00, 0x53, 0x4a, 0x53, 0x4b, 0x55, 0x49, 0x10, + 0x66, 0xfc, 0x6c, 0xfb, 0x6b, 0x4a, 0x6b, 0x4b, 0x51, 0x4a, 0x1b, 0xfa, + 0x51, 0x4b, 0x1b, 0xf9, 0xc0, 0x09, 0x53, 0x4b, 0x52, 0xfa, 0x1c, + 0x4a, 0x77, 0xfc, 0x7a, 0x49, 0xbf, 0xe3, 0x51, 0x4a, 0x54, 0xfa, + 0x51, 0x4b, 0x54, 0xf9, 0x18, 0x60, 0xd0, 0x7f, 0x55, 0x4d, 0x00, + 0x55, 0x4b, 0x00, 0x55, 0x4a, 0x00, 0x3c, 0x4d, 0x00, 0xb0, 0x06, + 0x3c, 0x4c, 0x00, 0xa0, 0x1a, 0x70, 0xfb, 0x6e, + 0x4d, 0x6e, 0x4c, 0xd0, 0x0c, 0x62, 0xd0, 0x00, 0x51, 0x4e, 0x04, 0x4b, + 0x51, 0x4f, 0x0c, 0x4a, 0x65, 0x4e, 0x6b, 0x4f, 0x8f, 0xde, 0x5f, + 0x4e, 0x4b, 0x5f, 0x4f, 0x4a, 0x62, 0xd0, 0x00, 0x7f, 0x52, 0x00, + 0x53, 0x50, 0x55, 0x51, 0x00, 0x65, 0x50, 0x6b, 0x51, 0x7f, 0x62, + 0xd0, 0x00, 0x52, 0x00, 0x53, 0x50, 0x55, 0x51, 0x00, 0x7f, 0x53, + 0x4e, 0x51, 0x51, 0x09, 0x00, 0x60, 0xd4, 0x3e, + 0x4e, 0x53, 0x4f, 0x3e, 0x4e, 0x53, 0x4e, 0x7f, 0x51, 0x51, 0x60, 0xd5, + 0x51, 0x4f, 0x3f, 0x50, 0x51, 0x4e, 0x3f, 0x50, 0x7f, 0x06, 0x50, + 0x8c, 0x0e, 0x51, 0x00, 0x51, 0x51, 0x60, 0xd4, 0x3e, 0x50, 0x53, + 0x4f, 0x3e, 0x50, 0x16, 0x50, 0x02, 0x53, 0x4e, 0x7f, 0x60, 0xd4, + 0x3e, 0x4e, 0x53, 0x4f, 0x3e, 0x4e, 0x53, 0x4e, 0x7f, 0x09, 0x00, + 0x60, 0xd4, 0x3e, 0x4e, 0x53, 0x4f, 0x3e, 0x4e, + 0x7f, 0x55, 0x4e, 0x06, 0x55, 0x4f, 0x00, 0x55, 0x4b, 0x00, 0x55, 0x4a, + 0x00, 0x3c, 0x4f, 0x00, 0xb0, 0x06, 0x3c, 0x4e, 0x00, 0xa0, 0x1a, + 0x70, 0xfb, 0x6e, 0x4f, 0x6e, 0x4e, 0xd0, 0x0c, 0x62, 0xd0, 0x00, + 0x51, 0x50, 0x04, 0x4b, 0x51, 0x51, 0x0c, 0x4a, 0x65, 0x50, 0x6b, + 0x51, 0x8f, 0xde, 0x5f, 0x50, 0x4b, 0x5f, 0x51, 0x4a, 0x62, 0xd0, + 0x00, 0x5a, 0x4e, 0x06, 0x4e, 0x01, 0x51, 0x4e, + 0x04, 0x50, 0x0e, 0x51, 0x03, 0x7f, 0x0e, 0x51, 0x00, 0x51, 0x51, 0x60, + 0xd4, 0x3e, 0x50, 0x53, 0x51, 0x7f, 0x60, 0xd4, 0x3e, 0x50, 0x53, + 0x51, 0x3e, 0x50, 0x53, 0x50, 0x7f, 0x53, 0x4e, 0x51, 0x51, 0x09, + 0x00, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x4e, 0x52, 0x15, 0x3f, 0x4e, + 0x7f, 0x3e, 0x50, 0x53, 0x50, 0x51, 0x4e, 0x12, 0x50, 0x51, 0x4f, + 0x1a, 0x51, 0x7f, 0x53, 0x50, 0x52, 0xfb, 0x09, + 0x00, 0x60, 0xd4, 0x3e, 0x50, 0x7f, 0x0e, 0x4f, 0x00, 0x51, 0x4f, 0x60, + 0xd4, 0x3e, 0x4e, 0x53, 0x4f, 0x3e, 0x4e, 0x7f, 0x60, 0xd4, 0x3e, + 0x50, 0x54, 0x00, 0x3e, 0x50, 0x54, 0x01, 0x7f, 0x0e, 0x51, 0x00, + 0x51, 0x51, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x50, 0x52, 0x15, 0x3f, + 0x50, 0x7f, 0x52, 0x00, 0x53, 0x4e, 0x55, 0x4f, 0x00, 0x65, 0x4e, + 0x6b, 0x4f, 0x7f, 0x09, 0x00, 0x60, 0xd4, 0x3e, + 0x4c, 0x53, 0x4d, 0x3e, 0x4c, 0x53, 0x4c, 0x7f, 0x71, 0x10, 0x41, 0x04, + 0xfe, 0x41, 0x05, 0xfe, 0x41, 0x04, 0xfd, 0x41, 0x05, 0xfd, 0x70, + 0xcf, 0x43, 0x04, 0x01, 0x43, 0x04, 0x02, 0x71, 0x01, 0x7f, 0x0e, + 0x4f, 0x00, 0x51, 0x4f, 0x60, 0xd5, 0x51, 0x51, 0x3f, 0x4e, 0x7f, + 0x53, 0x4e, 0x55, 0x4f, 0x00, 0x65, 0x4e, 0x6b, 0x4f, 0x51, 0x4e, + 0x7f, 0x0e, 0x51, 0x00, 0x51, 0x51, 0x60, 0xd5, + 0x50, 0x00, 0x3f, 0x50, 0x7f, 0x3e, 0x50, 0x12, 0x4e, 0x54, 0x04, 0x51, + 0x51, 0x1a, 0x4f, 0x54, 0x03, 0x7f, 0x3e, 0x50, 0x12, 0x4e, 0x54, + 0x02, 0x51, 0x51, 0x1a, 0x4f, 0x54, 0x01, 0x7f, 0x71, 0x10, 0x41, + 0x00, 0xf7, 0x41, 0x01, 0xf7, 0x70, 0xcf, 0x7f, 0x00, 0x2a, 0x00, + 0x16, 0x00, 0x52, 0x00, 0x10, 0x00, 0x6a, 0x00, 0x22, 0x00, 0x90, + 0x00, 0x04, 0x00, 0x94, 0x04, 0x08, 0x08, 0x08, + 0x08, 0x00, 0x98, 0x00, 0x04, 0x00, 0x9c, 0x02, 0x5f, 0x28, 0x00, 0x9e, + 0x00, 0x08, 0x00, 0xa6, 0x07, 0x03, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x02, 0x00, 0xad, 0x00, 0x06, 0xff, 0x00, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/touchkey_fw_U1.h b/drivers/input/keyboard/cypressbln/touchkey_fw_U1.h new file mode 100644 index 0000000..a625b41 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/touchkey_fw_U1.h @@ -0,0 +1,747 @@ +unsigned char firmware_data[] = { + 0x40, 0x7d, 0x00, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7d, 0x05, 0x4f, 0x7e, 0x7e, 0x30, + 0x30, 0x30, 0x7d, 0x06, 0xbb, 0x7e, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, + 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, + 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, + 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x7e, + 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x40, 0x71, 0x10, 0x62, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe3, 0x38, 0x50, 0x80, 0x4e, 0x62, 0xe3, 0x38, 0x5d, + 0xd5, 0x08, 0x62, 0xd5, 0x00, 0x55, 0xfa, 0x01, 0x40, 0x4f, 0x5b, + 0x01, 0x03, 0x53, 0xf9, 0x55, 0xf8, 0x3a, 0x50, 0x06, 0x00, 0x40, + 0x40, 0x71, 0x10, 0x51, 0xfa, 0x60, 0xe8, 0x70, 0xef, 0x18, 0x60, + 0xd5, 0x55, 0xf8, 0x00, 0x55, 0xf9, 0x00, 0x71, 0x10, 0x62, 0xe0, + 0x02, 0x70, 0xef, 0x62, 0xe3, 0x38, 0x71, 0x10, 0x41, 0xe1, 0xfe, + 0x70, 0xef, 0x62, 0xe3, 0x38, 0x62, 0xd1, 0x03, 0x50, 0x00, 0x4e, + 0x62, 0xd3, 0x03, 0x62, 0xd0, 0x00, 0x62, 0xd5, 0x00, 0x62, 0xd4, + 0x00, 0x71, 0xc0, 0x7c, 0x03, 0x01, 0x62, 0xd0, 0x00, 0x50, 0x02, + 0x57, 0xff, 0x08, 0x28, 0x53, 0x40, 0x18, 0x75, 0x09, 0x00, 0x28, + 0x4b, 0x51, 0x40, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xe3, 0x00, + 0x08, 0x28, 0x60, 0xd5, 0x74, 0xa0, 0x4b, 0x18, 0x75, 0x09, 0x00, + 0x08, 0x28, 0x53, 0x40, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xa0, + 0x1c, 0x53, 0x3f, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x3f, 0x40, + 0x47, 0x40, 0xff, 0xb0, 0x06, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x18, + 0x7a, 0x3f, 0xbf, 0xeb, 0x8f, 0xc9, 0x18, 0x75, 0x09, 0x00, 0x08, + 0x28, 0x53, 0x3f, 0x50, 0x00, 0x3f, 0x40, 0x47, 0x40, 0xff, 0xb0, + 0x08, 0x5d, 0xd5, 0x74, 0x60, 0xd5, 0x50, 0x00, 0x7a, 0x3f, 0xbf, + 0xef, 0x18, 0x8f, 0xaa, 0x18, 0x71, 0x10, 0x43, 0xe3, 0x00, 0x70, + 0xef, 0x62, 0xe0, 0x00, 0x41, 0xfe, 0xe7, 0x43, 0xfe, 0x10, 0x71, + 0x10, 0x62, 0xe0, 0x02, 0x70, 0xef, 0x62, 0xe2, 0x00, 0x7c, 0x0a, + 0xce, 0x8f, 0xff, 0x7f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x00, 0xfd, 0x00, 0xcd, 0x00, + 0xce, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0xa1, 0x80, 0xa2, + 0x80, 0xa3, 0x0c, 0xa8, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x7c, 0x33, + 0x7a, 0x00, 0x7b, 0x00, 0x79, 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, 0xca, 0x20, 0xd6, 0x44, 0xcf, 0x00, 0xcb, 0x00, + 0xc8, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xd7, 0x00, 0xa9, 0x00, 0x2b, + 0x00, 0xb0, 0x00, 0xb3, 0x02, 0xb6, 0x00, 0xb2, 0x00, 0xb5, 0x00, + 0xb8, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0x33, 0x00, 0x34, + 0x00, 0x35, 0x00, 0xff, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0xdc, 0x00, + 0xe2, 0x00, 0xdd, 0x00, 0xd8, 0x02, 0xd9, 0x00, 0xda, 0x28, 0xdb, + 0x00, 0xdf, 0x00, 0x29, 0x00, 0x30, 0x00, 0xbd, 0x00, 0xff, 0x70, + 0xef, 0x62, 0x00, 0x08, 0x71, 0x10, 0x62, 0x00, 0x08, 0x62, 0x01, + 0x92, 0x70, 0xef, 0x62, 0x04, 0x03, 0x71, 0x10, 0x62, 0x04, 0x14, + 0x62, 0x05, 0xa8, 0x70, 0xef, 0x62, 0x08, 0x00, 0x71, 0x10, 0x62, + 0x08, 0x00, 0x62, 0x09, 0x28, 0x70, 0xef, 0x62, 0x0c, 0x00, 0x71, + 0x10, 0x62, 0x0c, 0x00, 0x62, 0x0d, 0x00, 0x70, 0xef, 0x62, 0x10, + 0x00, 0x71, 0x10, 0x62, 0x10, 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, + 0x62, 0x01, 0x00, 0x62, 0x05, 0x00, 0x62, 0x09, 0x00, 0x62, 0x0d, + 0x00, 0x62, 0x11, 0x00, 0x70, 0xef, 0x7f, 0x17, 0xc4, 0x55, 0x02, + 0x08, 0x55, 0x03, 0x03, 0x55, 0x04, 0x00, 0x7c, 0x03, 0x11, 0x7c, + 0x02, 0xaa, 0x7f, 0x10, 0x70, 0xef, 0x50, 0x00, 0x67, 0x50, 0x02, + 0x57, 0x00, 0x7c, 0x03, 0x2c, 0x50, 0x01, 0x67, 0x50, 0x02, 0x57, + 0x83, 0x7c, 0x03, 0x2c, 0x70, 0xef, 0x20, 0x7f, 0x38, 0x02, 0x10, + 0x08, 0x4f, 0x56, 0xfc, 0x00, 0xd0, 0x04, 0x56, 0xfc, 0x01, 0x18, + 0x20, 0x70, 0xef, 0x62, 0xe3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xff, + 0xa0, 0x1f, 0x4f, 0x48, 0xfc, 0x01, 0xa0, 0x03, 0x71, 0x10, 0x54, + 0xfd, 0x18, 0x20, 0x75, 0x09, 0x00, 0x10, 0x08, 0x28, 0x4f, 0x59, + 0xfd, 0x61, 0x00, 0x18, 0x20, 0x75, 0x09, 0x00, 0x8f, 0xd7, 0x38, + 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + 0x62, 0xd0, 0x00, 0x2e, 0x02, 0x08, 0x51, 0x02, 0x60, 0x00, 0x71, + 0x10, 0x41, 0x01, 0xf7, 0x43, 0x00, 0x08, 0x70, 0xef, 0x7f, 0x71, + 0x10, 0x43, 0x01, 0x08, 0x41, 0x00, 0xf7, 0x70, 0xef, 0x7f, 0x62, + 0xd0, 0x00, 0x53, 0x00, 0x71, 0x10, 0x5d, 0xe0, 0x08, 0x21, 0xf8, + 0x29, 0x00, 0x70, 0xfe, 0x60, 0xe0, 0x70, 0xef, 0x4b, 0x4b, 0x4b, + 0x4b, 0x51, 0x02, 0x21, 0xf7, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, + 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, + 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, + 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, + 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, + 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, + 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, + 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, + 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, 0xf7, 0x80, 0x05, + 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x6e, 0x00, 0xc0, 0x05, 0x21, + 0xf7, 0x80, 0x05, 0x29, 0x08, 0x80, 0x01, 0x60, 0x00, 0x47, 0x00, + 0x00, 0x49, 0x01, 0x00, 0x29, 0x08, 0x60, 0x00, 0x57, 0x01, 0x79, + 0xbf, 0xfe, 0x18, 0x71, 0x10, 0x60, 0xe0, 0x70, 0xef, 0x71, 0x01, + 0x7f, 0x08, 0x67, 0x67, 0x67, 0x67, 0x21, 0x0f, 0xff, 0x20, 0x9f, + 0x4e, 0x18, 0x21, 0x0f, 0xff, 0x19, 0x9f, 0x47, 0x7f, 0x08, 0x5b, + 0x9f, 0xe9, 0x18, 0x9f, 0xe6, 0x7f, 0x08, 0x10, 0x28, 0xa0, 0x0b, + 0x9f, 0x37, 0x20, 0x18, 0x75, 0xdf, 0xf5, 0x74, 0x8f, 0xf2, 0x38, + 0xfe, 0x7f, 0x70, 0xbf, 0x60, 0xd3, 0x52, 0x00, 0xa0, 0x08, 0x10, + 0x9f, 0x21, 0x20, 0x75, 0x8f, 0xf6, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x50, 0x0d, 0x9f, 0x14, 0x50, 0x0a, 0x9f, 0x10, 0x7f, 0x70, 0xbf, + 0x62, 0xd3, 0x03, 0x4f, 0x52, 0xfb, 0xa0, 0x15, 0x7b, 0xfb, 0x52, + 0xfc, 0x59, 0xfd, 0x60, 0xd3, 0x52, 0x00, 0x9e, 0xf9, 0x4f, 0x62, + 0xd3, 0x03, 0x77, 0xfd, 0x8f, 0xe9, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x4f, 0x3d, 0xfa, 0x00, 0xb0, 0x06, 0x3d, 0xfb, 0x00, 0xa0, 0x18, + 0x10, 0x52, 0xfc, 0x59, 0xfd, 0x28, 0x9e, 0xd9, 0x20, 0x07, 0xfd, + 0x01, 0x0f, 0xfc, 0x00, 0x17, 0xfb, 0x01, 0x1f, 0xfa, 0x00, 0x8f, + 0xe0, 0x7f, 0x50, 0x01, 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, + 0x29, 0x00, 0xa0, 0x06, 0x26, 0x03, 0xfb, 0x80, 0x04, 0x2e, 0x03, + 0x04, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, + 0xd0, 0x00, 0x36, 0x03, 0x04, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0x03, 0x73, 0x21, 0x04, + 0xa0, 0x03, 0x50, 0x01, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, 0x01, + 0x80, 0x03, 0x50, 0x00, 0x62, 0xd0, 0x00, 0x29, 0x00, 0xa0, 0x06, + 0x26, 0x03, 0xef, 0x80, 0x04, 0x2e, 0x03, 0x10, 0x51, 0x03, 0x60, + 0x04, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x36, 0x03, + 0x10, 0x51, 0x03, 0x60, 0x04, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, + 0xd0, 0x00, 0x51, 0x03, 0x73, 0x21, 0x10, 0xa0, 0x03, 0x50, 0x01, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x10, 0x70, 0x3f, 0x71, 0x80, + 0x5d, 0xd3, 0x08, 0x5d, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x51, 0x08, + 0x60, 0xd3, 0x2e, 0x05, 0x80, 0x49, 0xd7, 0x08, 0xa0, 0x09, 0x26, + 0x05, 0xf0, 0x2e, 0x05, 0x00, 0x80, 0x08, 0x49, 0xd7, 0x20, 0xa0, + 0x03, 0x80, 0xac, 0x51, 0x05, 0x21, 0x0e, 0xe0, 0x01, 0x80, 0x11, + 0x80, 0x6d, 0x80, 0x7f, 0x80, 0x4d, 0x80, 0x9c, 0x80, 0x9a, 0x80, + 0x98, 0x80, 0x96, 0x80, 0x9d, 0x5d, 0xd8, 0x21, 0xfe, 0x39, 0x40, + 0xa0, 0x06, 0x62, 0xd7, 0x00, 0x80, 0x90, 0x49, 0xd8, 0x01, 0xb0, + 0x15, 0x55, 0x0c, 0x02, 0x26, 0x07, 0x00, 0x26, 0x06, 0x00, 0x26, + 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x62, 0xd7, 0x10, 0x80, 0x77, 0x55, + 0x0c, 0x01, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x06, 0x5f, 0x07, 0x06, + 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, 0x60, 0xd8, 0x76, 0x07, + 0x62, 0xd7, 0x14, 0x80, 0x5b, 0x51, 0x0a, 0x78, 0x3a, 0x07, 0xc0, + 0x0f, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x52, 0x00, 0x60, 0xd8, 0x76, + 0x07, 0x2e, 0x05, 0x20, 0x60, 0xd8, 0x62, 0xd7, 0x04, 0x80, 0x3f, + 0x5d, 0xd8, 0x3a, 0x0a, 0xd0, 0x2b, 0xa0, 0x29, 0x53, 0x07, 0x53, + 0x06, 0x26, 0x05, 0xf0, 0x2e, 0x05, 0x04, 0x80, 0x18, 0x51, 0x0b, + 0x78, 0x3a, 0x07, 0xc0, 0x16, 0x51, 0x09, 0x02, 0x07, 0x5c, 0x5d, + 0xd8, 0x54, 0x00, 0x2e, 0x05, 0x10, 0x76, 0x07, 0x80, 0x01, 0x62, + 0xd7, 0x10, 0x80, 0x0f, 0x62, 0xd7, 0x00, 0x80, 0x0a, 0x26, 0x05, + 0xf0, 0x2e, 0x05, 0x00, 0x55, 0x0c, 0x00, 0x18, 0x60, 0xd0, 0x18, + 0x60, 0xd3, 0x20, 0x18, 0x7e, 0x62, 0xd0, 0x00, 0x71, 0x10, 0x41, + 0x04, 0xfc, 0x43, 0x05, 0x03, 0x70, 0xef, 0x26, 0x03, 0xfc, 0x51, + 0x03, 0x60, 0x04, 0x55, 0x0c, 0x00, 0x90, 0x2d, 0x90, 0x32, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x50, 0x00, 0x53, 0x06, 0x71, 0x10, 0x43, + 0x04, 0x03, 0x43, 0x05, 0x03, 0x70, 0xef, 0x2e, 0x03, 0x03, 0x51, + 0x03, 0x60, 0x04, 0x7f, 0x62, 0xd0, 0x00, 0x51, 0x05, 0x21, 0xb0, + 0x26, 0x05, 0x4f, 0x7f, 0x50, 0x20, 0x7f, 0x80, 0x04, 0x41, 0xe0, + 0x7f, 0x43, 0xe0, 0x80, 0x7f, 0x43, 0xd6, 0x31, 0x7f, 0x41, 0xe0, + 0x7f, 0x41, 0xd6, 0xfe, 0x7f, 0x41, 0xe0, 0x7f, 0x7f, 0x41, 0xd6, + 0xfe, 0x7f, 0x62, 0xd0, 0x00, 0x4f, 0x52, 0xfd, 0x53, 0x0a, 0x52, + 0xfc, 0x53, 0x0b, 0x52, 0xfb, 0x53, 0x09, 0x52, 0xfa, 0x53, 0x08, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x02, 0x20, 0x02, 0x08, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x06, 0xb9, 0x08, 0x5d, 0xa4, 0x04, 0x1b, + 0x5d, 0xa5, 0x0c, 0x1a, 0x55, 0x1c, 0x01, 0x18, 0x7e, 0x70, 0xbf, + 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x53, 0x1e, 0x64, 0x5c, 0x62, 0xd3, + 0x00, 0x52, 0x5b, 0x62, 0xd3, 0x00, 0x13, 0x4b, 0x62, 0xd3, 0x00, + 0x54, 0x4f, 0x62, 0xd3, 0x00, 0x52, 0x5a, 0x62, 0xd3, 0x00, 0x1b, + 0x4a, 0x62, 0xd3, 0x00, 0x54, 0x4e, 0x48, 0x4e, 0x80, 0xb0, 0x33, + 0x3d, 0x4e, 0x00, 0xb0, 0x7b, 0x51, 0x0d, 0x3b, 0x4f, 0xc0, 0x75, + 0x52, 0x4f, 0x58, 0x1e, 0x01, 0x00, 0x6d, 0x62, 0xd3, 0x00, 0x05, + 0x41, 0xc0, 0x09, 0x51, 0x0f, 0x3b, 0x41, 0xd0, 0x12, 0xa0, 0x10, + 0x56, 0x41, 0x00, 0x5b, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x07, 0x4b, + 0x01, 0x0f, 0x4a, 0x00, 0x80, 0x41, 0x3d, 0x4e, 0xff, 0xb0, 0x09, + 0x50, 0xff, 0x12, 0x0e, 0x3b, 0x4f, 0xc0, 0x20, 0x62, 0xd3, 0x00, + 0x56, 0x4f, 0x00, 0x56, 0x4e, 0x00, 0x5b, 0x67, 0x5c, 0x62, 0xd3, + 0x00, 0x52, 0x48, 0x78, 0xd0, 0x03, 0x50, 0x00, 0x54, 0x48, 0x08, + 0x5b, 0x64, 0x5c, 0x18, 0xb0, 0x2c, 0x62, 0xd3, 0x00, 0x52, 0x5b, + 0x62, 0xd3, 0x00, 0x54, 0x4b, 0x62, 0xd3, 0x00, 0x52, 0x5a, 0x62, + 0xd3, 0x00, 0x54, 0x4a, 0x51, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, + 0x56, 0x4f, 0x00, 0x56, 0x4e, 0x00, 0x5b, 0x67, 0x5c, 0x62, 0xd3, + 0x00, 0x51, 0x12, 0x54, 0x48, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x70, + 0xbf, 0x62, 0xd0, 0x00, 0x70, 0xbf, 0x08, 0x5c, 0x62, 0xd3, 0x00, + 0x52, 0x43, 0x53, 0x19, 0x55, 0x18, 0x00, 0x18, 0x08, 0x90, 0x7e, + 0x62, 0xd3, 0x00, 0x23, 0x45, 0xb0, 0x2c, 0x51, 0x10, 0x04, 0x19, + 0x0e, 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x4f, + 0x12, 0x19, 0x52, 0x4e, 0x1a, 0x18, 0xc0, 0x39, 0x5b, 0x67, 0x5c, + 0x62, 0xd3, 0x00, 0x52, 0x46, 0x78, 0x54, 0x46, 0x08, 0x5b, 0x64, + 0x5c, 0x18, 0xb0, 0x3e, 0x80, 0x18, 0x51, 0x10, 0x14, 0x19, 0x1e, + 0x18, 0x00, 0x18, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x4f, 0x12, + 0x19, 0x52, 0x4e, 0x1a, 0x18, 0xc0, 0x0e, 0x5b, 0x67, 0x90, 0x31, + 0x62, 0xd3, 0x00, 0x2d, 0x45, 0x50, 0x01, 0x80, 0x24, 0x5b, 0x67, + 0x08, 0x90, 0x23, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x45, 0x62, 0xd3, + 0x00, 0x20, 0x51, 0x11, 0x54, 0x46, 0x50, 0x00, 0x80, 0x0d, 0x5b, + 0x67, 0x90, 0x0d, 0x73, 0x62, 0xd3, 0x00, 0x25, 0x45, 0x50, 0x00, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x08, 0x67, 0x67, 0x67, 0x5c, 0x18, + 0x21, 0x07, 0xf0, 0x01, 0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x70, 0xbf, 0x70, 0xbf, 0x62, 0xd3, 0x00, 0x50, 0x02, + 0x78, 0x08, 0x5c, 0x56, 0x43, 0x28, 0x18, 0x78, 0xdf, 0xf8, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x70, 0xbf, 0x50, 0x02, 0x78, 0x08, 0x91, + 0xf9, 0x70, 0xbf, 0x18, 0x08, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, + 0x5b, 0x62, 0xd3, 0x00, 0x54, 0x4b, 0x62, 0xd3, 0x00, 0x52, 0x5a, + 0x62, 0xd3, 0x00, 0x54, 0x4a, 0x18, 0x78, 0xdf, 0xe0, 0x70, 0x3f, + 0x71, 0xc0, 0x7f, 0x70, 0xbf, 0x08, 0x91, 0xd1, 0x70, 0xbf, 0x18, + 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x52, 0x5b, 0x62, 0xd3, 0x00, 0x54, + 0x4b, 0x62, 0xd3, 0x00, 0x52, 0x5a, 0x62, 0xd3, 0x00, 0x54, 0x4a, + 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x62, 0xd0, 0x00, 0x55, 0x14, 0x00, + 0x50, 0x02, 0x78, 0x08, 0x9e, 0xe6, 0x39, 0x01, 0xb0, 0x04, 0x55, + 0x14, 0x01, 0x18, 0x78, 0xdf, 0xf3, 0x51, 0x14, 0x7f, 0x50, 0x02, + 0x78, 0x08, 0x9e, 0x16, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x98, 0x90, + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0xd8, 0xd9, 0xda, 0xdb, + 0xdf, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x70, + 0xbf, 0x62, 0xd0, 0x00, 0x62, 0xd3, 0x00, 0x57, 0x00, 0x56, 0x45, + 0x00, 0x79, 0xdf, 0xfb, 0x62, 0xd3, 0x00, 0x57, 0x01, 0x50, 0x03, + 0x54, 0x46, 0x79, 0xdf, 0xfc, 0x62, 0xd3, 0x00, 0x50, 0x32, 0x57, + 0x01, 0x54, 0x48, 0x79, 0xdf, 0xfc, 0x70, 0x3f, 0x71, 0xc0, 0x55, + 0x0d, 0x14, 0x55, 0x0e, 0x05, 0x55, 0x0f, 0x19, 0x55, 0x10, 0x01, + 0x55, 0x11, 0x03, 0x55, 0x12, 0x32, 0x55, 0x22, 0x04, 0x55, 0x1f, + 0x14, 0x43, 0x61, 0x0d, 0x57, 0x00, 0x50, 0x02, 0x90, 0xd2, 0x50, + 0x04, 0xff, 0x98, 0x29, 0x00, 0x60, 0xa9, 0x62, 0xa0, 0x08, 0x43, + 0xa2, 0x04, 0x62, 0xa3, 0x70, 0x43, 0x7a, 0x01, 0x43, 0xaa, 0x02, + 0x43, 0xdf, 0x01, 0x50, 0x01, 0x57, 0x0d, 0x90, 0x1b, 0x90, 0x56, + 0x7f, 0x62, 0xd0, 0x00, 0x39, 0x09, 0xc0, 0x03, 0x50, 0x08, 0x53, + 0x22, 0xff, 0x6c, 0x29, 0x00, 0x60, 0xa9, 0x51, 0x21, 0x58, 0x20, + 0x90, 0x01, 0x7f, 0x62, 0xd0, 0x00, 0x21, 0x03, 0x53, 0x21, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x29, 0x80, 0x60, 0xa1, 0x5b, 0x78, 0x21, + 0x0f, 0x29, 0x08, 0x74, 0x53, 0x20, 0x12, 0x22, 0x02, 0x21, 0x5c, + 0x50, 0x00, 0x53, 0x1d, 0x53, 0x23, 0x29, 0x01, 0x79, 0xa0, 0x08, + 0x64, 0x6b, 0x1d, 0x6b, 0x23, 0x8f, 0xf5, 0x60, 0xb5, 0x51, 0x1d, + 0x60, 0xb4, 0x7f, 0x62, 0xd0, 0x00, 0x53, 0x1f, 0x7f, 0x50, 0x02, + 0x78, 0x08, 0x90, 0x4b, 0x90, 0x7d, 0x18, 0x78, 0xdf, 0xf8, 0x7f, + 0x41, 0xdf, 0xfe, 0x71, 0x10, 0x41, 0xd8, 0xfd, 0x70, 0xef, 0x41, + 0x61, 0xf3, 0x41, 0xa2, 0xfb, 0x41, 0xa0, 0xf7, 0x62, 0xa3, 0x00, + 0x62, 0xa9, 0x00, 0x41, 0xaa, 0xfd, 0x7f, 0x62, 0xd0, 0x00, 0x62, + 0xa3, 0x50, 0x51, 0x22, 0xfe, 0xf0, 0x29, 0x00, 0x60, 0xa9, 0x43, + 0x61, 0x0d, 0x57, 0x00, 0x50, 0x02, 0x90, 0x19, 0x43, 0xa3, 0x20, + 0x43, 0xa2, 0x04, 0x43, 0xa0, 0x08, 0x43, 0xaa, 0x02, 0x43, 0xdf, + 0x01, 0x7f, 0x64, 0x5c, 0xfc, 0xc6, 0x4b, 0x74, 0xfc, 0xc2, 0x7f, + 0x62, 0xd0, 0x00, 0x53, 0x1d, 0x10, 0x5b, 0x64, 0x64, 0x5c, 0x71, + 0x10, 0x5e, 0x01, 0x2a, 0x1d, 0x61, 0x01, 0x36, 0x1d, 0xff, 0x5e, + 0x00, 0x22, 0x1d, 0x61, 0x00, 0x36, 0x1d, 0xff, 0x18, 0xfe, 0xb2, + 0x5c, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, 0x7f, 0x62, + 0xd0, 0x00, 0x10, 0x73, 0x53, 0x1d, 0x71, 0x10, 0x5b, 0xfe, 0x9c, + 0x5c, 0x5e, 0x00, 0x22, 0x1d, 0x61, 0x00, 0x70, 0xef, 0x18, 0x64, + 0x64, 0x5c, 0x71, 0x10, 0x5e, 0x01, 0x22, 0x1d, 0x61, 0x01, 0x36, + 0x1d, 0xff, 0x5e, 0x00, 0x2a, 0x1d, 0x61, 0x00, 0x70, 0xef, 0x7f, + 0x70, 0xbf, 0x62, 0xd0, 0x00, 0x53, 0x1e, 0x50, 0x00, 0x53, 0x1a, + 0x53, 0x1b, 0x43, 0xa0, 0x01, 0x51, 0x1f, 0x60, 0xfd, 0x41, 0xa3, + 0xdf, 0x51, 0x1e, 0x9f, 0x84, 0x9f, 0x8b, 0x58, 0x23, 0x55, 0x1c, + 0x00, 0x62, 0xa5, 0x00, 0x62, 0xa4, 0x00, 0x43, 0xb3, 0x01, 0x51, + 0x1c, 0xaf, 0xfd, 0x79, 0xdf, 0xee, 0x51, 0x1e, 0x9f, 0x69, 0x9f, + 0x9b, 0x43, 0xa3, 0x20, 0x41, 0xa0, 0xfe, 0x62, 0xfd, 0x00, 0x50, + 0xff, 0x4c, 0x1b, 0x14, 0x1b, 0x51, 0x20, 0x11, 0x08, 0xfe, 0x33, + 0x4c, 0x1a, 0x1c, 0x1a, 0xd0, 0x07, 0x55, 0x1a, 0x00, 0x55, 0x1b, + 0x00, 0x51, 0x1e, 0x64, 0x5c, 0x62, 0xd3, 0x00, 0x51, 0x1b, 0x54, + 0x5b, 0x51, 0x1a, 0x54, 0x5a, 0x70, 0x3f, 0x71, 0xc0, 0x7f, 0x50, + 0x02, 0x78, 0x08, 0x9f, 0x8d, 0x18, 0x78, 0xdf, 0xfa, 0x7f, 0x70, + 0xbf, 0x62, 0xd3, 0x00, 0x64, 0x5c, 0x52, 0x5b, 0x59, 0x5a, 0x70, + 0x3f, 0x71, 0xc0, 0x7f, 0x10, 0x7c, 0x05, 0x15, 0x7c, 0x04, 0xd7, + 0x20, 0x7c, 0x13, 0x53, 0x43, 0x00, 0x08, 0x62, 0xd0, 0x00, 0x55, + 0x24, 0x08, 0x55, 0x25, 0x04, 0x55, 0x26, 0x0c, 0x55, 0x28, 0x55, + 0x55, 0x29, 0x02, 0x10, 0x50, 0x00, 0x08, 0x50, 0x24, 0x08, 0x50, + 0x06, 0x08, 0x50, 0x12, 0x08, 0x7c, 0x06, 0x96, 0x38, 0xfc, 0x7c, + 0x06, 0x36, 0x7c, 0x06, 0x7c, 0x20, 0x71, 0x10, 0x41, 0x04, 0xfc, + 0x41, 0x05, 0xfc, 0x71, 0x01, 0x10, 0x70, 0xcf, 0x7c, 0x08, 0xcf, + 0x7c, 0x08, 0x2d, 0x20, 0x90, 0xc4, 0x80, 0xbf, 0x7c, 0x14, 0xa7, + 0x62, 0xe3, 0x38, 0x92, 0xd0, 0x62, 0xd0, 0x00, 0x3c, 0x6f, 0x01, + 0xb0, 0x09, 0x50, 0x00, 0x08, 0x7c, 0x16, 0x49, 0x38, 0xff, 0x62, + 0xe3, 0x38, 0x10, 0x7c, 0x08, 0x93, 0x20, 0x39, 0x00, 0xa0, 0x6e, + 0x62, 0xd0, 0x00, 0x51, 0x63, 0x11, 0xd0, 0x51, 0x62, 0x19, 0x07, + 0xd0, 0x12, 0x7c, 0x13, 0x65, 0x39, 0xe1, 0xa0, 0x16, 0x62, 0xd0, + 0x00, 0x76, 0x63, 0x0e, 0x62, 0x00, 0x80, 0x0c, 0x62, 0xd0, 0x00, + 0x55, 0x63, 0x00, 0x55, 0x62, 0x00, 0x90, 0x75, 0x62, 0xd0, 0x00, + 0x3c, 0x72, 0xf0, 0xd0, 0x06, 0x62, 0xd0, 0x00, 0x76, 0x72, 0x62, + 0xd0, 0x00, 0x51, 0x72, 0x62, 0xd0, 0x00, 0x3a, 0x29, 0xb0, 0x56, + 0x97, 0xde, 0x62, 0xd0, 0x00, 0x53, 0x73, 0x3c, 0x73, 0xe1, 0xa0, + 0x18, 0x62, 0xd0, 0x00, 0x55, 0x75, 0x00, 0x7c, 0x13, 0xa0, 0x62, + 0xd0, 0x00, 0x55, 0x71, 0x01, 0x62, 0xd0, 0x00, 0x55, 0x6e, 0x03, + 0x80, 0x33, 0x62, 0xd0, 0x00, 0x55, 0x72, 0x00, 0x80, 0x2b, 0x62, + 0xd0, 0x00, 0x55, 0x63, 0x00, 0x55, 0x62, 0x00, 0x62, 0xd0, 0x00, + 0x3c, 0x71, 0x01, 0xb0, 0x1a, 0x62, 0xd0, 0x00, 0x7a, 0x6e, 0x3c, + 0x6e, 0x00, 0xb0, 0x10, 0x7c, 0x14, 0x28, 0x62, 0xd0, 0x00, 0x55, + 0x71, 0x00, 0x62, 0xd0, 0x00, 0x55, 0x72, 0x00, 0x7c, 0x15, 0x24, + 0x8f, 0x41, 0x8f, 0xff, 0x10, 0x4f, 0x38, 0x16, 0x10, 0x62, 0xd0, + 0x00, 0x51, 0x68, 0x7c, 0x09, 0x8e, 0x20, 0x10, 0x50, 0x00, 0x7c, + 0x0a, 0x46, 0x20, 0x56, 0x0d, 0x00, 0x80, 0xae, 0x56, 0x00, 0x00, + 0x80, 0xa2, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, + 0x00, 0x06, 0x3f, 0x68, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, + 0x3e, 0x3f, 0x10, 0x7c, 0x09, 0x8e, 0x20, 0x10, 0x52, 0x00, 0x7c, + 0x0a, 0x46, 0x20, 0x10, 0x7c, 0x06, 0x6c, 0x62, 0xd0, 0x00, 0x20, + 0x39, 0x00, 0xbf, 0xee, 0x55, 0x40, 0x03, 0x5a, 0x3f, 0x06, 0x3f, + 0x01, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, 0x08, 0x51, 0x3d, 0x08, + 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x17, 0x94, 0x38, 0xfc, + 0x51, 0x38, 0x53, 0x3d, 0x51, 0x37, 0x53, 0x3e, 0x51, 0x3d, 0x02, + 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x53, 0x40, 0x52, 0x0d, + 0x53, 0x3d, 0x55, 0x3e, 0x00, 0x65, 0x3d, 0x6b, 0x3e, 0x51, 0x3d, + 0x02, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x53, 0x40, 0x52, + 0x00, 0x53, 0x3d, 0x55, 0x3e, 0x00, 0x65, 0x3d, 0x6b, 0x3e, 0x06, + 0x3d, 0x5a, 0x0e, 0x3e, 0x00, 0x51, 0x3e, 0x60, 0xd4, 0x3e, 0x3d, + 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x51, 0x40, 0x60, 0xd5, 0x51, + 0x3e, 0x3f, 0x3f, 0x51, 0x3d, 0x3f, 0x3f, 0x77, 0x00, 0x3d, 0x00, + 0x02, 0xcf, 0x5b, 0x77, 0x0d, 0x3d, 0x0d, 0x03, 0xcf, 0x4f, 0x56, + 0x00, 0x00, 0x81, 0x3d, 0x62, 0xd0, 0x00, 0x55, 0x40, 0x03, 0x5a, + 0x3f, 0x06, 0x3f, 0x01, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, 0x08, + 0x51, 0x3d, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x17, + 0x94, 0x38, 0xfc, 0x51, 0x38, 0x53, 0x3d, 0x51, 0x37, 0x53, 0x3e, + 0x51, 0x3d, 0x02, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x60, + 0xd4, 0x3e, 0x3f, 0x54, 0x0e, 0x3e, 0x3f, 0x54, 0x0f, 0x5a, 0x3f, + 0x06, 0x3f, 0x03, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, 0x08, 0x51, + 0x3d, 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x17, 0x94, + 0x38, 0xfc, 0x51, 0x38, 0x53, 0x3d, 0x51, 0x37, 0x53, 0x3e, 0x51, + 0x3d, 0x02, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x60, 0xd4, + 0x3e, 0x3f, 0x54, 0x10, 0x3e, 0x3f, 0x54, 0x11, 0x5a, 0x3f, 0x06, + 0x3f, 0x05, 0x52, 0x00, 0x53, 0x3d, 0x50, 0x00, 0x08, 0x51, 0x3d, + 0x08, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7c, 0x17, 0x94, 0x38, + 0xfc, 0x51, 0x38, 0x53, 0x3d, 0x51, 0x37, 0x53, 0x3e, 0x51, 0x3d, + 0x02, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x0a, 0x40, 0x60, 0xd4, 0x3e, + 0x3f, 0x54, 0x12, 0x3e, 0x3f, 0x54, 0x13, 0x50, 0x03, 0x08, 0x5a, + 0x3f, 0x06, 0x3f, 0x0e, 0x08, 0x51, 0x3f, 0x08, 0x7c, 0x15, 0x6b, + 0x38, 0xfd, 0x62, 0xd0, 0x00, 0x51, 0x3f, 0x54, 0x15, 0x51, 0x40, + 0x54, 0x14, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, + 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x53, 0x3d, 0x51, 0x40, 0x09, + 0x00, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x3d, 0x52, 0x15, 0x3f, 0x3d, + 0x06, 0x3f, 0x4a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd5, 0x52, + 0x14, 0x3f, 0x3f, 0x52, 0x15, 0x3f, 0x3f, 0x52, 0x00, 0x53, 0x3f, + 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x52, + 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd5, 0x52, 0x14, 0x3f, + 0x3d, 0x52, 0x15, 0x3f, 0x3d, 0x51, 0x3f, 0x01, 0x56, 0x53, 0x3d, + 0x51, 0x40, 0x09, 0x00, 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x3d, 0x52, + 0x15, 0x3f, 0x3d, 0x06, 0x3f, 0x5e, 0x0e, 0x40, 0x00, 0x51, 0x40, + 0x60, 0xd5, 0x52, 0x14, 0x3f, 0x3f, 0x52, 0x15, 0x3f, 0x3f, 0x77, + 0x00, 0x3d, 0x00, 0x02, 0xce, 0xc0, 0x38, 0xea, 0x20, 0x7f, 0x10, + 0x4f, 0x38, 0x06, 0x62, 0xd0, 0x00, 0x55, 0x70, 0x00, 0x10, 0x62, + 0xd0, 0x00, 0x51, 0x68, 0x7c, 0x09, 0x8e, 0x20, 0x10, 0x50, 0x00, + 0x7c, 0x0a, 0x46, 0x20, 0x56, 0x00, 0x00, 0x80, 0x31, 0x62, 0xd0, + 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x06, 0x3f, 0x68, + 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x10, 0x7c, + 0x09, 0x8e, 0x20, 0x10, 0x52, 0x00, 0x7c, 0x0a, 0x46, 0x20, 0x10, + 0x7c, 0x06, 0x6c, 0x62, 0xd0, 0x00, 0x20, 0x39, 0x00, 0xbf, 0xee, + 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcf, 0xcc, 0x56, 0x00, 0x00, 0x83, + 0xa2, 0x62, 0xd0, 0x00, 0x3c, 0x6f, 0x02, 0xa1, 0x8c, 0x62, 0xd0, + 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, + 0x40, 0x51, 0x3f, 0x01, 0x4a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, + 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, + 0x3f, 0x5a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, + 0x53, 0x40, 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x3d, 0x12, 0x3f, 0x51, + 0x3e, 0x1a, 0x40, 0xd0, 0x3f, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, + 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, + 0x4a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, + 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x5a, 0x0e, 0x40, + 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, + 0x12, 0x3d, 0x54, 0x03, 0x51, 0x40, 0x1a, 0x3e, 0x54, 0x02, 0x80, + 0x3d, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, + 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x53, 0x3d, 0x51, + 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, + 0x53, 0x3d, 0x06, 0x3f, 0x4a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, + 0xd4, 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, 0x12, 0x3d, 0x54, 0x03, + 0x51, 0x40, 0x1a, 0x3e, 0x54, 0x02, 0x50, 0x90, 0x13, 0x03, 0x50, + 0x01, 0x1b, 0x02, 0xc0, 0xc3, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, + 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, + 0x5e, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, + 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x5a, 0x0e, 0x40, + 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, + 0x53, 0x3f, 0x51, 0x3d, 0x12, 0x3f, 0x51, 0x3e, 0x1a, 0x40, 0xd0, + 0x3f, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, + 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x5e, 0x53, 0x3d, 0x51, + 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, + 0x53, 0x3d, 0x06, 0x3f, 0x5a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, + 0xd4, 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, 0x12, 0x3d, 0x54, 0x05, + 0x51, 0x40, 0x1a, 0x3e, 0x54, 0x04, 0x80, 0x3d, 0x62, 0xd0, 0x00, + 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, + 0x51, 0x3f, 0x01, 0x5a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, + 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, + 0x5e, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, + 0x40, 0x3e, 0x3f, 0x12, 0x3d, 0x54, 0x05, 0x51, 0x40, 0x1a, 0x3e, + 0x54, 0x04, 0x50, 0x90, 0x13, 0x05, 0x50, 0x01, 0x1b, 0x04, 0xd0, + 0x08, 0x62, 0xd0, 0x00, 0x76, 0x70, 0x82, 0x0d, 0x62, 0xd0, 0x00, + 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, + 0x51, 0x3f, 0x01, 0x56, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, + 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, + 0x52, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd5, 0x51, 0x3e, 0x3f, + 0x3f, 0x51, 0x3d, 0x3f, 0x3f, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, + 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x5e, 0x53, 0x3d, + 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, + 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x56, 0x0e, 0x40, 0x00, 0x51, 0x40, + 0x60, 0xd5, 0x51, 0x3e, 0x3f, 0x3f, 0x51, 0x3d, 0x3f, 0x3f, 0x52, + 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, + 0x3f, 0x01, 0x5a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, + 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x5e, + 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd5, 0x51, 0x3e, 0x3f, 0x3f, + 0x51, 0x3d, 0x3f, 0x3f, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, + 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x53, 0x3d, 0x51, + 0x40, 0x09, 0x00, 0x53, 0x3e, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3c, + 0x3e, 0x3d, 0x16, 0x3d, 0x02, 0x53, 0x3b, 0x08, 0x51, 0x3c, 0x53, + 0x3a, 0x18, 0x53, 0x39, 0x65, 0x39, 0x6b, 0x3a, 0x06, 0x3f, 0x56, + 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, 0x40, + 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x39, 0x04, 0x3f, 0x51, 0x3a, 0x0c, + 0x40, 0x51, 0x3b, 0x04, 0x3f, 0x51, 0x3c, 0x0c, 0x40, 0x70, 0xfb, + 0x6e, 0x40, 0x6e, 0x3f, 0x70, 0xfb, 0x6e, 0x40, 0x6e, 0x3f, 0x51, + 0x3e, 0x60, 0xd5, 0x51, 0x40, 0x3f, 0x3d, 0x51, 0x3f, 0x3f, 0x3d, + 0x10, 0x52, 0x00, 0x7c, 0x06, 0xc9, 0x20, 0x62, 0xd0, 0x00, 0x52, + 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, + 0x3f, 0x01, 0x5a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, + 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x4a, + 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, 0x40, + 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x3d, 0x12, 0x3f, 0x51, 0x3e, 0x1a, + 0x40, 0xd0, 0x28, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, + 0x40, 0x00, 0x06, 0x3f, 0x6c, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, + 0xd4, 0x3e, 0x3f, 0x7a, 0x3f, 0x53, 0x3e, 0x06, 0x3e, 0x01, 0x51, + 0x40, 0x60, 0xd5, 0x51, 0x3e, 0x3f, 0x3f, 0x80, 0x19, 0x62, 0xd0, + 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x06, 0x3f, 0x6c, + 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd5, 0x50, 0x00, 0x3f, 0x3f, + 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x06, + 0x3f, 0x6c, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, + 0x53, 0x40, 0x50, 0x05, 0x3a, 0x40, 0xd0, 0x6b, 0x62, 0xd0, 0x00, + 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, + 0x51, 0x3f, 0x01, 0x4a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x53, + 0x3e, 0x06, 0x3f, 0x5a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, + 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x3e, 0x60, + 0xd4, 0x3e, 0x3d, 0x53, 0x3c, 0x3e, 0x3d, 0x16, 0x3d, 0x02, 0x02, + 0x3f, 0x53, 0x3f, 0x51, 0x3c, 0x0a, 0x40, 0x53, 0x40, 0x70, 0xfb, + 0x6e, 0x40, 0x6e, 0x3f, 0x51, 0x3e, 0x60, 0xd5, 0x51, 0x40, 0x3f, + 0x3d, 0x51, 0x3f, 0x3f, 0x3d, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, + 0x00, 0x06, 0x3f, 0x6c, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd5, + 0x50, 0x00, 0x3f, 0x3f, 0x77, 0x00, 0x3d, 0x00, 0x02, 0xcc, 0x5b, + 0x62, 0xd0, 0x00, 0x3c, 0x6f, 0x02, 0xb1, 0x08, 0x56, 0x00, 0x00, + 0x80, 0xfe, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, + 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x5a, 0x53, 0x3d, + 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, + 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x2e, 0x0e, 0x40, 0x00, 0x51, 0x40, + 0x60, 0xd5, 0x51, 0x3e, 0x3f, 0x3f, 0x51, 0x3d, 0x3f, 0x3f, 0x52, + 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, + 0x3f, 0x01, 0x4a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, + 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x51, 0x3f, 0x01, + 0x5a, 0x53, 0x3b, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3b, + 0x53, 0x3c, 0x3e, 0x3b, 0x53, 0x3b, 0x51, 0x3d, 0x12, 0x3b, 0x51, + 0x3e, 0x1a, 0x3c, 0xd0, 0x3f, 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, + 0x3d, 0x55, 0x3e, 0x00, 0x65, 0x3d, 0x6b, 0x3e, 0x51, 0x3d, 0x01, + 0x4a, 0x53, 0x3b, 0x51, 0x3e, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3b, + 0x53, 0x3c, 0x3e, 0x3b, 0x53, 0x3b, 0x06, 0x3d, 0x5a, 0x0e, 0x3e, + 0x00, 0x51, 0x3e, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, + 0x12, 0x3b, 0x54, 0x03, 0x51, 0x3e, 0x1a, 0x3c, 0x54, 0x02, 0x80, + 0x07, 0x56, 0x03, 0x00, 0x56, 0x02, 0x00, 0x62, 0xd0, 0x00, 0x06, + 0x3f, 0x2a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd5, 0x52, 0x02, + 0x3f, 0x3f, 0x52, 0x03, 0x3f, 0x3f, 0x52, 0x00, 0x53, 0x3f, 0x55, + 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x4a, 0x53, + 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, + 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x32, 0x0e, 0x40, 0x00, 0x51, + 0x40, 0x60, 0xd5, 0x51, 0x3e, 0x3f, 0x3f, 0x51, 0x3d, 0x3f, 0x3f, + 0x77, 0x00, 0x3d, 0x00, 0x02, 0xce, 0xff, 0x62, 0xd0, 0x00, 0x3c, + 0x6f, 0x02, 0xa0, 0x1a, 0x62, 0xd0, 0x00, 0x3c, 0x70, 0x00, 0xa0, + 0x12, 0x50, 0x75, 0x08, 0x50, 0x30, 0x08, 0x90, 0x0d, 0x38, 0xfe, + 0x98, 0xbf, 0x10, 0x7c, 0x08, 0xad, 0x20, 0x38, 0xfa, 0x20, 0x7f, + 0x10, 0x4f, 0x80, 0x02, 0x40, 0x62, 0xd0, 0x00, 0x52, 0xfc, 0x53, + 0x3f, 0x52, 0xfb, 0x53, 0x40, 0x51, 0x3f, 0x11, 0x01, 0x54, 0xfc, + 0x51, 0x40, 0x19, 0x00, 0x54, 0xfb, 0x3c, 0x40, 0x00, 0xbf, 0xe4, + 0x3c, 0x3f, 0x00, 0xbf, 0xdf, 0x20, 0x7f, 0x10, 0x7c, 0x05, 0x15, + 0x7c, 0x04, 0xd7, 0x20, 0x7f, 0x10, 0x7c, 0x05, 0x11, 0x7c, 0x04, + 0xd3, 0x20, 0x7f, 0x62, 0xd0, 0x00, 0x50, 0x28, 0x12, 0x4f, 0x50, + 0x00, 0x1a, 0x4e, 0xd0, 0x15, 0x62, 0xd0, 0x00, 0x50, 0x28, 0x12, + 0x51, 0x50, 0x00, 0x1a, 0x50, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, + 0xe1, 0x80, 0x1a, 0x62, 0xd0, 0x00, 0x51, 0x51, 0x12, 0x4f, 0x51, + 0x50, 0x1a, 0x4e, 0xd0, 0x08, 0x62, 0xd0, 0x00, 0x50, 0x00, 0x80, + 0x06, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x7f, 0x10, 0x4f, 0x38, 0x02, + 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x51, 0x73, 0x01, 0x01, 0x62, 0xd0, + 0x00, 0x53, 0x24, 0x71, 0x01, 0x62, 0xd0, 0x00, 0x3c, 0x71, 0x00, + 0xb0, 0x69, 0x62, 0xe3, 0x38, 0x10, 0x7c, 0x06, 0x6c, 0x62, 0xd0, + 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, + 0x7c, 0x06, 0x6c, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x40, 0x47, 0x40, + 0x20, 0xa0, 0x03, 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, + 0x9f, 0x43, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, 0xc8, 0xcf, 0xdf, + 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x6c, 0x62, 0xd0, + 0x00, 0x20, 0x53, 0x40, 0x47, 0x40, 0x20, 0xb0, 0x03, 0x80, 0x12, + 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9f, 0x1c, 0x38, 0xfe, 0x77, + 0x00, 0x3d, 0x00, 0x1e, 0xcf, 0xdf, 0x62, 0xd0, 0x00, 0x51, 0x24, + 0x29, 0x08, 0x53, 0x24, 0x43, 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, + 0x10, 0x4f, 0x38, 0x02, 0x70, 0xfe, 0x62, 0xd0, 0x00, 0x51, 0x73, + 0x01, 0x09, 0x62, 0xd0, 0x00, 0x53, 0x24, 0x71, 0x01, 0x62, 0xd0, + 0x00, 0x3c, 0x71, 0x01, 0xb0, 0x60, 0x62, 0xe3, 0x38, 0x10, 0x7c, + 0x06, 0x6c, 0x62, 0xd0, 0x00, 0x20, 0x41, 0x00, 0xf7, 0x56, 0x00, + 0x00, 0x80, 0x1e, 0x10, 0x7c, 0x06, 0x6c, 0x62, 0xd0, 0x00, 0x20, + 0x53, 0x40, 0x47, 0x40, 0x20, 0xa0, 0x03, 0x80, 0x12, 0x50, 0x00, + 0x08, 0x50, 0x14, 0x08, 0x9e, 0xbb, 0x38, 0xfe, 0x77, 0x00, 0x3d, + 0x00, 0xc8, 0xcf, 0xdf, 0x56, 0x00, 0x00, 0x80, 0x1e, 0x10, 0x7c, + 0x06, 0x6c, 0x62, 0xd0, 0x00, 0x20, 0x53, 0x40, 0x47, 0x40, 0x20, + 0xb0, 0x03, 0x80, 0x12, 0x50, 0x00, 0x08, 0x50, 0x14, 0x08, 0x9e, + 0x94, 0x38, 0xfe, 0x77, 0x00, 0x3d, 0x00, 0x1e, 0xcf, 0xdf, 0x43, + 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x01, 0x62, + 0xd0, 0x00, 0x51, 0x24, 0x54, 0x00, 0x52, 0x00, 0x21, 0x0f, 0x39, + 0x01, 0xb0, 0x18, 0x62, 0xd0, 0x00, 0x55, 0x75, 0x00, 0x62, 0xd0, + 0x00, 0x55, 0x67, 0x00, 0x55, 0x66, 0x00, 0x62, 0xd0, 0x00, 0x55, + 0x24, 0x00, 0x80, 0x50, 0x52, 0x00, 0x21, 0x0f, 0x39, 0x02, 0xb0, + 0x1e, 0x62, 0xd0, 0x00, 0x55, 0x75, 0x01, 0x62, 0xd0, 0x00, 0x55, + 0x74, 0x00, 0x62, 0xd0, 0x00, 0x55, 0x67, 0x08, 0x55, 0x66, 0x08, + 0x62, 0xd0, 0x00, 0x55, 0x24, 0x00, 0x80, 0x2b, 0x52, 0x00, 0x21, + 0xf0, 0x39, 0x40, 0xb0, 0x0f, 0x62, 0xd0, 0x00, 0x55, 0x6f, 0x02, + 0x62, 0xd0, 0x00, 0x55, 0x24, 0x00, 0x80, 0x15, 0x52, 0x00, 0x21, + 0xf0, 0x39, 0x50, 0xb0, 0x0d, 0x62, 0xd0, 0x00, 0x55, 0x6f, 0x01, + 0x62, 0xd0, 0x00, 0x55, 0x24, 0x00, 0x38, 0xff, 0x20, 0x7f, 0x62, + 0xd0, 0x00, 0x3c, 0x75, 0x00, 0xa0, 0x13, 0x9e, 0x25, 0x62, 0xd0, + 0x00, 0x3c, 0x74, 0x00, 0xb0, 0x35, 0x55, 0x74, 0x01, 0x7c, 0x0b, + 0xe1, 0x80, 0x2d, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x66, 0xd0, + 0x08, 0x10, 0x7c, 0x05, 0x15, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x05, + 0x11, 0x20, 0x62, 0xd0, 0x00, 0x50, 0x01, 0x3a, 0x67, 0xd0, 0x08, + 0x10, 0x7c, 0x04, 0xd7, 0x20, 0x80, 0x06, 0x10, 0x7c, 0x04, 0xd3, + 0x20, 0x98, 0x8d, 0x7f, 0x10, 0x4f, 0x38, 0x03, 0x56, 0x02, 0x00, + 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x80, 0x51, 0x62, 0xd0, 0x00, + 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, + 0x52, 0xfc, 0x04, 0x3f, 0x52, 0xfb, 0x0c, 0x40, 0x51, 0x40, 0x60, + 0xd4, 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, 0x53, 0x3f, 0x52, 0x02, + 0x12, 0x3f, 0x52, 0x01, 0x1a, 0x40, 0xd0, 0x23, 0x62, 0xd0, 0x00, + 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, + 0x52, 0xfc, 0x04, 0x3f, 0x52, 0xfb, 0x0c, 0x40, 0x51, 0x40, 0x60, + 0xd4, 0x3e, 0x3f, 0x54, 0x01, 0x3e, 0x3f, 0x54, 0x02, 0x77, 0x00, + 0x52, 0x00, 0x3b, 0xfa, 0xcf, 0xab, 0x62, 0xd0, 0x00, 0x52, 0x02, + 0x53, 0x3f, 0x52, 0x01, 0x53, 0x40, 0x38, 0xfd, 0x20, 0x7f, 0x10, + 0x4f, 0x38, 0x02, 0x71, 0x10, 0x41, 0x01, 0xf7, 0x43, 0x00, 0x08, + 0x70, 0xcf, 0x43, 0x00, 0x08, 0x50, 0x00, 0x08, 0x50, 0x64, 0x08, + 0x9d, 0x33, 0x71, 0x10, 0x41, 0x01, 0xf7, 0x41, 0x00, 0xf7, 0x70, + 0xcf, 0x43, 0x00, 0x08, 0x50, 0x00, 0x08, 0x50, 0x64, 0x08, 0x9d, + 0x1e, 0x38, 0xfc, 0x5d, 0x00, 0x62, 0xd0, 0x00, 0x53, 0x40, 0x26, + 0x40, 0x08, 0x3c, 0x40, 0x08, 0xb0, 0x09, 0x56, 0x01, 0x00, 0x56, + 0x00, 0x00, 0x80, 0x07, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x52, + 0x01, 0x62, 0xd0, 0x00, 0x53, 0x6f, 0x71, 0x10, 0x43, 0x00, 0x08, + 0x41, 0x01, 0xf7, 0x70, 0xcf, 0x3c, 0x6f, 0x00, 0xb0, 0x04, 0x43, + 0x00, 0x08, 0x38, 0xfe, 0x20, 0x7f, 0x10, 0x4f, 0x38, 0x01, 0x62, + 0xe3, 0x38, 0x10, 0x50, 0x02, 0x7c, 0x03, 0x9c, 0x20, 0x10, 0x50, + 0xff, 0x7c, 0x03, 0x9c, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x9c, + 0x20, 0x10, 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, 0x50, 0x5a, 0x08, + 0x7c, 0x04, 0x8d, 0x38, 0xfd, 0x20, 0x56, 0x00, 0x00, 0x80, 0xda, + 0x62, 0xd0, 0x00, 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, + 0x3f, 0x6b, 0x40, 0x51, 0x3f, 0x01, 0x4a, 0x53, 0x3d, 0x51, 0x40, + 0x09, 0x00, 0x60, 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, + 0x3d, 0x06, 0x3f, 0x5a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, + 0x3e, 0x3f, 0x53, 0x40, 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x3d, 0x12, + 0x3f, 0x51, 0x3e, 0x1a, 0x40, 0xd0, 0x8c, 0x62, 0xd0, 0x00, 0x52, + 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, 0x51, + 0x3f, 0x01, 0x4a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, 0xd4, + 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, 0x5a, + 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, 0x40, + 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x3d, 0x14, 0x3f, 0x51, 0x3e, 0x1c, + 0x40, 0x51, 0x40, 0x10, 0x7c, 0x03, 0x9c, 0x20, 0x62, 0xd0, 0x00, + 0x52, 0x00, 0x53, 0x3f, 0x55, 0x40, 0x00, 0x65, 0x3f, 0x6b, 0x40, + 0x51, 0x3f, 0x01, 0x4a, 0x53, 0x3d, 0x51, 0x40, 0x09, 0x00, 0x60, + 0xd4, 0x3e, 0x3d, 0x53, 0x3e, 0x3e, 0x3d, 0x53, 0x3d, 0x06, 0x3f, + 0x5a, 0x0e, 0x40, 0x00, 0x51, 0x40, 0x60, 0xd4, 0x3e, 0x3f, 0x53, + 0x40, 0x3e, 0x3f, 0x53, 0x3f, 0x51, 0x3d, 0x14, 0x3f, 0x51, 0x3e, + 0x1c, 0x40, 0x26, 0x40, 0x00, 0x51, 0x3f, 0x10, 0x7c, 0x03, 0x9c, + 0x20, 0x80, 0x0f, 0x10, 0x50, 0x00, 0x7c, 0x03, 0x9c, 0x20, 0x10, + 0x50, 0x00, 0x7c, 0x03, 0x9c, 0x20, 0x77, 0x00, 0x3d, 0x00, 0x02, + 0xcf, 0x23, 0x10, 0x50, 0x00, 0x7c, 0x03, 0x9c, 0x20, 0x10, 0x50, + 0x01, 0x7c, 0x03, 0x9c, 0x20, 0x10, 0x50, 0x00, 0x7c, 0x03, 0x9c, + 0x20, 0x10, 0x50, 0x01, 0x7c, 0x03, 0x9c, 0x20, 0x10, 0x50, 0xff, + 0x7c, 0x03, 0x9c, 0x20, 0x10, 0x50, 0xff, 0x7c, 0x03, 0x9c, 0x7c, + 0x04, 0x84, 0x20, 0x50, 0x13, 0x08, 0x50, 0x88, 0x08, 0x9b, 0x9e, + 0x38, 0xfe, 0x38, 0xff, 0x20, 0x7f, 0x7f, 0x10, 0x4f, 0x5d, 0xd0, + 0x08, 0x62, 0xd0, 0x00, 0x50, 0x00, 0x53, 0x38, 0x53, 0x37, 0x55, + 0x36, 0x10, 0x6f, 0xf9, 0x6f, 0xfa, 0xd0, 0x09, 0x52, 0xfc, 0x04, + 0x38, 0x52, 0xfb, 0x0c, 0x37, 0x66, 0xfc, 0x6c, 0xfb, 0x7a, 0x36, + 0xbf, 0xeb, 0x18, 0x60, 0xd0, 0x20, 0x70, 0x3f, 0x71, 0xc0, 0x7f, + 0x00, 0x24, 0x00, 0x12, 0x00, 0x52, 0x00, 0x08, 0x00, 0x5e, 0x00, + 0x07, 0x00, 0x65, 0x05, 0x01, 0x08, 0x08, 0x78, 0x64, 0x00, 0x6a, + 0x00, 0x04, 0x00, 0x6e, 0x01, 0x03, 0x00, 0x6f, 0x00, 0x05, 0x00, + 0x74, 0x02, 0x01, 0x01, 0xff, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 +}; diff --git a/drivers/input/keyboard/cypressbln/u1-cypress-gpio.h b/drivers/input/keyboard/cypressbln/u1-cypress-gpio.h new file mode 100644 index 0000000..36296e1 --- /dev/null +++ b/drivers/input/keyboard/cypressbln/u1-cypress-gpio.h @@ -0,0 +1,28 @@ +#ifndef __U1_CYPRESS_GPIO_H__ +#define __U1_CYPRESS_GPIO_H__ + +extern unsigned int system_rev; + +#if defined (CONFIG_MACH_U1_REV00PRE) || defined (CONFIG_MACH_U1_REV01PRE) \ + || defined (CONFIG_MACH_U1_REV00) +#define _3_GPIO_TOUCH_EN S5PV310_GPE3(3) +#define _3_GPIO_TOUCH_INT S5PV310_GPE3(7) +#define _3_GPIO_TOUCH_INT_AF S3C_GPIO_SFN(0xf) +#define _3_TOUCH_SDA_28V S5PV310_GPE4(0) +#define _3_TOUCH_SCL_28V S5PV310_GPE4(1) + +#define IRQ_TOUCH_INT gpio_to_irq(_3_GPIO_TOUCH_INT) + +#else + +#define _3_GPIO_TOUCH_EN -1 +#define _3_GPIO_TOUCH_INT GPIO_3_TOUCH_INT +#define _3_GPIO_TOUCH_INT_AF S3C_GPIO_SFN(0xf) +#define _3_TOUCH_SDA_28V GPIO_3_TOUCH_SDA +#define _3_TOUCH_SCL_28V GPIO_3_TOUCH_SCL + +#define IRQ_TOUCH_INT gpio_to_irq(_3_GPIO_TOUCH_INT) +#endif + + +#endif diff --git a/drivers/input/touchscreen/mxt224_u1.c b/drivers/input/touchscreen/mxt224_u1.c index 5a243fb..8b81017 100644 --- a/drivers/input/touchscreen/mxt224_u1.c +++ b/drivers/input/touchscreen/mxt224_u1.c @@ -1210,6 +1210,10 @@ static int __devinit mxt224_init_touch_driver(struct mxt224_data *data) return ret; } +#if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) && defined(CONFIG_TOUCHKEY_BLN) +void (*mxt224_touch_cb)(void) = NULL; +#endif + static void report_input_data(struct mxt224_data *data) { int i; @@ -1336,6 +1340,9 @@ static void report_input_data(struct mxt224_data *data) level); copy_data->lock_status = 1; } + #if defined(CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN) && defined(CONFIG_TOUCHKEY_BLN) + if(mxt224_touch_cb!=NULL) (*mxt224_touch_cb)(); + #endif } } -- cgit v1.1 From 24bbfe56b518e2a94c35a618becd7cd17ddc276a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 23 Apr 2012 22:26:58 -0700 Subject: Input: aiptek - adjust error-handling code label At the point of this error-handling code, aiptek->urb has been allocated, and it does not appear to be less necessary to free it here than in the error-handling code just below. Change-Id: I1b07d7cd62a3df78759dd5a9a5ad27e58350df01 Signed-off-by: Julia Lawall Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/aiptek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 0a619c5..fbc0802 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1853,7 +1853,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) if (i == ARRAY_SIZE(speeds)) { dev_info(&intf->dev, "Aiptek tried all speeds, no sane response\n"); - goto fail2; + goto fail3; } /* Associate this driver's struct with the usb interface. -- cgit v1.1 From 55699ae6f09377fcce9af40f43b4e5e167421958 Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Tue, 1 Dec 2015 13:09:17 -0800 Subject: Input: aiptek - fix crash on detecting device without endpoints The aiptek driver crashes in aiptek_probe() when a specially crafted USB device without endpoints is detected. This fix adds a check that the device has proper configuration expected by the driver. Also an error return value is changed to more matching one in one of the error paths. Change-Id: I02fa4ffcbe9a71948947ef5baeb72632688d9d07 Reported-by: Ralf Spenneberg Signed-off-by: Vladis Dronov Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/aiptek.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index fbc0802..c0531c2 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1811,6 +1811,14 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0); + /* Verify that a device really has an endpoint */ + if (intf->altsetting[0].desc.bNumEndpoints < 1) { + dev_err(&intf->dev, + "interface has %d endpoints, but must have minimum 1\n", + intf->altsetting[0].desc.bNumEndpoints); + err = -EINVAL; + goto fail3; + } endpoint = &intf->altsetting[0].endpoint[0].desc; /* Go set up our URB, which is called when the tablet receives @@ -1853,6 +1861,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) if (i == ARRAY_SIZE(speeds)) { dev_info(&intf->dev, "Aiptek tried all speeds, no sane response\n"); + err = -EINVAL; goto fail3; } -- cgit v1.1 From 09019d5a21ea44e73c7c850e815d11f26a15b813 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:44:07 -0400 Subject: ALSA: timer: Fix leak in SNDRV_TIMER_IOCTL_PARAMS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “tread” has a total size of 32 bytes. Its field “event” and “val” both contain 4 bytes padding. These 8 bytes padding bytes are sent to user without being initialized. Change-Id: Ibf2868136a538eed0f2e75395a5f14a8608dd86d Signed-off-by: Kangjie Lu Signed-off-by: Takashi Iwai --- sound/core/timer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index 9eb25d4..269108a 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1679,6 +1679,7 @@ static int snd_timer_user_params(struct file *file, if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { struct snd_timer_tread tread; + memset(&tread, 0, sizeof(tread)); tread.event = SNDRV_TIMER_EVENT_EARLY; tread.tstamp.tv_sec = 0; tread.tstamp.tv_nsec = 0; -- cgit v1.1 From 75f9d7249fcdce27a26aa50a1620f60d24b9afc5 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:44:20 -0400 Subject: ALSA: timer: Fix leak in events via snd_timer_user_ccallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “r1” has a total size of 32 bytes. Its field “event” and “val” both contain 4 bytes padding. These 8 bytes padding bytes are sent to user without being initialized. Change-Id: I5ece63432f6ca6251fa31c046c211c8c03313a59 Signed-off-by: Kangjie Lu Signed-off-by: Takashi Iwai --- sound/core/timer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index 269108a..c0a11d5 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1180,6 +1180,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, tu->tstamp = *tstamp; if ((tu->filter & (1 << event)) == 0 || !tu->tread) return; + memset(&r1, 0, sizeof(r1)); r1.event = event; r1.tstamp = *tstamp; r1.val = resolution; -- cgit v1.1 From 634a18fa462f27fb73b1d7ec2646d2e08d391b41 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:44:32 -0400 Subject: ALSA: timer: Fix leak in events via snd_timer_user_tinterrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “r1” has a total size of 32 bytes. Its field “event” and “val” both contain 4 bytes padding. These 8 bytes padding bytes are sent to user without being initialized. Change-Id: Ie3dcdee7da8ad292712814e8402c571a717ab8d1 Signed-off-by: Kangjie Lu Signed-off-by: Takashi Iwai --- sound/core/timer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index c0a11d5..22c43b3 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1215,6 +1215,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, } if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { + memset(&r1, 0, sizeof(r1)); r1.event = SNDRV_TIMER_EVENT_RESOLUTION; r1.tstamp = tstamp; r1.val = resolution; -- cgit v1.1 From 7656ba597d2714a580a91742a8adcc48a516b93b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Mar 2016 01:56:23 +0100 Subject: netfilter: x_tables: check for size overflow Ben Hawkes says: integer overflow in xt_alloc_table_info, which on 32-bit systems can lead to small structure allocation and a copy_from_user based heap corruption. Change-Id: I13c554c630651a37e3f6a195e9a5f40cddcb29a1 Reported-by: Ben Hawkes Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/x_tables.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index b0869fe..9d4ad83 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -663,6 +663,10 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) { struct xt_table_info *newinfo; int cpu; + size_t sz = sizeof(*newinfo) + size; + + if (sz < sizeof(*newinfo)) + return NULL; /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages) -- cgit v1.1 From b9b25ea875b42f176babe9b5232f9bd1527933a3 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Wed, 23 Mar 2016 16:38:55 +0100 Subject: ppp: take reference on channels netns Let channels hold a reference on their network namespace. Some channel types, like ppp_async and ppp_synctty, can have their userspace controller running in a different namespace. Therefore they can't rely on them to preclude their netns from being removed from under them. ================================================================== BUG: KASAN: use-after-free in ppp_unregister_channel+0x372/0x3a0 at addr ffff880064e217e0 Read of size 8 by task syz-executor/11581 ============================================================================= BUG net_namespace (Not tainted): kasan: bad access detected ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Allocated in copy_net_ns+0x6b/0x1a0 age=92569 cpu=3 pid=6906 [< none >] ___slab_alloc+0x4c7/0x500 kernel/mm/slub.c:2440 [< none >] __slab_alloc+0x4c/0x90 kernel/mm/slub.c:2469 [< inline >] slab_alloc_node kernel/mm/slub.c:2532 [< inline >] slab_alloc kernel/mm/slub.c:2574 [< none >] kmem_cache_alloc+0x23a/0x2b0 kernel/mm/slub.c:2579 [< inline >] kmem_cache_zalloc kernel/include/linux/slab.h:597 [< inline >] net_alloc kernel/net/core/net_namespace.c:325 [< none >] copy_net_ns+0x6b/0x1a0 kernel/net/core/net_namespace.c:360 [< none >] create_new_namespaces+0x2f6/0x610 kernel/kernel/nsproxy.c:95 [< none >] copy_namespaces+0x297/0x320 kernel/kernel/nsproxy.c:150 [< none >] copy_process.part.35+0x1bf4/0x5760 kernel/kernel/fork.c:1451 [< inline >] copy_process kernel/kernel/fork.c:1274 [< none >] _do_fork+0x1bc/0xcb0 kernel/kernel/fork.c:1723 [< inline >] SYSC_clone kernel/kernel/fork.c:1832 [< none >] SyS_clone+0x37/0x50 kernel/kernel/fork.c:1826 [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a kernel/arch/x86/entry/entry_64.S:185 INFO: Freed in net_drop_ns+0x67/0x80 age=575 cpu=2 pid=2631 [< none >] __slab_free+0x1fc/0x320 kernel/mm/slub.c:2650 [< inline >] slab_free kernel/mm/slub.c:2805 [< none >] kmem_cache_free+0x2a0/0x330 kernel/mm/slub.c:2814 [< inline >] net_free kernel/net/core/net_namespace.c:341 [< none >] net_drop_ns+0x67/0x80 kernel/net/core/net_namespace.c:348 [< none >] cleanup_net+0x4e5/0x600 kernel/net/core/net_namespace.c:448 [< none >] process_one_work+0x794/0x1440 kernel/kernel/workqueue.c:2036 [< none >] worker_thread+0xdb/0xfc0 kernel/kernel/workqueue.c:2170 [< none >] kthread+0x23f/0x2d0 kernel/drivers/block/aoe/aoecmd.c:1303 [< none >] ret_from_fork+0x3f/0x70 kernel/arch/x86/entry/entry_64.S:468 INFO: Slab 0xffffea0001938800 objects=3 used=0 fp=0xffff880064e20000 flags=0x5fffc0000004080 INFO: Object 0xffff880064e20000 @offset=0 fp=0xffff880064e24200 CPU: 1 PID: 11581 Comm: syz-executor Tainted: G B 4.4.0+ Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.8.2-0-g33fbe13 by qemu-project.org 04/01/2014 00000000ffffffff ffff8800662c7790 ffffffff8292049d ffff88003e36a300 ffff880064e20000 ffff880064e20000 ffff8800662c77c0 ffffffff816f2054 ffff88003e36a300 ffffea0001938800 ffff880064e20000 0000000000000000 Call Trace: [< inline >] __dump_stack kernel/lib/dump_stack.c:15 [] dump_stack+0x6f/0xa2 kernel/lib/dump_stack.c:50 [] print_trailer+0xf4/0x150 kernel/mm/slub.c:654 [] object_err+0x2f/0x40 kernel/mm/slub.c:661 [< inline >] print_address_description kernel/mm/kasan/report.c:138 [] kasan_report_error+0x215/0x530 kernel/mm/kasan/report.c:236 [< inline >] kasan_report kernel/mm/kasan/report.c:259 [] __asan_report_load8_noabort+0x3e/0x40 kernel/mm/kasan/report.c:280 [< inline >] ? ppp_pernet kernel/include/linux/compiler.h:218 [] ? ppp_unregister_channel+0x372/0x3a0 kernel/drivers/net/ppp/ppp_generic.c:2392 [< inline >] ppp_pernet kernel/include/linux/compiler.h:218 [] ppp_unregister_channel+0x372/0x3a0 kernel/drivers/net/ppp/ppp_generic.c:2392 [< inline >] ? ppp_pernet kernel/drivers/net/ppp/ppp_generic.c:293 [] ? ppp_unregister_channel+0xe6/0x3a0 kernel/drivers/net/ppp/ppp_generic.c:2392 [] ppp_asynctty_close+0xa3/0x130 kernel/drivers/net/ppp/ppp_async.c:241 [] ? async_lcp_peek+0x5b0/0x5b0 kernel/drivers/net/ppp/ppp_async.c:1000 [] tty_ldisc_close.isra.1+0x99/0xe0 kernel/drivers/tty/tty_ldisc.c:478 [] tty_ldisc_kill+0x40/0x170 kernel/drivers/tty/tty_ldisc.c:744 [] tty_ldisc_release+0x1b3/0x260 kernel/drivers/tty/tty_ldisc.c:772 [] tty_release+0xac1/0x13e0 kernel/drivers/tty/tty_io.c:1901 [] ? release_tty+0x320/0x320 kernel/drivers/tty/tty_io.c:1688 [] __fput+0x236/0x780 kernel/fs/file_table.c:208 [] ____fput+0x15/0x20 kernel/fs/file_table.c:244 [] task_work_run+0x16b/0x200 kernel/kernel/task_work.c:115 [< inline >] exit_task_work kernel/include/linux/task_work.h:21 [] do_exit+0x8b5/0x2c60 kernel/kernel/exit.c:750 [] ? debug_check_no_locks_freed+0x290/0x290 kernel/kernel/locking/lockdep.c:4123 [] ? mm_update_next_owner+0x6f0/0x6f0 kernel/kernel/exit.c:357 [] ? __dequeue_signal+0x136/0x470 kernel/kernel/signal.c:550 [] ? recalc_sigpending_tsk+0x13b/0x180 kernel/kernel/signal.c:145 [] do_group_exit+0x108/0x330 kernel/kernel/exit.c:880 [] get_signal+0x5e4/0x14f0 kernel/kernel/signal.c:2307 [< inline >] ? kretprobe_table_lock kernel/kernel/kprobes.c:1113 [] ? kprobe_flush_task+0xb5/0x450 kernel/kernel/kprobes.c:1158 [] do_signal+0x83/0x1c90 kernel/arch/x86/kernel/signal.c:712 [] ? recycle_rp_inst+0x310/0x310 kernel/include/linux/list.h:655 [] ? setup_sigcontext+0x780/0x780 kernel/arch/x86/kernel/signal.c:165 [] ? finish_task_switch+0x424/0x5f0 kernel/kernel/sched/core.c:2692 [< inline >] ? finish_lock_switch kernel/kernel/sched/sched.h:1099 [] ? finish_task_switch+0x120/0x5f0 kernel/kernel/sched/core.c:2678 [< inline >] ? context_switch kernel/kernel/sched/core.c:2807 [] ? __schedule+0x919/0x1bd0 kernel/kernel/sched/core.c:3283 [] exit_to_usermode_loop+0xf1/0x1a0 kernel/arch/x86/entry/common.c:247 [< inline >] prepare_exit_to_usermode kernel/arch/x86/entry/common.c:282 [] syscall_return_slowpath+0x19f/0x210 kernel/arch/x86/entry/common.c:344 [] int_ret_from_sys_call+0x25/0x9f kernel/arch/x86/entry/entry_64.S:281 Memory state around the buggy address: ffff880064e21680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff880064e21700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff880064e21780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff880064e21800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff880064e21880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Change-Id: I591b30eafa1b57bd2e211e1f33c39128702ff0b0 Fixes: 273ec51dd7ce ("net: ppp_generic - introduce net-namespace functionality v2") Reported-by: Baozeng Ding Signed-off-by: Guillaume Nault Reviewed-by: Cyrill Gorcunov Signed-off-by: David S. Miller ppp = NULL; pch->chan = chan; - pch->chan_net = net; + pch->chan_net = get_net(net); chan->ppp = pch; init_ppp_file(&pch->file, CHANNEL); pch->file.hdrlen = chan->hdrlen; @@ -2269,6 +2269,8 @@ ppp_unregister_channel(struct ppp_channel *chan) spin_lock_bh(&pn->all_channels_lock); list_del(&pch->list); spin_unlock_bh(&pn->all_channels_lock); + put_net(pch->chan_net); + pch->chan_net = NULL; pch->file.dead = 1; wake_up_interruptible(&pch->file.rwait); -- cgit v1.1 From aa678abcacddbd9b7a6d30793b9d204f28f365d7 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Wed, 18 May 2016 22:36:20 +1000 Subject: smdk4x12: disable vmware mvp driver Change-Id: I5378dfc05847c4e7e22487c4e80582da36e0f2e2 --- arch/arm/configs/cyanogenmod_i925_defconfig | 2 +- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 +- arch/arm/configs/cyanogenmod_i9305_defconfig | 2 +- arch/arm/configs/cyanogenmod_n5100_defconfig | 2 +- arch/arm/configs/cyanogenmod_n5110_defconfig | 2 +- arch/arm/configs/cyanogenmod_n5120_defconfig | 2 +- arch/arm/configs/cyanogenmod_n7100_defconfig | 2 +- arch/arm/configs/cyanogenmod_n8000_defconfig | 2 +- arch/arm/configs/cyanogenmod_n8013_defconfig | 2 +- arch/arm/configs/cyanogenmod_t0lte_defconfig | 2 +- arch/arm/configs/cyanogenmod_t0ltecdma_defconfig | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i925_defconfig b/arch/arm/configs/cyanogenmod_i925_defconfig index 4737aab..0023524 100644 --- a/arch/arm/configs/cyanogenmod_i925_defconfig +++ b/arch/arm/configs/cyanogenmod_i925_defconfig @@ -681,7 +681,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 9d6f8b8..39e780d 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -702,7 +702,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_i9305_defconfig b/arch/arm/configs/cyanogenmod_i9305_defconfig index 5c640cc..2d9dc52 100755 --- a/arch/arm/configs/cyanogenmod_i9305_defconfig +++ b/arch/arm/configs/cyanogenmod_i9305_defconfig @@ -665,7 +665,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_n5100_defconfig b/arch/arm/configs/cyanogenmod_n5100_defconfig index c8fa884..fb1af40 100644 --- a/arch/arm/configs/cyanogenmod_n5100_defconfig +++ b/arch/arm/configs/cyanogenmod_n5100_defconfig @@ -710,7 +710,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_n5110_defconfig b/arch/arm/configs/cyanogenmod_n5110_defconfig index 64c01c7..7775d3f 100644 --- a/arch/arm/configs/cyanogenmod_n5110_defconfig +++ b/arch/arm/configs/cyanogenmod_n5110_defconfig @@ -712,7 +712,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index 541a128..586f708 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -714,7 +714,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_n7100_defconfig b/arch/arm/configs/cyanogenmod_n7100_defconfig index eb68010..c1c52d9 100644 --- a/arch/arm/configs/cyanogenmod_n7100_defconfig +++ b/arch/arm/configs/cyanogenmod_n7100_defconfig @@ -688,7 +688,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_n8000_defconfig b/arch/arm/configs/cyanogenmod_n8000_defconfig index bb6f24e..a3b96e1 100644 --- a/arch/arm/configs/cyanogenmod_n8000_defconfig +++ b/arch/arm/configs/cyanogenmod_n8000_defconfig @@ -684,7 +684,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_n8013_defconfig b/arch/arm/configs/cyanogenmod_n8013_defconfig index 37bc308..ba974d2 100644 --- a/arch/arm/configs/cyanogenmod_n8013_defconfig +++ b/arch/arm/configs/cyanogenmod_n8013_defconfig @@ -663,7 +663,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_t0lte_defconfig b/arch/arm/configs/cyanogenmod_t0lte_defconfig index b69d4fe..3c850ac 100755 --- a/arch/arm/configs/cyanogenmod_t0lte_defconfig +++ b/arch/arm/configs/cyanogenmod_t0lte_defconfig @@ -687,7 +687,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # diff --git a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig index 43e473a..b6ac9f5 100755 --- a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig +++ b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig @@ -687,7 +687,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -CONFIG_VMWARE_MVP=y +# CONFIG_VMWARE_MVP is not set # CONFIG_VMWARE_MVP_DEBUG is not set # -- cgit v1.1 From 9001c4e0f1e823ba318cdf9e61c75cbc9442a829 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Wed, 18 May 2016 23:07:25 +1000 Subject: i9300: disable network filesystems and regen defconfig Change-Id: Id1e80d0b01e579b6068f30f73b4542ffe546f62b --- arch/arm/configs/cyanogenmod_i9300_defconfig | 32 ++-------------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 39e780d..e359d19 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -703,7 +703,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y # CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options @@ -1526,6 +1525,7 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_SENSORS_HALL is not set CONFIG_KEYBOARD_CYPRESS_TOUCH=y +# CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set @@ -3050,35 +3050,7 @@ CONFIG_F2FS_FS_XATTR=y CONFIG_F2FS_FS_POSIX_ACL=y CONFIG_F2FS_FS_SECURITY=y # CONFIG_F2FS_CHECK_FS is not set -CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_NFS_FS=m -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -CONFIG_NFS_V4=y -# CONFIG_NFS_V4_1 is not set -# CONFIG_NFS_USE_LEGACY_DNS is not set -CONFIG_NFS_USE_KERNEL_DNS=y -# CONFIG_NFS_USE_NEW_IDMAPPER is not set -# CONFIG_NFSD is not set -CONFIG_LOCKD=m -CONFIG_LOCKD_V4=y -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=m -CONFIG_SUNRPC_GSS=m -# CONFIG_CEPH_FS is not set -CONFIG_CIFS=m -CONFIG_CIFS_STATS=y -CONFIG_CIFS_STATS2=y -CONFIG_CIFS_WEAK_PW_HASH=y -CONFIG_CIFS_UPCALL=y -CONFIG_CIFS_XATTR=y -CONFIG_CIFS_POSIX=y -# CONFIG_CIFS_DEBUG2 is not set -CONFIG_CIFS_DFS_UPCALL=y -CONFIG_CIFS_ACL=y -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set -# CONFIG_AFS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set # # Partition Types -- cgit v1.1 From d3378b75f8c853b3d963443da07a372fb351758f Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Thu, 19 May 2016 22:40:24 +1000 Subject: purge the vmware mvp driver permanently Change-Id: Ifae030867a11bca4fcf0439f1cda2bfd10fc22d8 --- arch/arm/Kconfig | 2 - arch/arm/Makefile | 1 - arch/arm/configs/cyanogenmod_d710_defconfig | 1 - arch/arm/configs/cyanogenmod_i777_defconfig | 1 - arch/arm/configs/cyanogenmod_i9100_defconfig | 1 - arch/arm/configs/cyanogenmod_i925_defconfig | 2 - arch/arm/configs/cyanogenmod_i9300_defconfig | 1 - arch/arm/configs/cyanogenmod_i9305_defconfig | 2 - arch/arm/configs/cyanogenmod_n5100_defconfig | 2 - arch/arm/configs/cyanogenmod_n5110_defconfig | 2 - arch/arm/configs/cyanogenmod_n5120_defconfig | 2 - arch/arm/configs/cyanogenmod_n7000_defconfig | 1 - arch/arm/configs/cyanogenmod_n7100_defconfig | 2 - arch/arm/configs/cyanogenmod_n8000_defconfig | 2 - arch/arm/configs/cyanogenmod_n8013_defconfig | 2 - arch/arm/configs/cyanogenmod_t0lte_defconfig | 2 - arch/arm/configs/cyanogenmod_t0ltecdma_defconfig | 2 - arch/arm/mvp/Kconfig | 24 - arch/arm/mvp/Makefile | 3 - arch/arm/mvp/commkm/COPYING | 341 --- arch/arm/mvp/commkm/Kbuild | 9 - arch/arm/mvp/commkm/Makefile | 1 - arch/arm/mvp/commkm/check_kconfig.c | 91 - arch/arm/mvp/commkm/comm.c | 1457 ----------- arch/arm/mvp/commkm/comm.h | 171 -- arch/arm/mvp/commkm/comm_ev.h | 51 - arch/arm/mvp/commkm/comm_ev_kernel.c | 136 - arch/arm/mvp/commkm/comm_os.h | 150 -- arch/arm/mvp/commkm/comm_os_linux.c | 371 --- arch/arm/mvp/commkm/comm_os_linux.h | 699 ------ arch/arm/mvp/commkm/comm_os_mod_linux.c | 105 - arch/arm/mvp/commkm/comm_os_mod_ver.h | 38 - arch/arm/mvp/commkm/comm_svc.c | 421 ---- arch/arm/mvp/commkm/comm_svc.h | 71 - arch/arm/mvp/commkm/comm_transp.h | 90 - arch/arm/mvp/commkm/comm_transp_impl.h | 165 -- arch/arm/mvp/commkm/comm_transp_mvp.c | 944 ------- arch/arm/mvp/commkm/fatalerror.h | 126 - arch/arm/mvp/commkm/include_check.h | 18 - arch/arm/mvp/commkm/mksck.h | 153 -- arch/arm/mvp/commkm/mksck_sockaddr.h | 50 - arch/arm/mvp/commkm/mvp.h | 48 - arch/arm/mvp/commkm/mvp_assert.h | 125 - arch/arm/mvp/commkm/mvp_compiler.h | 56 - arch/arm/mvp/commkm/mvp_compiler_gcc.h | 87 - arch/arm/mvp/commkm/mvp_types.h | 94 - arch/arm/mvp/commkm/mvpkm_comm_ev.h | 53 - arch/arm/mvp/commkm/nottested.h | 54 - arch/arm/mvp/commkm/platdefx.h | 67 - arch/arm/mvp/commkm/qp.h | 332 --- arch/arm/mvp/commkm/utils.h | 172 -- arch/arm/mvp/commkm/vmid.h | 44 - arch/arm/mvp/mvpkm/COPYING | 341 --- arch/arm/mvp/mvpkm/Kbuild | 32 - arch/arm/mvp/mvpkm/Makefile | 1 - arch/arm/mvp/mvpkm/actions.h | 57 - arch/arm/mvp/mvpkm/arm_as_macros.h | 91 - arch/arm/mvp/mvpkm/arm_defs.h | 54 - arch/arm/mvp/mvpkm/arm_gcc_inline.h | 206 -- arch/arm/mvp/mvpkm/arm_inline.h | 179 -- arch/arm/mvp/mvpkm/arm_types.h | 42 - arch/arm/mvp/mvpkm/atomic.h | 88 - arch/arm/mvp/mvpkm/atomic_arm.h | 329 --- arch/arm/mvp/mvpkm/check_kconfig.c | 91 - arch/arm/mvp/mvpkm/comm_os.h | 150 -- arch/arm/mvp/mvpkm/comm_os_linux.h | 699 ------ arch/arm/mvp/mvpkm/comm_transp.h | 90 - arch/arm/mvp/mvpkm/comm_transp_impl.h | 165 -- arch/arm/mvp/mvpkm/coproc_defs.h | 351 --- arch/arm/mvp/mvpkm/cpufreq_kernel.c | 308 --- arch/arm/mvp/mvpkm/cpufreq_kernel.h | 47 - arch/arm/mvp/mvpkm/exc_defs.h | 67 - arch/arm/mvp/mvpkm/exc_types.h | 53 - arch/arm/mvp/mvpkm/exitstatus.h | 67 - arch/arm/mvp/mvpkm/fatalerror.h | 126 - arch/arm/mvp/mvpkm/include_check.h | 18 - arch/arm/mvp/mvpkm/instr_defs.h | 426 ---- arch/arm/mvp/mvpkm/lowmemkiller_variant.sh | 92 - arch/arm/mvp/mvpkm/lpae_defs.h | 92 - arch/arm/mvp/mvpkm/lpae_types.h | 124 - arch/arm/mvp/mvpkm/mksck.h | 153 -- arch/arm/mvp/mvpkm/mksck_kernel.c | 2589 -------------------- arch/arm/mvp/mvpkm/mksck_kernel.h | 68 - arch/arm/mvp/mvpkm/mksck_shared.c | 343 --- arch/arm/mvp/mvpkm/mksck_shared.h | 189 -- arch/arm/mvp/mvpkm/mksck_sockaddr.h | 50 - arch/arm/mvp/mvpkm/mmu_defs.h | 218 -- arch/arm/mvp/mvpkm/mmu_types.h | 226 -- arch/arm/mvp/mvpkm/montimer_kernel.c | 102 - arch/arm/mvp/mvpkm/montimer_kernel.h | 47 - arch/arm/mvp/mvpkm/monva_common.h | 106 - arch/arm/mvp/mvpkm/mutex.h | 107 - arch/arm/mvp/mvpkm/mutex_kernel.c | 480 ---- arch/arm/mvp/mvpkm/mutex_kernel.h | 41 - arch/arm/mvp/mvpkm/mvp.h | 48 - arch/arm/mvp/mvpkm/mvp_assert.h | 125 - arch/arm/mvp/mvpkm/mvp_balloon.h | 217 -- arch/arm/mvp/mvpkm/mvp_compiler.h | 56 - arch/arm/mvp/mvpkm/mvp_compiler_gcc.h | 87 - arch/arm/mvp/mvpkm/mvp_math.h | 133 - arch/arm/mvp/mvpkm/mvp_timer.h | 72 - arch/arm/mvp/mvpkm/mvp_types.h | 94 - arch/arm/mvp/mvpkm/mvp_version.h | 116 - arch/arm/mvp/mvpkm/mvpkm_comm_ev.c | 60 - arch/arm/mvp/mvpkm/mvpkm_comm_ev.h | 53 - arch/arm/mvp/mvpkm/mvpkm_kernel.h | 83 - arch/arm/mvp/mvpkm/mvpkm_main.c | 2691 -------------------- arch/arm/mvp/mvpkm/mvpkm_private.h | 97 - arch/arm/mvp/mvpkm/mvpkm_types.h | 49 - arch/arm/mvp/mvpkm/nottested.h | 54 - arch/arm/mvp/mvpkm/platdefx.h | 67 - arch/arm/mvp/mvpkm/psr_defs.h | 117 - arch/arm/mvp/mvpkm/qp.h | 332 --- arch/arm/mvp/mvpkm/qp_common.c | 337 --- arch/arm/mvp/mvpkm/qp_host_kernel.c | 574 ----- arch/arm/mvp/mvpkm/qp_host_kernel.h | 44 - arch/arm/mvp/mvpkm/tsc.h | 49 - arch/arm/mvp/mvpkm/utils.h | 172 -- arch/arm/mvp/mvpkm/ve_defs.h | 72 - arch/arm/mvp/mvpkm/vfp_switch.S | 216 -- arch/arm/mvp/mvpkm/vmid.h | 44 - arch/arm/mvp/mvpkm/worldswitch.h | 381 --- arch/arm/mvp/mvpkm/wscalls.h | 165 -- arch/arm/mvp/pvtcpkm/COPYING | 341 --- arch/arm/mvp/pvtcpkm/Kbuild | 9 - arch/arm/mvp/pvtcpkm/Makefile | 1 - arch/arm/mvp/pvtcpkm/check_kconfig.c | 91 - arch/arm/mvp/pvtcpkm/comm.h | 171 -- arch/arm/mvp/pvtcpkm/comm_os.h | 150 -- arch/arm/mvp/pvtcpkm/comm_os_linux.c | 371 --- arch/arm/mvp/pvtcpkm/comm_os_linux.h | 699 ------ arch/arm/mvp/pvtcpkm/comm_os_mod_linux.c | 105 - arch/arm/mvp/pvtcpkm/comm_os_mod_ver.h | 38 - arch/arm/mvp/pvtcpkm/comm_svc.h | 71 - arch/arm/mvp/pvtcpkm/comm_transp.h | 90 - arch/arm/mvp/pvtcpkm/include_check.h | 18 - arch/arm/mvp/pvtcpkm/pvtcp.c | 587 ----- arch/arm/mvp/pvtcpkm/pvtcp.h | 458 ---- arch/arm/mvp/pvtcpkm/pvtcp_off.c | 81 - arch/arm/mvp/pvtcpkm/pvtcp_off.h | 219 -- arch/arm/mvp/pvtcpkm/pvtcp_off_io_linux.c | 831 ------- arch/arm/mvp/pvtcpkm/pvtcp_off_linux.c | 2858 ---------------------- arch/arm/mvp/pvtcpkm/pvtcp_off_linux.h | 226 -- arch/arm/mvp/pvtcpkm/pvtcp_off_linux_shim.S | 70 - 144 files changed, 30040 deletions(-) delete mode 100644 arch/arm/mvp/Kconfig delete mode 100644 arch/arm/mvp/Makefile delete mode 100644 arch/arm/mvp/commkm/COPYING delete mode 100644 arch/arm/mvp/commkm/Kbuild delete mode 100644 arch/arm/mvp/commkm/Makefile delete mode 100644 arch/arm/mvp/commkm/check_kconfig.c delete mode 100644 arch/arm/mvp/commkm/comm.c delete mode 100644 arch/arm/mvp/commkm/comm.h delete mode 100644 arch/arm/mvp/commkm/comm_ev.h delete mode 100644 arch/arm/mvp/commkm/comm_ev_kernel.c delete mode 100644 arch/arm/mvp/commkm/comm_os.h delete mode 100644 arch/arm/mvp/commkm/comm_os_linux.c delete mode 100644 arch/arm/mvp/commkm/comm_os_linux.h delete mode 100644 arch/arm/mvp/commkm/comm_os_mod_linux.c delete mode 100644 arch/arm/mvp/commkm/comm_os_mod_ver.h delete mode 100644 arch/arm/mvp/commkm/comm_svc.c delete mode 100644 arch/arm/mvp/commkm/comm_svc.h delete mode 100644 arch/arm/mvp/commkm/comm_transp.h delete mode 100644 arch/arm/mvp/commkm/comm_transp_impl.h delete mode 100644 arch/arm/mvp/commkm/comm_transp_mvp.c delete mode 100644 arch/arm/mvp/commkm/fatalerror.h delete mode 100644 arch/arm/mvp/commkm/include_check.h delete mode 100644 arch/arm/mvp/commkm/mksck.h delete mode 100644 arch/arm/mvp/commkm/mksck_sockaddr.h delete mode 100644 arch/arm/mvp/commkm/mvp.h delete mode 100644 arch/arm/mvp/commkm/mvp_assert.h delete mode 100644 arch/arm/mvp/commkm/mvp_compiler.h delete mode 100644 arch/arm/mvp/commkm/mvp_compiler_gcc.h delete mode 100644 arch/arm/mvp/commkm/mvp_types.h delete mode 100644 arch/arm/mvp/commkm/mvpkm_comm_ev.h delete mode 100644 arch/arm/mvp/commkm/nottested.h delete mode 100644 arch/arm/mvp/commkm/platdefx.h delete mode 100644 arch/arm/mvp/commkm/qp.h delete mode 100644 arch/arm/mvp/commkm/utils.h delete mode 100644 arch/arm/mvp/commkm/vmid.h delete mode 100644 arch/arm/mvp/mvpkm/COPYING delete mode 100644 arch/arm/mvp/mvpkm/Kbuild delete mode 100644 arch/arm/mvp/mvpkm/Makefile delete mode 100644 arch/arm/mvp/mvpkm/actions.h delete mode 100644 arch/arm/mvp/mvpkm/arm_as_macros.h delete mode 100644 arch/arm/mvp/mvpkm/arm_defs.h delete mode 100644 arch/arm/mvp/mvpkm/arm_gcc_inline.h delete mode 100644 arch/arm/mvp/mvpkm/arm_inline.h delete mode 100644 arch/arm/mvp/mvpkm/arm_types.h delete mode 100644 arch/arm/mvp/mvpkm/atomic.h delete mode 100644 arch/arm/mvp/mvpkm/atomic_arm.h delete mode 100644 arch/arm/mvp/mvpkm/check_kconfig.c delete mode 100644 arch/arm/mvp/mvpkm/comm_os.h delete mode 100644 arch/arm/mvp/mvpkm/comm_os_linux.h delete mode 100644 arch/arm/mvp/mvpkm/comm_transp.h delete mode 100644 arch/arm/mvp/mvpkm/comm_transp_impl.h delete mode 100644 arch/arm/mvp/mvpkm/coproc_defs.h delete mode 100644 arch/arm/mvp/mvpkm/cpufreq_kernel.c delete mode 100644 arch/arm/mvp/mvpkm/cpufreq_kernel.h delete mode 100644 arch/arm/mvp/mvpkm/exc_defs.h delete mode 100644 arch/arm/mvp/mvpkm/exc_types.h delete mode 100644 arch/arm/mvp/mvpkm/exitstatus.h delete mode 100644 arch/arm/mvp/mvpkm/fatalerror.h delete mode 100644 arch/arm/mvp/mvpkm/include_check.h delete mode 100644 arch/arm/mvp/mvpkm/instr_defs.h delete mode 100644 arch/arm/mvp/mvpkm/lowmemkiller_variant.sh delete mode 100644 arch/arm/mvp/mvpkm/lpae_defs.h delete mode 100644 arch/arm/mvp/mvpkm/lpae_types.h delete mode 100644 arch/arm/mvp/mvpkm/mksck.h delete mode 100644 arch/arm/mvp/mvpkm/mksck_kernel.c delete mode 100644 arch/arm/mvp/mvpkm/mksck_kernel.h delete mode 100644 arch/arm/mvp/mvpkm/mksck_shared.c delete mode 100644 arch/arm/mvp/mvpkm/mksck_shared.h delete mode 100644 arch/arm/mvp/mvpkm/mksck_sockaddr.h delete mode 100644 arch/arm/mvp/mvpkm/mmu_defs.h delete mode 100644 arch/arm/mvp/mvpkm/mmu_types.h delete mode 100644 arch/arm/mvp/mvpkm/montimer_kernel.c delete mode 100644 arch/arm/mvp/mvpkm/montimer_kernel.h delete mode 100644 arch/arm/mvp/mvpkm/monva_common.h delete mode 100644 arch/arm/mvp/mvpkm/mutex.h delete mode 100644 arch/arm/mvp/mvpkm/mutex_kernel.c delete mode 100644 arch/arm/mvp/mvpkm/mutex_kernel.h delete mode 100644 arch/arm/mvp/mvpkm/mvp.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_assert.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_balloon.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_compiler.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_compiler_gcc.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_math.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_timer.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_types.h delete mode 100644 arch/arm/mvp/mvpkm/mvp_version.h delete mode 100644 arch/arm/mvp/mvpkm/mvpkm_comm_ev.c delete mode 100644 arch/arm/mvp/mvpkm/mvpkm_comm_ev.h delete mode 100644 arch/arm/mvp/mvpkm/mvpkm_kernel.h delete mode 100644 arch/arm/mvp/mvpkm/mvpkm_main.c delete mode 100644 arch/arm/mvp/mvpkm/mvpkm_private.h delete mode 100644 arch/arm/mvp/mvpkm/mvpkm_types.h delete mode 100644 arch/arm/mvp/mvpkm/nottested.h delete mode 100644 arch/arm/mvp/mvpkm/platdefx.h delete mode 100644 arch/arm/mvp/mvpkm/psr_defs.h delete mode 100644 arch/arm/mvp/mvpkm/qp.h delete mode 100644 arch/arm/mvp/mvpkm/qp_common.c delete mode 100644 arch/arm/mvp/mvpkm/qp_host_kernel.c delete mode 100644 arch/arm/mvp/mvpkm/qp_host_kernel.h delete mode 100644 arch/arm/mvp/mvpkm/tsc.h delete mode 100644 arch/arm/mvp/mvpkm/utils.h delete mode 100644 arch/arm/mvp/mvpkm/ve_defs.h delete mode 100644 arch/arm/mvp/mvpkm/vfp_switch.S delete mode 100644 arch/arm/mvp/mvpkm/vmid.h delete mode 100644 arch/arm/mvp/mvpkm/worldswitch.h delete mode 100644 arch/arm/mvp/mvpkm/wscalls.h delete mode 100644 arch/arm/mvp/pvtcpkm/COPYING delete mode 100644 arch/arm/mvp/pvtcpkm/Kbuild delete mode 100644 arch/arm/mvp/pvtcpkm/Makefile delete mode 100644 arch/arm/mvp/pvtcpkm/check_kconfig.c delete mode 100644 arch/arm/mvp/pvtcpkm/comm.h delete mode 100644 arch/arm/mvp/pvtcpkm/comm_os.h delete mode 100644 arch/arm/mvp/pvtcpkm/comm_os_linux.c delete mode 100644 arch/arm/mvp/pvtcpkm/comm_os_linux.h delete mode 100644 arch/arm/mvp/pvtcpkm/comm_os_mod_linux.c delete mode 100644 arch/arm/mvp/pvtcpkm/comm_os_mod_ver.h delete mode 100644 arch/arm/mvp/pvtcpkm/comm_svc.h delete mode 100644 arch/arm/mvp/pvtcpkm/comm_transp.h delete mode 100644 arch/arm/mvp/pvtcpkm/include_check.h delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp.c delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp.h delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp_off.c delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp_off.h delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp_off_io_linux.c delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp_off_linux.c delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp_off_linux.h delete mode 100644 arch/arm/mvp/pvtcpkm/pvtcp_off_linux_shim.S diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6799d57..a6725f1 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1777,8 +1777,6 @@ config ARM_FLUSH_CONSOLE_ON_RESTART released if it failed to be acquired, which will cause all the pending messages to be flushed. -source "arch/arm/mvp/Kconfig" - endmenu menu "Boot options" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 32d0cc3..f05679a 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -249,7 +249,6 @@ endif core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/ core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) core-$(CONFIG_VFP) += arch/arm/vfp/ -core-$(CONFIG_VMWARE_MVP) += arch/arm/mvp/ # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ diff --git a/arch/arm/configs/cyanogenmod_d710_defconfig b/arch/arm/configs/cyanogenmod_d710_defconfig index e5945d3..37b84c7 100644 --- a/arch/arm/configs/cyanogenmod_d710_defconfig +++ b/arch/arm/configs/cyanogenmod_d710_defconfig @@ -660,7 +660,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set # CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set -# CONFIG_VMWARE_MVP is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_i777_defconfig b/arch/arm/configs/cyanogenmod_i777_defconfig index 49d5e4d..fe979ab 100644 --- a/arch/arm/configs/cyanogenmod_i777_defconfig +++ b/arch/arm/configs/cyanogenmod_i777_defconfig @@ -673,7 +673,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set # CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set -# CONFIG_VMWARE_MVP is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index c56b778..fae21aa 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -686,7 +686,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set # CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set -# CONFIG_VMWARE_MVP is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_i925_defconfig b/arch/arm/configs/cyanogenmod_i925_defconfig index 0023524..ea42fa6 100644 --- a/arch/arm/configs/cyanogenmod_i925_defconfig +++ b/arch/arm/configs/cyanogenmod_i925_defconfig @@ -681,8 +681,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index e359d19..0239633 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -702,7 +702,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_i9305_defconfig b/arch/arm/configs/cyanogenmod_i9305_defconfig index 2d9dc52..a5737e3 100755 --- a/arch/arm/configs/cyanogenmod_i9305_defconfig +++ b/arch/arm/configs/cyanogenmod_i9305_defconfig @@ -665,8 +665,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n5100_defconfig b/arch/arm/configs/cyanogenmod_n5100_defconfig index fb1af40..ab51fd6 100644 --- a/arch/arm/configs/cyanogenmod_n5100_defconfig +++ b/arch/arm/configs/cyanogenmod_n5100_defconfig @@ -710,8 +710,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n5110_defconfig b/arch/arm/configs/cyanogenmod_n5110_defconfig index 7775d3f..0e94537 100644 --- a/arch/arm/configs/cyanogenmod_n5110_defconfig +++ b/arch/arm/configs/cyanogenmod_n5110_defconfig @@ -712,8 +712,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index 586f708..2b6784b 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -714,8 +714,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n7000_defconfig b/arch/arm/configs/cyanogenmod_n7000_defconfig index 0ac33a0..874c7f3 100644 --- a/arch/arm/configs/cyanogenmod_n7000_defconfig +++ b/arch/arm/configs/cyanogenmod_n7000_defconfig @@ -685,7 +685,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set # CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set -# CONFIG_VMWARE_MVP is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n7100_defconfig b/arch/arm/configs/cyanogenmod_n7100_defconfig index c1c52d9..cda07f5 100644 --- a/arch/arm/configs/cyanogenmod_n7100_defconfig +++ b/arch/arm/configs/cyanogenmod_n7100_defconfig @@ -688,8 +688,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n8000_defconfig b/arch/arm/configs/cyanogenmod_n8000_defconfig index a3b96e1..e4e8e12 100644 --- a/arch/arm/configs/cyanogenmod_n8000_defconfig +++ b/arch/arm/configs/cyanogenmod_n8000_defconfig @@ -684,8 +684,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_n8013_defconfig b/arch/arm/configs/cyanogenmod_n8013_defconfig index ba974d2..9f749f2 100644 --- a/arch/arm/configs/cyanogenmod_n8013_defconfig +++ b/arch/arm/configs/cyanogenmod_n8013_defconfig @@ -663,8 +663,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_t0lte_defconfig b/arch/arm/configs/cyanogenmod_t0lte_defconfig index 3c850ac..113daa0 100755 --- a/arch/arm/configs/cyanogenmod_t0lte_defconfig +++ b/arch/arm/configs/cyanogenmod_t0lte_defconfig @@ -687,8 +687,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig index b6ac9f5..e63b06b 100755 --- a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig +++ b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig @@ -687,8 +687,6 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y -# CONFIG_VMWARE_MVP is not set -# CONFIG_VMWARE_MVP_DEBUG is not set # # Boot options diff --git a/arch/arm/mvp/Kconfig b/arch/arm/mvp/Kconfig deleted file mode 100644 index 4f2c5c7..0000000 --- a/arch/arm/mvp/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config VMWARE_MVP - bool "Build VMware Mobile Virtualization Platform modules" - select MODULES - select MODULE_UNLOAD - select SYSFS - select NAMESPACES - select NET_NS - select INET - select IPV6 - select TUN - select NETFILTER - help - Say Y here to enable the building of kernel modules - for VMware's Mobile Virtualization Platform - -config VMWARE_MVP_DEBUG - bool "Enable debug for VMware Mobile Virtualization Platform modules" - depends on VMWARE_MVP - select IKCONFIG - select IKCONFIG_PROC - help - Say Y here to enable debug on kernel modules - for VMware's Mobile Virtualization Platform. - This should be enabled for eng or userdebug builds. diff --git a/arch/arm/mvp/Makefile b/arch/arm/mvp/Makefile deleted file mode 100644 index cd38d75..0000000 --- a/arch/arm/mvp/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-y += mvpkm/ -obj-y += commkm/ -obj-y += pvtcpkm/ diff --git a/arch/arm/mvp/commkm/COPYING b/arch/arm/mvp/commkm/COPYING deleted file mode 100644 index 10828e0..0000000 --- a/arch/arm/mvp/commkm/COPYING +++ /dev/null @@ -1,341 +0,0 @@ - - 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/arch/arm/mvp/commkm/Kbuild b/arch/arm/mvp/commkm/Kbuild deleted file mode 100644 index de43a5c..0000000 --- a/arch/arm/mvp/commkm/Kbuild +++ /dev/null @@ -1,9 +0,0 @@ -# Warning: autogenerated -obj-m := commkm.o -commkm-objs := check_kconfig.o comm_ev_kernel.o comm.o comm_os_linux.o comm_os_mod_linux.o comm_svc.o comm_transp_mvp.o - -ccflags-y += -fno-pic -fno-dwarf2-cfi-asm -march=armv7-a -D__linux__ -ccflags-y += -DCOMM_BUILDING_SERVER -ccflags-y += -mfpu=neon -DIN_MODULE -DGPLED_CODE -ccflags-y += --std=gnu89 -O2 -g2 -ggdb -mapcs -fno-optimize-sibling-calls -mno-sched-prolog -ccflags-$(CONFIG_VMWARE_MVP_DEBUG) += -DMVP_DEBUG diff --git a/arch/arm/mvp/commkm/Makefile b/arch/arm/mvp/commkm/Makefile deleted file mode 100644 index 16eb389..0000000 --- a/arch/arm/mvp/commkm/Makefile +++ /dev/null @@ -1 +0,0 @@ -# Warning: autogenerated diff --git a/arch/arm/mvp/commkm/check_kconfig.c b/arch/arm/mvp/commkm/check_kconfig.c deleted file mode 100644 index 0867d74..0000000 --- a/arch/arm/mvp/commkm/check_kconfig.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * @brief Check for required kernel configuration - * - * Check to make sure that the kernel options that the MVP hypervisor requires - * have been enabled in the kernel that this kernel module is being built - * against. - */ -#include - -/* - * Minimum kernel version - * - network namespace support is only really functional starting in 2.6.29 - * - Android Gingerbread requires 2.6.35 - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -#error "MVP requires a host kernel newer than 2.6.35" -#endif - -/* module loading ability */ -#ifndef CONFIG_MODULES -#error "MVP requires kernel loadable module support be enabled (CONFIG_MODULES)" -#endif -#ifndef CONFIG_MODULE_UNLOAD -#error "MVP requires kernel module unload support be enabled (CONFIG_MODULE_UNLOAD)" -#endif - -/* sysfs */ -#ifndef CONFIG_SYSFS -#error "MVP requires sysfs support (CONFIG_SYSFS)" -#endif - -/* network traffic isolation */ -#ifndef CONFIG_NAMESPACES -#error "MVP networking support requires namespace support (CONFIG_NAMESPACES)" -#endif -#ifndef CONFIG_NET_NS -#error "MVP networking support requires Network Namespace support to be enabled (CONFIG_NET_NS)" -#endif - -/* TCP/IP networking */ -#ifndef CONFIG_INET -#error "MVP networking requires IPv4 support (CONFIG_INET)" -#endif -#ifndef CONFIG_IPV6 -#error "MVP networking requires IPv6 support (CONFIG_IPV6)" -#endif - -/* VPN support */ -#if !defined(CONFIG_TUN) && !defined(CONFIG_TUN_MODULE) -#error "MVP VPN support requires TUN device support (CONFIG_TUN)" -#endif - -#if !defined(CONFIG_NETFILTER) && !defined(PVTCP_DISABLE_NETFILTER) -#error "MVP networking support requires netfilter support (CONFIG_NETFILTER)" -#endif - -/* Force /proc/config.gz support for eng/userdebug builds */ -#ifdef MVP_DEBUG -#if !defined(CONFIG_IKCONFIG) || !defined(CONFIG_IKCONFIG_PROC) -#error "MVP kernel /proc/config.gz support required for debuggability (CONFIG_IKCONFIG_PROC)" -#endif -#endif - -/* Sanity check we're only dealing with the memory hotplug + migrate and/or - * compaction combo */ -#ifdef CONFIG_MIGRATION -#if defined(CONFIG_NUMA) || defined(CONFIG_CPUSETS) || defined(CONFIG_MEMORY_FAILURE) -#error "MVP not tested with migration features other than CONFIG_MEMORY_HOTPLUG and CONFIG_COMPACTION" -#endif -#endif diff --git a/arch/arm/mvp/commkm/comm.c b/arch/arm/mvp/commkm/comm.c deleted file mode 100644 index 8fd591c..0000000 --- a/arch/arm/mvp/commkm/comm.c +++ /dev/null @@ -1,1457 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Communication functions based on transport functionality. - */ - -#include "comm.h" -#include "comm_transp_impl.h" - - -/* Constant and macro definitions */ - -#if defined(COMM_INSTRUMENT) -static CommOSAtomic commMaxCoalesceSize; -static CommOSAtomic commPacketsReceived; -static CommOSAtomic commCommittedPacketsReceived; -static CommOSAtomic commOpCalls; -#endif - -#define COMM_DISPATCH_EXTRA_WRITER_WAKEUP 1 - -#define COMM_CHANNEL_MAX_CAPACITY 2048 -#define COMM_CHANNEL_FREE 0x0 -#define COMM_CHANNEL_INITIALIZED 0x1 -#define COMM_CHANNEL_OPENED 0x2 -#define COMM_CHANNEL_ACTIVE 0x4 -#define COMM_CHANNEL_ZOMBIE 0x8 - -#define CommIsFree(chan) \ - ((chan)->lifecycleState == COMM_CHANNEL_FREE) -#define CommIsInitialized(chan) \ - ((chan)->lifecycleState == COMM_CHANNEL_INITIALIZED) -#define CommIsOpened(chan) \ - ((chan)->lifecycleState == COMM_CHANNEL_OPENED) -#define CommIsActive(chan) \ - ((chan)->lifecycleState == COMM_CHANNEL_ACTIVE) -#define CommIsZombie(chan) \ - ((chan)->lifecycleState == COMM_CHANNEL_ZOMBIE) - -#define CommSetFree(chan) \ - SetLifecycleState(chan, COMM_CHANNEL_FREE) -#define CommSetInitialized(chan) \ - SetLifecycleState(chan, COMM_CHANNEL_INITIALIZED) -#define CommSetOpened(chan) \ - SetLifecycleState(chan, COMM_CHANNEL_OPENED) -#define CommSetActive(chan) \ - SetLifecycleState(chan, COMM_CHANNEL_ACTIVE) -#define CommSetZombie(chan) \ - SetLifecycleState(chan, COMM_CHANNEL_ZOMBIE) - -#define CommGlobalLock() CommOS_SpinLock(&commGlobalLock) -#define CommGlobalUnlock() CommOS_SpinUnlock(&commGlobalLock) -#define CommGlobalLockBH() CommOS_SpinLockBH(&commGlobalLock) -#define CommGlobalUnlockBH() CommOS_SpinUnlockBH(&commGlobalLock) - -#define DispatchTrylock(chan) CommOS_MutexTrylock(&(chan)->dispatchMutex) -#define DispatchUnlock(chan) CommOS_MutexUnlock(&(chan)->dispatchMutex) - -#define WriteLock(chan) CommOS_MutexLock(&(chan)->writeMutex) -#define WriteTrylock(chan) CommOS_MutexTrylock(&(chan)->writeMutex) -#define WriteUnlock(chan) CommOS_MutexUnlock(&(chan)->writeMutex) - -#define StateLock(chan) CommOS_MutexLock(&(chan)->stateMutex) -#define StateTrylock(chan) CommOS_MutexTrylock(&(chan)->stateMutex) -#define StateUnlock(chan) CommOS_MutexUnlock(&(chan)->stateMutex) - -#define CommHoldInit(chan) CommOS_WriteAtomic(&(chan)->holds, 0) -#define CommHold(chan) CommOS_AddReturnAtomic(&(chan)->holds, 1) -#define CommRelease(chan) CommOS_SubReturnAtomic(&(chan)->holds, 1) -#define CommIsHeld(chan) (CommOS_ReadAtomic(&(chan)->holds) > 0) - -#define PacketLenOverLimit(chan, len) \ - (((len) - sizeof (CommPacket)) > ((chan)->transpArgs.capacity / 4)) - - -/* - * Data structure describing the offload <-> paravirtualized module - * communication channel. - */ - -struct CommChannelPriv { - CommOSAtomic holds; // Active readers and writers - CommTranspInitArgs transpArgs; // Transport initialization arguments - CommTransp transp; // Transport handle - CommOSMutex dispatchMutex; // Dispatch mutex - CommOSMutex writeMutex; // Non-BH write mutex - CommOSMutex stateMutex; // Upper-layer state mutex - CommOSWaitQueue availableWaitQ; // Available write space wait data - unsigned int desiredWriteSpace; // Size of write space needed - const CommImpl *impl; // Implementation - unsigned int implNmbOps; // Number of implementation operations - unsigned int lifecycleState; // Lifecycle state - void *state; // Upper layer-specific state -}; - - -static volatile int running; // Initialized and running. -static CommOSWaitQueue exitWaitQ; // Exit wait queue. -static CommOSSpinlock commGlobalLock; // Global lock. - - -/* Communication channel slots. */ - -static unsigned int commChannelCapacity; // Maximum number of channels. -static unsigned int commChannelSize; // Current size of channel array. -static unsigned int commChannelAllocated; // Nmb. entries currently in use. -static struct CommChannelPriv *commChannels; // Allocated channel array. - - -/** - * @brief Callback function called when the other side created a transport - * handle to which we need to potentially attach. - * @param[in,out] transpArgs arguments used when shared memory area was created. - * @param probeData our callback data, an implementation block. - * @return 0 if successful, -1 otherwise. - * @sideeffects May allocate a channel. - */ - -static int -DefaultTranspListener(CommTranspInitArgs *transpArgs, - void *probeData) -{ - int rc = -1; - const int inBH = 1; - const CommImpl *impl; - - if (!transpArgs || !probeData) { - CommOS_Debug(("%s: NULL args [0x%p, 0x%p].\n", - __FUNCTION__, transpArgs, probeData)); - goto out; - } - - impl = probeData; - CommOS_Debug(("%s: Received attach info [%u,%u,%u:%u].\n", - __FUNCTION__, - transpArgs->capacity, transpArgs->type, - transpArgs->id.d32[0], transpArgs->id.d32[1])); - - if (impl->checkArgs(transpArgs)) { - goto out; - } - transpArgs->mode = COMM_TRANSP_INIT_ATTACH; /* Ensure we attach. */ - - /* We recognized it, so don't let others waste any time. Even if we fail. */ - - rc = 0; - if (Comm_Alloc(transpArgs, impl, inBH, NULL)) { - impl->closeNtf(impl->closeNtfData, transpArgs, inBH); - CommOS_Log(("%s: Can't allocate new channel!\n", __FUNCTION__)); - } - -out: - return rc; -} - - -/** - * @brief Sets the lifecycle state of a channel entry - * @param channel channel to update - * @param newState state to update to - */ - -static inline void -SetLifecycleState(CommChannel channel, - unsigned int newState) -{ - - channel->lifecycleState = newState; -} - - -/* Wait conditions: functions returning 1: true, 0: false, < 0: error. */ - -/** - * @brief Wait condition function to check whether module can be unloaded. - * @param arg1 dummy - * @param arg2 dummy - * @return 1 if no channels are currently allocated, 0 if there are - */ - -static int -ExitCondition(void *arg1, - void *arg2) -{ - unsigned int i; - int rc; - - (void)arg1; - (void)arg2; - CommOS_Debug(("%s: running [%d] " - "commChannelAllocated [%u] commChannelSize [%u].\n", - __FUNCTION__, running, commChannelAllocated, commChannelSize)); - rc = !running && (commChannelAllocated == 0); - if (!rc) { - for (i = 0; i < commChannelCapacity; i++) { - CommOS_Debug(("%s: channel[%u] state [0x%x].\n", - __FUNCTION__, i, commChannels[i].lifecycleState)); - } - } - return rc; -} - - -/** - * @brief Wait condition function to check available write space. - * @param arg1 pointer to CommChannel struct - * @param arg2 size argument - * @return 1 if there is enough write space, 0 if not, -ENOMEM if comm down. - */ - -static int -WriteSpaceCondition(void *arg1, - void *arg2) -{ - CommChannel channel = arg1; - - if (!CommIsActive(channel)) { - return -ENOMEM; - } - return channel->desiredWriteSpace < CommTransp_EnqueueSpace(channel->transp); -} - - -/** - * @brief Registers an implementation block used when attaching to channels - * in response to transport attach events. - * @param impl implementation block. - * @return 0 if successful, non-zero otherwise. - */ - -int -Comm_RegisterImpl(const CommImpl *impl) -{ - CommTranspListener listener = { - .probe = DefaultTranspListener, - .probeData = (void *)impl - }; - - return CommTransp_Register(&listener); -} - - -/** - * @brief Unregisters an implementation block used when attaching to channels - * in response to transport attach events. - * @param impl implementation block. - */ - -void -Comm_UnregisterImpl(const CommImpl *impl) -{ - CommTranspListener listener = { - .probe = DefaultTranspListener, - .probeData = (void *)impl - }; - - CommTransp_Unregister(&listener); -} - - -/** - * @brief Allocates and initializes comm global state. Single-threaded use. - * @param maxChannels maximum number of channels. - * @return zero if successful, non-zero otherwise. - */ - -int -Comm_Init(unsigned int maxChannels) -{ - int rc = -1; - unsigned int i; - - if (running || commChannels || - (maxChannels == 0) || (maxChannels > COMM_CHANNEL_MAX_CAPACITY)) { - goto out; - } - -#if defined(COMM_INSTRUMENT) - CommOS_WriteAtomic(&commMaxCoalesceSize, 0); - CommOS_WriteAtomic(&commPacketsReceived, 0); - CommOS_WriteAtomic(&commCommittedPacketsReceived, 0); - CommOS_WriteAtomic(&commOpCalls, 0); -#endif - - CommOS_WaitQueueInit(&exitWaitQ); - CommOS_SpinlockInit(&commGlobalLock); - commChannelCapacity = maxChannels; - commChannelAllocated = 0; - commChannels = CommOS_Kmalloc((sizeof *commChannels) * commChannelCapacity); - if (!commChannels) { - goto out; - } - - memset(commChannels, 0, (sizeof *commChannels) * commChannelCapacity); - for (i = 0; i < commChannelCapacity; i++ ) { - CommChannel channel; - - channel = &commChannels[i]; - CommHoldInit(channel); - channel->transp = NULL; - CommOS_MutexInit(&channel->dispatchMutex); - CommOS_MutexInit(&channel->writeMutex); - CommOS_MutexInit(&channel->stateMutex); - CommOS_WaitQueueInit(&channel->availableWaitQ); - channel->desiredWriteSpace = -1U; - channel->state = NULL; - CommSetFree(channel); - } - - rc = CommTransp_Init(); - if (!rc) { - commChannelSize = 0; - running = 1; - rc = 0; - } else { - CommOS_Kfree(commChannels); - } - -out: - return rc; -} - - -/** - * @brief Initiates and finishes, comm global state deallocations. - * @param timeoutMillis initialization timeout in milliseconds - * @return zero if deallocations done, non-zero if more calls are needed. - */ - -int -Comm_Finish(unsigned long long *timeoutMillis) -{ - int rc; - unsigned int i; - unsigned long long timeout; - - for (i = 0; i < commChannelSize; i++) { - Comm_Zombify(&commChannels[i], 0); - } - - running = 0; - timeout = timeoutMillis ? *timeoutMillis : 0; - /* coverity[var_deref_model] */ - rc = CommOS_Wait(&exitWaitQ, ExitCondition, NULL, NULL, &timeout); - if (rc == 1) { - /* - * Didn't time out, task wasn't interrupted, we can wrap it up.. - */ - - CommTransp_Exit(); - CommOS_Kfree(commChannels); - commChannels = NULL; - commChannelSize = 0; -#if defined(COMM_INSTRUMENT) - CommOS_Log(("%s: commMaxCoalesceSize = %lu.\n", - __FUNCTION__, - CommOS_ReadAtomic(&commMaxCoalesceSize))); - CommOS_Log(("%s: commPacketsReceived = %lu.\n", - __FUNCTION__, - CommOS_ReadAtomic(&commPacketsReceived))); - CommOS_Log(("%s: commCommittedPacketsReceived = %lu.\n", - __FUNCTION__, - CommOS_ReadAtomic(&commCommittedPacketsReceived))); - CommOS_Log(("%s: commOpCalls = %lu.\n", - __FUNCTION__, - CommOS_ReadAtomic)(&commOpCalls))); -#endif - rc = 0; - } else { - rc = -1; - } - return rc; -} - - -/** - * @brief Finds a free entry and initializes it with the information provided. - * May be called from BH. It doesn't call potentially blocking functions. - * - * @note Depending on the choice of shared memory transport (VMCI or MVP QP), - * the 'inBH' distinction is important. VMCI datagrams are received under - * some circumstances in bottom-half context, so 'inBH' should be set. This - * is not a restriction on MVP. - * - * @param transpArgs transport initialization arguments. - * @param impl implementation block. - * @param inBH non-zero if called in bottom half. - * @param[out] newChannel newly allocated channel. - * @return zero if successful, non-zero otherwise. - * @sideeffects Initializes the communications channel with given parameters - */ - -int -Comm_Alloc(const CommTranspInitArgs *transpArgs, - const CommImpl *impl, - int inBH, - CommChannel *newChannel) -{ - unsigned int i; - CommChannel channel = NULL; - int restoreSize = 0; - int modHeld = 0; - int rc = -1; - - if (inBH) { - CommGlobalLock(); - } else { - CommGlobalLockBH(); - } - - if (!running || !transpArgs || !impl) { - goto out; - } - - if (CommOS_ModuleGet(impl->owner)) { - goto out; - } - modHeld = 1; - - for (i = 0; i < commChannelSize; i++) { - /* - * Check if this channel is already allocated. We don't match against - * ANY because those channels are in the process of being opened; after - * that happens, they'll get proper IDs. - */ - - if (!CommIsFree(&commChannels[i]) && - (transpArgs->id.d64 != COMM_TRANSP_ID_64_ANY) && - (transpArgs->id.d64 == commChannels[i].transpArgs.id.d64)) { - goto out; - } - if (!channel && CommIsFree(&commChannels[i])) { - channel = &commChannels[i]; - } - } - if (!channel) { - if (commChannelSize == commChannelCapacity) { - goto out; - } - channel = &commChannels[commChannelSize]; - commChannelSize++; - restoreSize = 1; - } - - if (channel->transp) { /* Inconsistency! */ - if (restoreSize) { - commChannelSize--; - } - goto out; - } - - channel->transpArgs = *transpArgs; - channel->impl = impl; - for (i = 0; impl->operations[i]; i++) { - ; - } - channel->implNmbOps = i; - channel->desiredWriteSpace = -1U; - commChannelAllocated++; - CommSetInitialized(channel); - if (newChannel) { - *newChannel = channel; - } - rc = 0; - CommOS_ScheduleDisp(); - -out: - if (inBH) { - CommGlobalUnlock(); - } else { - CommGlobalUnlockBH(); - } - if (rc && modHeld) { - CommOS_ModulePut(impl->owner); - } - return rc; -} - - -/** - * @brief Zombifies a channel. May fail if channel isn't active. - * @param[in,out] channel channel to zombify. - * @param inBH non-zero if called in bottom half. - * @return zero if channel zombified, non-zero otherwise. - */ - -int -Comm_Zombify(CommChannel channel, - int inBH) -{ - int rc = -1; - - if (!running) { - goto out; - } - if (inBH) { - CommGlobalLock(); - } else { - CommGlobalLockBH(); - } - if (CommIsActive(channel) || CommIsOpened(channel)) { - CommSetZombie(channel); - rc = 0; - } - if (inBH) { - CommGlobalUnlock(); - } else { - CommGlobalUnlockBH(); - } - -out: - if (!rc) { - CommOS_ScheduleDisp(); - } - return rc; -} - - -/** - * @brief Reports whether a channel is active. - * @param channel channel to report on. - * @return non-zero if channel active, zero otherwise. - */ - -int -Comm_IsActive(CommChannel channel) -{ - return channel ? CommIsActive(channel) : 0; -} - - -/** - * @brief Wakes up potential writer on the channel. This function must be - * called on an active channel, with either the dispatch lock taken, or - * the channel ref count incremented. - * @param channel CommChannel structure on which potential writer waits. - */ - -static inline void -WakeUpWriter(CommChannel channel) -{ - if (WriteSpaceCondition(channel, NULL)) { - CommOS_WakeUp(&channel->availableWaitQ); - } -} - - -/** - * @brief Transport event handler for comm channels. - * @param transp transport handle. - * @param event type of event. - * @param data callback data. - * @sideeffects may put the channel into zombie state, or schedule it for I/O. - */ - -static void -TranspEventHandler(CommTransp transp, - CommTranspIOEvent event, - void *data) -{ - CommChannel channel = (CommChannel)data; - - switch (event) { - case COMM_TRANSP_IO_DETACH: - CommOS_Debug(("%s: Detach event. Zombifying channel.\n", __FUNCTION__)); - Comm_Zombify(channel, 1); - break; - - case COMM_TRANSP_IO_IN: - case COMM_TRANSP_IO_INOUT: - /* - * The dispatch threads may not have been started because either: - * a) we're not running in the CommSvc service, or - * b) the Comm client didn't create them explicitly (CommOS_StartIO()). - * - * If so, the CommOS_ScheduleDisp() call is ineffective. This is - * the intended behavior: the client obviously wants to call the Comm - * dispatch function(s) directly. - */ - - CommOS_ScheduleDisp(); - break; - - case COMM_TRANSP_IO_OUT: - CommHold(channel); - if (CommIsActive(channel)) { - WakeUpWriter(channel); - } - CommRelease(channel); - if (CommIsZombie(channel)) { - /* - * After releasing the hold on the channel, we must check if it was - * set to zombie and the dispatcher was supposed to nuke it. If the - * dispatcher had made its run while we were holding the channel, it - * gave up. So schedule it. - */ - - CommOS_ScheduleDisp(); - } - break; - - default: - CommOS_Debug(("%s: Unhandled event [%u, %p, %p].\n", - __FUNCTION__, event, transp, data)); - } -} - - -/** - * @brief Destroys upper layer state, unregisters event handlers and - * detaches from or deletes shared memory. - * @param[in,out] channel CommChannel structure to close. - */ - -static void -CommClose(CommChannel channel) -{ - const CommImpl *impl = channel->impl; - - StateLock(channel); - if (impl->stateDtor && channel->state) { - impl->stateDtor(channel->state); - } - channel->state = NULL; - StateUnlock(channel); - - CommOS_ModulePut(impl->owner); - - if (channel->transp) { - CommTransp_Close(channel->transp); - channel->transp = NULL; - } - - CommGlobalLockBH(); - CommSetFree(channel); - commChannelAllocated--; - if (channel == &commChannels[commChannelSize - 1]) { - commChannelSize--; - } - CommGlobalUnlockBH(); - if (!running && (commChannelAllocated == 0)) { - CommOS_WakeUp(&exitWaitQ); - } -} - - -/** - * @brief Allocates upper layer state, registers transport event handler - * and creates or attaches to shared memory. - * @param[in,out] channel CommChannel structure to open. - * @return zero if successful, -1 otherwise - * @sideeffects Memory may be allocated, event handlers registered and - * QP allocated or attached to. - */ - -static int -CommOpen(CommChannel channel) -{ - int rc = -1; - CommTranspEvent transpEvent = { - .ioEvent = TranspEventHandler, - .ioEventData = channel - }; - const CommImpl *impl; - - if (!channel || !CommIsInitialized(channel)) { - return rc; - } - - if (!running) { /* Ok, toggle it back to FREE. */ - goto out; - } - - impl = channel->impl; - if (impl->stateCtor) { - channel->state = impl->stateCtor(channel); - if (!channel->state) { - goto out; - } - } - - if (!CommTransp_Open(&channel->transp, &channel->transpArgs, &transpEvent)) { - rc = 0; - } else { - channel->transp = NULL; - } - -out: - if (!rc) { - CommSetOpened(channel); - } else { - CommClose(channel); - } - return rc; -} - - -/** - * @brief Retrieves a channel's transport initialization arguments. - * It doesn't lock, the caller must ensure the channel may be accessed. - * @param channel CommChannel structure to get initialization arguments from. - * @return initialization arguments used to allocate/attach to channel. - */ - -CommTranspInitArgs -Comm_GetTranspInitArgs(CommChannel channel) -{ - if (!channel) { - CommTranspInitArgs res = { .capacity = 0 }; - - return res; - } - return channel->transpArgs; -} - - -/** - * @brief Retrieves upper layer state (pointer). It doesn't lock, the caller - * must ensure the channel may be accessed. - * @param channel CommChannel structure to get state from. - * @return pointer to upper layer state. - */ - -void * -Comm_GetState(CommChannel channel) -{ - if (!channel) { - return NULL; - } - return channel->state; -} - - -/** - * @brief Main input processing function operating on a given channel. - * @param channel CommChannel structure to process. - * @return number of processed channels (0 or 1), or -1 if channel closed. - * @sideeffects Lifecycle states are transitioned to and from. Channel may - * be opened or destroyed, waiting writers may be woken up, and input - * may be handed off to operation callbacks. - */ - -int -Comm_Dispatch(CommChannel channel) -{ - int rc = 0; - int zombify = 0; - CommPacket packet; - CommPacket firstPacket; - unsigned int dataLen; -#define VEC_SIZE 32 - struct kvec vec[VEC_SIZE]; - unsigned int vecLen; - - /* - * Taking the reader mutex is safe in all cases: entries, including - * free ones, are guaranteed to have initialized mutexes and locks. - * Locking empty entries may seem wasteful, but those entries are rare. - */ - - if (DispatchTrylock(channel)) { - return 0; - } - - /* Process input and writer wake-up. */ - - if (CommIsActive(channel)) { - /* - * The entry may have transitioned to ZOMBIE, somehow. That's OK - * since it can't be freed just yet (it's currently locked). - */ - - /* Wake up any waiting writers, if necessary. */ - - WakeUpWriter(channel); - - /* Read packets, payloads. */ - CommTransp_DequeueReset(channel->transp); - - for (vecLen = 0; vecLen < VEC_SIZE; vecLen++) { - if (!running) { - break; - } - - /* Read header. */ - - rc = CommTransp_DequeueSegment(channel->transp, - &packet, sizeof packet); - if (rc <= 0) { - /* No packet (header). */ - - rc = vecLen == 0 ? 0 : 1; - break; - } -#if defined(COMM_INSTRUMENT) - CommOS_AddReturnAtomic(commPacketsReceived, 1); -#endif - if ((rc != sizeof packet) || (packet.len < sizeof packet)) { - rc = -1; /* Fatal protocol error, close down comm. */ - break; - } - rc = 1; - - /* Read payload, if any. */ - - dataLen = packet.len - sizeof packet; - if (vecLen == 0) { - /* Save header of first packet. */ - - firstPacket = packet; - if (dataLen == 0) { - /* Commit no-payload packet read and we're done. */ - - CommTransp_DequeueCommit(channel->transp); -#if defined(COMM_INSTRUMENT) - CommOS_AddReturnAtomic(&commCommittedPacketsReceived, 1); -#endif - break; - } - } else { - /* - * Check if non-equivalent packet or above coalescing limit. - * If so, don't commit the read. - */ - - if (memcmp(&packet.opCode, &firstPacket.opCode, - sizeof packet - offsetof(CommPacket, opCode)) || - PacketLenOverLimit(channel, firstPacket.len + dataLen)) { - break; - } - } - - if (dataLen == 0) { - /* - * Received equivalent packet with zero-sized payload. This may - * happen in certain cases, such as pvtcp forwarding zero-sized - * datagrams. So don't break the loop, but keep going for as - * along as we can. - */ - - vec[vecLen].iov_base = NULL; - goto dequeueCommit; - } - - /* The packet has a payload (dataLen > 0). */ - - if (!(vec[vecLen].iov_base = channel->impl->dataAlloc(dataLen))) { - /* - * We treat out-of-(net?-)memory errors as "nothing to read". - * Memory pressure may either subside, in which case a future - * read may be successful, or be severe enough for the kernel - * to oops, anyway. Leave packet uncommitted. - */ - - CommOS_Debug(("%s: COULD NOT ALLOC PAYLOAD BYTES!\n", - __FUNCTION__)); - rc = vecLen == 0 ? 0 : 1; - break; - } - - /* Read payload and commit (packet and payload). */ - - rc = CommTransp_DequeueSegment(channel->transp, - vec[vecLen].iov_base, dataLen); - if (rc != dataLen) { - channel->impl->dataFree(vec[vecLen].iov_base); - CommOS_Log(("%s: BOOG -- COULD NOT DEQUEUE PAYLOAD! [%d != %u]", - __FUNCTION__, rc, dataLen)); - rc = -1; /* Fatal protocol error, close down comm. */ - break; - } - rc = 1; - -dequeueCommit: - CommTransp_DequeueCommit(channel->transp); -#if defined(COMM_INSTRUMENT) - CommOS_AddReturnAtomic(&commCommittedPacketsReceived, 1); -#endif - vec[vecLen].iov_len = dataLen; - if (vecLen > 0) { - firstPacket.len += dataLen; - if (packet.flags) { - /* Update to latest flags _iff_ latter non-zero. */ - - firstPacket.flags = packet.flags; - } - } -#if defined(COMM_INSTRUMENT) - if (firstPacket.len > - CommOS_ReadAtomic(&commMaxCoalesceSize)) { - CommOS_WriteAtomic(&commMaxCoalesceSize, firstPacket.len); - } -#endif - if (COMM_OPF_TEST_ERR(packet.flags)) { - /* If error bit is set, we're done (no more coalescing). */ - - vecLen++; - break; - } - } - - if (rc <= 0) { - if (rc < 0) { - zombify = 1; - rc = 1; - } - goto outUnlockAndFreeIovec; - } - -#if defined(COMM_DISPATCH_EXTRA_WRITER_WAKEUP) - /* Check again if we need to wake up any writers. */ - - WakeUpWriter(channel); -#endif - - if (firstPacket.opCode >= channel->implNmbOps) { - CommOS_Debug(("%s: Ignoring illegal opCode [%u]!\n", - __FUNCTION__, (unsigned int)firstPacket.opCode)); - CommOS_Debug(("%s: Max opCode: %u\n", - __FUNCTION__, channel->implNmbOps)); - goto outUnlockAndFreeIovec; - } - - /* - * NOTE: - * DispatchUnlock() _must_ be called from the operation callback. - * The reason for doing so is that, for better scalability, we want - * it released as soon as possible, BUT: - * - releasing it here, before calling into the operation, doesn't - * let the latter coordinate its own lock acquisition, such as - * potential socket or state locks. - * - alternatively, always releasing the dispatch lock after the - * operation completes, ties up the channel and imposes too much - * serialization between sockets. - * - to prevent the channel from being torn down while an operation - * is in flight (and potentially having released the dispatch lock), - * we increment the ref count on the channel and then release it - * after the function returns. - */ - -#if defined(COMM_INSTRUMENT) - CommOS_AddReturnAtomic(&commOpCalls, 1); -#endif - - CommHold(channel); - channel->impl->operations[firstPacket.opCode](channel, channel->state, - &firstPacket, vec, vecLen); - CommRelease(channel); - goto out; /* No unlocking, see comment above. */ - } - - /* Process state changes. */ - - if (CommIsZombie(channel) && !CommIsHeld(channel)) { - CommTranspInitArgs transpArgs = channel->transpArgs; - void (*closeNtf)(void *, - const CommTranspInitArgs *, - int inBH) = channel->impl->closeNtf; - void *closeNtfData = channel->impl->closeNtfData; - - while (WriteTrylock(channel)) { - /* Take the write lock; kick writers out if necessary. */ - - CommOS_Debug(("%s: Kicking writers out...\n", __FUNCTION__)); - CommOS_WakeUp(&channel->availableWaitQ); - } - WriteUnlock(channel); - - CommOS_Debug(("%s: Nuking zombie channel.\n", __FUNCTION__)); - CommClose(channel); - if (closeNtf) { - closeNtf(closeNtfData, &transpArgs, 0); - } - rc = -1; - } else if (CommIsInitialized(channel) && - (channel->impl->openAtMillis <= - CommOS_GetCurrentMillis())) { - if (!CommOpen(channel)) { - if (channel->transpArgs.mode == COMM_TRANSP_INIT_CREATE) { - /* - * If the attach side doesn't get notified, the entry will - * time out in OPENED and will be collected. - * Note that during the CommOpen(Transp_Open) call, the IDs - * in the transpArgs may have changed. Use those. - */ - - CommTransp_Notify(&channel->impl->ntfCenterID, - &channel->transpArgs); - } else { /* Attach mode */ - packet.len = sizeof packet; - packet.opCode = 0xff; - packet.flags = 0x00; - - /* - * Send out control packet, attach ack, and transition straight - * to ACTIVE. - */ - - rc = CommTransp_EnqueueAtomic(channel->transp, - &packet, sizeof packet); - if (rc == sizeof packet) { - /* Guard against potentially concurrent zombify. */ - - CommGlobalLockBH(); - if (CommIsOpened(channel)) { - CommOS_Debug(("%s: Sent attach ack. Activating channel.\n", - __FUNCTION__)); - CommSetActive(channel); - } - CommGlobalUnlockBH(); - } - } - rc = 1; - } - } else if (CommIsOpened(channel) && - (channel->transpArgs.mode == COMM_TRANSP_INIT_CREATE)) { - /* - * Get control packet (opCode == 0xff), attach ack (flags == 0x0), - * or check whether the channel timed out in OPENED. - */ - - rc = CommTransp_DequeueAtomic(channel->transp, - &packet, sizeof packet); - if (rc == sizeof packet) { - void (*activateNtf)(void *activateNtfData, CommChannel) = NULL; - void *activateNtfData = NULL; - - /* Guard against potentially concurrent zombify. */ - - CommGlobalLockBH(); - if (CommIsOpened(channel) && - (packet.opCode == 0xff) && (packet.flags == 0x0)) { - activateNtf = channel->impl->activateNtf; - activateNtfData = channel->impl->activateNtfData; - - CommSetActive(channel); - CommOS_Debug(("%s: Received attach ack. Activating channel.\n", - __FUNCTION__)); - } - CommHold(channel); - CommGlobalUnlockBH(); - - if (activateNtf) { - /* The callback must be short and 'put' the channel when done. */ - - activateNtf(activateNtfData, channel); - } else { - /* Don't forget to put back the channel if no activate callback. */ - - CommRelease(channel); - } - } else if ((channel->impl->openTimeoutAtMillis <= - CommOS_GetCurrentMillis()) || - !running) { - zombify = 1; - CommOS_Debug(("%s: Zombifying expired opened channel.\n", - __FUNCTION__)); - } - rc = 1; - } - DispatchUnlock(channel); - -out: - if (zombify) { - Comm_Zombify(channel, 0); - } - return rc; - -outUnlockAndFreeIovec: - DispatchUnlock(channel); - for ( ; vecLen; ) { - if (vec[--vecLen].iov_base) { - channel->impl->dataFree(vec[vecLen].iov_base); - vec[vecLen].iov_base = NULL; - } - vec[vecLen].iov_len = 0; - } - goto out; -#undef VEC_SIZE -} - - -/** - * @brief Main input processing function operating on all channels. - * @return number of processed channels. - * @sideeffects Lifecycle states are transitioned to and from. Channels may - * be opened and destroyed, waiting writers may be woken up, and input - * may be handed off to operation callbacks. - */ - -unsigned int -Comm_DispatchAll(void) -{ - unsigned int i; - unsigned int hits; - - for (hits = 0, i = 0; running && (i < commChannelSize); i++) { - hits += !!Comm_Dispatch(&commChannels[i]); - } - return hits; -} - - -/** - * @brief Writes a fully formatted packet (containing payload data, if - * applicable) to the specified channel. - * - * The operation may block until enough write space is available, but no - * more than the specified interval. The operation either writes the full - * amount of bytes, or it fails. Warning: callers must _not_ use the - * _Lock/_Unlock functions to bracket calls to this function. - * @param[in,out] channel channel to write to. - * @param packet packet to write. - * @param[in,out] timeoutMillis interval in milliseconds to wait. - * @return number of bytes written, 0 if it times out, -1 error. - * @sideeffects Data may be written to the channel. - */ - -int -Comm_Write(CommChannel channel, - const CommPacket *packet, - unsigned long long *timeoutMillis) -{ - int rc = -1; - int zombify; - - if (!channel || !timeoutMillis || - !packet || (packet->len < sizeof *packet)) { - return rc; - } - - zombify = (*timeoutMillis >= COMM_MAX_TO); - - WriteLock(channel); - if (!CommIsActive(channel)) { - goto out; - } - - CommTransp_EnqueueReset(channel->transp); - channel->desiredWriteSpace = packet->len; - rc = CommOS_DoWait(&channel->availableWaitQ, WriteSpaceCondition, - channel, NULL, timeoutMillis, - (*timeoutMillis != COMM_MAX_TO_UNINT)); - channel->desiredWriteSpace = -1U; - - if (rc) { /* Don't zombify, if it didn't time out. */ - zombify = 0; - } - if (rc == 1) { /* Enough write space, enqueue the packet. */ - rc = CommTransp_EnqueueAtomic(channel->transp, packet, packet->len); - if (rc != packet->len) { - zombify = 1; - rc = -1; /* Fatal protocol error. */ - } - } - -out: - WriteUnlock(channel); - if (zombify) { - Comm_Zombify(channel, 0); - } - return rc; -} - - -/** - * @brief Writes a packet and associated payload data to the specified channel. - * The operation may block until enough write space is available, but - * not more than the specified interval. - * The operation either writes the full amount of bytes, or it fails. - * If there is not enough data in the vector, padding will be added to - * reach the specified packet length, if the flags parameter requires it. - * Users may call this function successively to write several packets - * from large {io|k}vecs, when the flags parameter indicates it. If this - * is the case, the packet header needs to be updated accordingly in - * between calls, for the different (total) lengths. - * Warning: callers must _not_ use the _Lock/_Unlock functions to bracket - * calls to this function. - * @param[in,out] channel the specified channel. - * @param packet packet to write. - * @param[in,out] vec kvec to write from. - * @param[in,out] vecLen length of kvec. - * @param[in,out] timeoutMillis interval in milliseconds to wait. - * @param[in,out] iovOffset must be set to 0 before first call (internal cookie) - * @return number of bytes written, 0 if it timed out, -1 error. - * @sideeffects data may be written to the channel. - */ - -int -Comm_WriteVec(CommChannel channel, - const CommPacket *packet, - struct kvec **vec, - unsigned int *vecLen, - unsigned long long *timeoutMillis, - unsigned int *iovOffset) -{ - int rc; - int zombify; - unsigned int dataLen; - unsigned int vecDataLen; - unsigned int vecNdx; - unsigned int iovLen; - void *iovBase; - - if (!channel || !timeoutMillis || !iovOffset || - !packet || (packet->len < sizeof *packet) || - (((dataLen = packet->len - sizeof *packet) > 0) && - (!*vec || !*vecLen))) { - return -1; - } - - zombify = (*timeoutMillis >= COMM_MAX_TO); - - WriteLock(channel); - if (!CommIsActive(channel)) { - rc = -1; - goto out; - } - - CommTransp_EnqueueReset(channel->transp); - channel->desiredWriteSpace = packet->len; - rc = CommOS_DoWait(&channel->availableWaitQ, WriteSpaceCondition, - channel, NULL, timeoutMillis, - (*timeoutMillis != COMM_MAX_TO_UNINT)); - channel->desiredWriteSpace = -1U; - - if (rc) { /* Don't zombify, if it didn't time out. */ - zombify = 0; - } - if (rc == 1) { /* Enough write space, enqueue the packet. */ - iovLen = 0; - rc = CommTransp_EnqueueSegment(channel->transp, packet, sizeof *packet); - if (rc != sizeof *packet) { - zombify = 1; - rc = -1; /* Fatal protocol error. */ - goto out; - } - - if (dataLen > 0) { - int done = 0; - - for (vecDataLen = 0, vecNdx = 0; vecNdx < *vecLen; vecNdx++) { - if (vecNdx) { - *iovOffset = 0; - } - iovLen = (*vec)[vecNdx].iov_len - *iovOffset; - iovBase = (*vec)[vecNdx].iov_base + *iovOffset; - - if (!iovLen) { - continue; - } - - vecDataLen += iovLen; - if (vecDataLen >= dataLen) { - iovLen -= (vecDataLen - dataLen); - done = 1; - } - - rc = CommTransp_EnqueueSegment(channel->transp, iovBase, iovLen); - if (rc != iovLen) { - zombify = 1; - rc = -1; /* Fatal protocol error, close down comm. */ - goto out; - } - - if (done) { - CommTransp_EnqueueCommit(channel->transp); - if (vecDataLen == dataLen) { - vecNdx++; - *iovOffset = 0; - } else { - *iovOffset += iovLen; - } - *vecLen -= vecNdx; - *vec += vecNdx; - break; - } - } - - if (!done) { - /* - * We exhausted all the bytes in the given vector, but total length - * in the packet header is more than we sent (was available). - * If so, we pad by sending zero bytes to reach length required. - */ - - static char pad[1024]; - unsigned int delta; - unsigned int toSend; - - while (vecDataLen < dataLen) { - delta = dataLen - vecDataLen; - toSend = delta <= sizeof pad ? delta : sizeof pad; - if (toSend == delta) { - done = 1; - } - vecDataLen += toSend; - - rc = CommTransp_EnqueueSegment(channel->transp, pad, toSend); - if (rc != toSend) { - zombify = 1; - rc = -1; /* Fatal protocol error, close down comm. */ - goto out; - } - - if (done) { - CommTransp_EnqueueCommit(channel->transp); - *vec = NULL; - *vecLen = 0; - *iovOffset = 0; - break; - } - } - } - } else { - CommTransp_EnqueueCommit(channel->transp); - } - rc = (int)packet->len; - } else { - CommOS_Debug(("%s: timed out...\n", __FUNCTION__)); - } - -out: - WriteUnlock(channel); - if (zombify) { - Comm_Zombify(channel, 0); - } - return rc; -} - - -/** - * @brief Releases channel ref count. This function is exported for the upper - * layer's 'activateNtf' callback which may be run asynchronously. The - * callback is protected from concurrent channel releases until it calls - * this function. - * @param[in,out] channel CommChannel structure to release. - */ - -void -Comm_Put(CommChannel channel) -{ - if (channel) { - CommRelease(channel); - } -} - - -/** - * @brief Uses the read lock. This function is exported for the upper layer - * such that it can order acquisition of a different lock (socket) with - * the release of the dispatch lock. - * @param[in,out] channel CommChannel structure to unlock. - */ - -void -Comm_DispatchUnlock(CommChannel channel) -{ - if (channel) { - DispatchUnlock(channel); - } -} - - -/** - * @brief Lock the channel for upper layer state. - * This function is exported for the upper layer to ensure that channel - * isn't closed while updating the layer state. Operations using this - * function are expected to be short, since unlike the _Write functions, - * these callers cannot be signaled. - * @param[in,out] channel CommChannel structure to lock. - * @return zero if successful, -1 otherwise. - */ - -int -Comm_Lock(CommChannel channel) -{ - if (!channel) { - return -1; - } - StateLock(channel); - if (!CommIsActive(channel) && !CommIsZombie(channel)) { - StateUnlock(channel); - return -1; - } - return 0; -} - - -/** - * @brief Uses the writer lock. This function is exported for the upper layer - * to ensure that channel isn't closed while updating the layer state. - * See Comm_Lock for details). - * @param[in,out] channel CommChannel structure to unlock. - */ - -void -Comm_Unlock(CommChannel channel) -{ - if (channel) { - StateUnlock(channel); - } -} - - -/** - * @brief Requests events be posted in-line after the function completes. - * @param channel channel object. - * @return current number of requests for inline event posting, or -1 on error. - */ - -unsigned int -Comm_RequestInlineEvents(CommChannel channel) -{ - if (channel->transp) { - return CommTransp_RequestInlineEvents(channel->transp); - } else { - return (unsigned int)-1; - } -} - - -/** - * @brief Requests events be posted out-of-band after the function completes. - * @param channel channel object. - * @return current number of requests for inline event posting, or -1 on error. - */ - -unsigned int -Comm_ReleaseInlineEvents(CommChannel channel) -{ - if (channel->transp) { - return CommTransp_ReleaseInlineEvents(channel->transp); - } else { - return (unsigned int)-1; - } -} diff --git a/arch/arm/mvp/commkm/comm.h b/arch/arm/mvp/commkm/comm.h deleted file mode 100644 index 8291ae4..0000000 --- a/arch/arm/mvp/commkm/comm.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Communication functions based on queue pair transport APIs. - * - * Comm is a shared memory-based mechanism that facilitates the implementation - * of kernel components that require host-to-guest, or guest-to-guest - * communication. - * This facility assumes the availability of a minimal shared memory queue pair - * implementation, such as MVP queue pairs or VMCI queue pairs. The latter must - * provide primitives for queue pair creation and destruction, and reading and - * writing from/to queue pairs. - * Comm assumes that the queue pair (transport) layer is not concerned with - * multi-threading, locking or flow control, and does not require such features. - */ - -#ifndef _COMM_H_ -#define _COMM_H_ - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "comm_os.h" -#include "comm_transp.h" - - -/* Default/maximum Comm timeouts (in milliseconds). */ -#define COMM_MAX_TO 60000ULL -#define COMM_MAX_TO_UNINT (COMM_MAX_TO + 1) - -#define COMM_OPF_SET_ERR(flags) ((flags) |= 128) -#define COMM_OPF_CLEAR_ERR(flags) ((flags) &= 127) -#define COMM_OPF_TEST_ERR(flags) ((flags) & 128) - -#define COMM_OPF_SET_VAL(flags, val) ((flags) |= ((val) & 127)) -#define COMM_OPF_GET_VAL(flags) ((flags) & 127) - -/** - * Packet (header) structure. - * NB: Do not change this structure, especially the first three fields; there - * will be consequences. It may be extended, but it's not recommended: all - * operations carry this header, so it's better kept in its minimal form. - */ - -typedef struct CommPacket { - unsigned int len; // Total length - unsigned char flags; // Operation flags - unsigned char opCode; // Operation to call - unsigned short data16; // Auxiliary data - unsigned long long data64; - unsigned long long data64ex; - union { - struct { - unsigned int data32; - unsigned int data32ex; - }; - unsigned long long data64ex2; - }; -} CommPacket; - - -/* Opaque structure representing a communication channel. */ - -struct CommChannelPriv; -typedef struct CommChannelPriv *CommChannel; - - -/* Input operations associated with a comm channel. */ - -typedef void (*CommOperationFunc)(CommChannel channel, - void *state, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen); - - -/* Helper macros */ - -#define COMM_DEFINE_OP(funcName) \ -void \ -funcName(CommChannel channel, \ - void *state, \ - CommPacket *packet, \ - struct kvec *vec, \ - unsigned int vecLen) - - -/* Comm-based implementations. */ - -typedef struct CommImpl { - struct module *owner; - int (*checkArgs)(CommTranspInitArgs *transpArgs); - void *(*stateCtor)(CommChannel channel); - void (*stateDtor)(void *state); - void *(*dataAlloc)(unsigned int dataLen); - void (*dataFree)(void *data); - const CommOperationFunc *operations; - void (*closeNtf)(void *closeNtfData, - const CommTranspInitArgs *transpArgs, - int inBH); - void *closeNtfData; - void (*activateNtf)(void *activateNtfData, - CommChannel channel); - void *activateNtfData; - unsigned long long openAtMillis; - unsigned long long openTimeoutAtMillis; - CommTranspID ntfCenterID; -} CommImpl; - - -int Comm_Init(unsigned int maxChannels); -int Comm_Finish(unsigned long long *timeoutMillis); -int Comm_RegisterImpl(const CommImpl *impl); -void Comm_UnregisterImpl(const CommImpl *impl); -int Comm_IsActive(CommChannel channel); -CommTranspInitArgs Comm_GetTranspInitArgs(CommChannel channel); -void *Comm_GetState(CommChannel channel); -int Comm_Dispatch(CommChannel channel); -unsigned int Comm_DispatchAll(void); -void Comm_Put(CommChannel channel); -void Comm_DispatchUnlock(CommChannel channel); -int Comm_Lock(CommChannel channel); -void Comm_Unlock(CommChannel channel); -int Comm_Zombify(CommChannel channel, int inBH); - -int -Comm_Alloc(const CommTranspInitArgs *transpArgs, - const CommImpl *impl, - int inBH, - CommChannel *newChannel); - - -int -Comm_Write(CommChannel channel, - const CommPacket *packet, - unsigned long long *timeoutMillis); - -int -Comm_WriteVec(CommChannel channel, - const CommPacket *packet, - struct kvec **vec, - unsigned int *vecLen, - unsigned long long *timeoutMillis, - unsigned int *iovOffset); - -unsigned int Comm_RequestInlineEvents(CommChannel channel); -unsigned int Comm_ReleaseInlineEvents(CommChannel channel); - -#endif // _COMM_H_ diff --git a/arch/arm/mvp/commkm/comm_ev.h b/arch/arm/mvp/commkm/comm_ev.h deleted file mode 100644 index bf629c3..0000000 --- a/arch/arm/mvp/commkm/comm_ev.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief various comm event signaling types and signatures - */ - -#ifndef _COMM_EV_H -#define _COMM_EV_H - -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_MODULE -#include "include_check.h" - -/** - * @name Identifiers of comm event signaling class methods - * @{ - */ -#define MVP_COMM_EV_SIGNATURE 0x4d4d4f43 ///< 'COMM' -#define MVP_COMM_EV_SIGNAL (MVP_OBJECT_CUSTOM_BASE + 0) ///< Signal host -#define MVP_COMM_EV_READ_EVENT_DATA (MVP_OBJECT_CUSTOM_BASE + 1) ///< read event data -#define MVP_COMM_EV_LAST (MVP_OBJECT_CUSTOM_BASE + 2) ///< Number of methods -/**@}*/ - -typedef struct CommEvent { - CommTranspID id; - CommTranspIOEvent event; -} CommEvent; - -#endif diff --git a/arch/arm/mvp/commkm/comm_ev_kernel.c b/arch/arm/mvp/commkm/comm_ev_kernel.c deleted file mode 100644 index 0701945..0000000 --- a/arch/arm/mvp/commkm/comm_ev_kernel.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Comm event signaling, host kernel side. - */ - -#include - -#include "mvp_types.h" -#include "comm_os.h" -#include "comm_transp_impl.h" -#include "mksck_sockaddr.h" -#include "comm_ev.h" -#include "mvpkm_comm_ev.h" - -static struct socket *sock; - -/** - * @brief Raises a transport event on the provided event ID (address). This - * function is called from a comm_transp provider, such as comm_transp_mvp, - * when it needs to signal an event on a given channel. - * @param targetEvID opaque event channel ID (interpreted by implementation). - * @param transpID ID of transport to signal. - * @param eventType event type to raise. - * @return 0 if successful, -1 otherwise. - */ - -int -CommTranspEvent_Raise(unsigned int targetEvID, // unused - CommTranspID *transpID, - CommTranspIOEvent eventType) -{ - struct sockaddr_mk guestAddr; - struct msghdr msg; - struct kvec vec[1]; - int rc; - CommEvent event; - - if (!transpID) { - return -1; - } - - guestAddr.mk_family = AF_MKSCK; - guestAddr.mk_addr.addr = Mksck_AddrInit(transpID->d32[0], MKSCK_PORT_COMM_EV); - - memset(&msg, 0, sizeof (struct msghdr)); - msg.msg_name = &guestAddr; - msg.msg_namelen = sizeof (guestAddr); - - event.id = *transpID; - event.event = eventType; - - vec[0].iov_base = &event; - vec[0].iov_len = sizeof (CommEvent); - - rc = kernel_sendmsg(sock, - &msg, - vec, - 1, - sizeof (CommEvent)); - rc = (rc < 0) ? -1 : 0; - return rc; -} - - -/** - * @brief Performs one-time, global initialization of event provider. - * @return 0 if successful, -1 otherwise. - */ -int -CommTranspEvent_Init(void) -{ - struct sockaddr_mk addr = { AF_MKSCK, { .addr = MKSCK_ADDR_UNDEF } }; - int rc; - - rc = sock_create_kern(AF_MKSCK, SOCK_DGRAM, 0, &sock); - if (rc < 0) { - goto out; - } - - rc = kernel_bind(sock, (struct sockaddr *) &addr, sizeof addr); - if (rc < 0) { - sock_release(sock); - sock = NULL; - goto out; - } - - Mvpkm_CommEvRegisterProcessCB(CommTranspEvent_Process); - -out: - if (rc) { - CommOS_Log(("%s: Failed to initialize transport event signaling\n", - __FUNCTION__)); - } else { - CommOS_Log(("%s: Transport event signaling initialization successful\n", - __FUNCTION__)); - } - return rc; -} - - -/** - * @brief Performs global clean-up of event provider. - */ - -void -CommTranspEvent_Exit(void) -{ - Mvpkm_CommEvUnregisterProcessCB(); - if (sock) { - sock_release(sock); - sock = NULL; - } - - CommOS_Debug(("%s: done.\n", __FUNCTION__)); -} diff --git a/arch/arm/mvp/commkm/comm_os.h b/arch/arm/mvp/commkm/comm_os.h deleted file mode 100644 index f98c8d4..0000000 --- a/arch/arm/mvp/commkm/comm_os.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Cross-platform base type definitions and function declarations. - * Includes OS-specific base type definitions and function declarations. - */ - -#ifndef _COMM_OS_H_ -#define _COMM_OS_H_ - -/* For-ever timeout constant (in milliseconds). */ -#define COMM_OS_4EVER_TO ((unsigned long long)(~0UL >> 1)) - -/* Condition function prototype. Returns 1: true, 0: false, < 0: error code. */ -typedef int (*CommOSWaitConditionFunc)(void *arg1, void *arg2); - -/* Dispatch function prototype. Called by input (dispatch) kernel threads. */ -typedef unsigned int (*CommOSDispatchFunc)(void); - -/* Module initialization and exit callback functions. */ -extern int (*commOSModInit)(void *args); -extern void (*commOSModExit)(void); - -/* Macro to assign Init and Exit callbacks. */ -#define COMM_OS_MOD_INIT(init, exit) \ - int (*commOSModInit)(void *args) = init; \ - void (*commOSModExit)(void) = exit - - -/* - * OS-specific implementations must provide the following: - * 1. Types: - * CommOSAtomic - * CommOSSpinlock - * CommOSMutex - * CommOSWaitQueue - * CommOSWork - * CommOSWorkFunc - * CommOSList - * CommOSModule - * struct kvec - * - * 2. Definition, initializers: - * CommOSSpinlock_Define() - * - * 3. Functions: - * void CommOS_Debug(const char *format, ...); - * void CommOS_Log(const char *format, ...); - * void CommOS_WriteAtomic(CommOSAtomic *atomic, int val); - * int CommOS_ReadAtomic(CommOSAtomic *atomic); - * int CommOS_AddReturnAtomic(CommOSAtomic *atomic, int val); - * int CommOS_SubReturnAtomic(CommOSAtomic *atomic, int val); - * void CommOS_SpinlockInit(CommOSSpinlock *lock); - * void CommOS_SpinLockBH(CommOSSpinlock *lock); - * int CommOS_SpinTrylockBH(CommOSSpinlock *lock); - * void CommOS_SpinUnlockBH(CommOSSpinlock *lock); - * void CommOS_SpinLock(CommOSSpinlock *lock); - * int CommOS_SpinTrylock(CommOSSpinlock *lock); - * void CommOS_SpinUnlock(CommOSSpinlock *lock); - * void CommOS_MutexInit(CommOSMutex *mutex); - * void CommOS_MutexLock(CommOSMutex *mutex); - * int CommOS_MutexLockUninterruptible(CommOSMutex *mutex); - * int CommOS_MutexTrylock(CommOSMutex *mutex); - * void CommOS_MutexUnlock(CommOSMutex *mutex); - * void CommOS_WaitQueueInit(CommOSWaitQueue *wq); - * CommOS_DoWait(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc cond, - * void *condArg1, - * void *condArg2, - * unsigned long long *timeoutMillis, - * int interruptible); - * int CommOS_Wait(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc func, - * void *funcArg1, - * void *funcArg2, - * unsigned long long *timeoutMillis); - * int CommOS_WaitUninterruptible(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc func, - * void *funcArg1, - * void *funcArg2, - * unsigned long long *timeoutMillis); - * void CommOS_WakeUp(CommOSWaitQueue *wq); - * void *CommOS_KmallocNoSleep(unsigned int size); - * void *CommOS_Kmalloc(unsigned int size); - * void CommOS_Kfree(void *arg); - * void CommOS_Yield(void); - * unsigned long long CommOS_GetCurrentMillis(void); - * void CommOS_ListInit(CommOSList *list); - * int CommOS_ListEmpty(CommOSList *list); - * void CommOS_ListAdd(CommOSList *list, CommOSList *listElem); - * void CommOS_ListAddTail(CommOSList *list, CommOSList *listElem); - * void int CommOS_ListDel(CommOSList *listElem); - * Macros: - * CommOS_ListForEach(*list, *item, itemListFieldName); - * CommOS_ListForEachSafe(*list, *item, *tmp, itemListFieldName); - * void CommOS_ListSplice(CommOSList *list, CommOSList *listToAdd); - * void CommOS_ListSpliceTail(CommOSList *list, CommOSList *listToAdd); - * CommOSModule CommOS_ModuleSelf(void); - * int CommOS_ModuleGet(CommOSModule module); - * void CommOS_ModulePut(CommOSModule module); - * void CommOS_MemBarrier(void); - * - * These cannot be defined here: a) non-pointer type definitions need size - * information, and b) functions may or may not be inlined, or macros may - * be used instead. - */ - - -#ifdef __linux__ -#include "comm_os_linux.h" -#else -#error "Unsupported OS" -#endif - -/* Functions to start and stop the dispatch and aio kernel threads. */ -void CommOS_StopIO(void); -void CommOS_ScheduleDisp(void); -void CommOS_InitWork(CommOSWork *work, CommOSWorkFunc func); -int CommOS_ScheduleAIOWork(CommOSWork *work); -void CommOS_FlushAIOWork(CommOSWork *work); - -int -CommOS_StartIO(const char *dispatchTaskName, - CommOSDispatchFunc dispatchHandler, - unsigned int interval, - unsigned int maxCycles, - const char *aioTaskName); - - -#endif /* _COMM_OS_H_ */ diff --git a/arch/arm/mvp/commkm/comm_os_linux.c b/arch/arm/mvp/commkm/comm_os_linux.c deleted file mode 100644 index 74f99f5..0000000 --- a/arch/arm/mvp/commkm/comm_os_linux.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Linux-specific functions/types. - */ - -#include "comm_os.h" - -#define DISPATCH_MAX_CYCLES 8192 - -/* Type definitions */ - -typedef struct workqueue_struct CommOSWorkQueue; - - -/* Static data */ - -static volatile int running; -static int numCpus; -static CommOSWorkQueue *dispatchWQ; -static CommOSDispatchFunc dispatch; -static CommOSWork dispatchWorksNow[NR_CPUS]; -static CommOSWork dispatchWorks[NR_CPUS]; -static unsigned int dispatchInterval = 1; -static unsigned int dispatchMaxCycles = 2048; -static CommOSWorkQueue *aioWQ; - - -/** - * @brief Initializes a workqueue consisting of per-cpu kernel threads. - * @param name workqueue name - * @return workqueue handle if successful, NULL otherwise - */ - -static inline CommOSWorkQueue * -CreateWorkqueue(const char *name) -{ - return create_workqueue(name); -} - - -/** - * @brief Destroys a workqueue and stops its threads. - * @param[in,out] wq workqueue to destroy. - * @return workqueue handle is successful, NULL otherwise. - */ - -static inline void -DestroyWorkqueue(CommOSWorkQueue *wq) -{ - destroy_workqueue(wq); -} - - -/** - * @brief Force execution of a work item. - * @param[in,out] work work item to dequeue. - */ - -static inline void -FlushDelayedWork(CommOSWork *work) -{ - flush_delayed_work(work); -} - - -/** - * @brief Enqueue a work item to a workqueue for execution on a given cpu - * and after the specified interval. - * @param cpu cpu number. If negative, work item is enqueued on current cpu. - * @param[in,out] wq target work queue. - * @param[in,out] work work item to enqueue. - * @param jif delay interval. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -QueueDelayedWorkOn(int cpu, - CommOSWorkQueue *wq, - CommOSWork *work, - unsigned long jif) -{ - if (cpu < 0) { - return !queue_delayed_work(wq, work, jif) ? -1 : 0; - } else { - return !queue_delayed_work_on(cpu, wq, work, jif) ? -1 : 0; - } -} - - -/** - * @brief Enqueues a work item to a workqueue for execution on the current cpu - * and after the specified interval. - * @param[in,out] wq target work queue. - * @param[in,out] work work item to enqueue. - * @param jif delay interval. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -QueueDelayedWork(CommOSWorkQueue *wq, - CommOSWork *work, - unsigned long jif) -{ - return QueueDelayedWorkOn(-1, wq, work, jif); -} - - -/** - * @brief Cancels a queued delayed work item and synchronizes with its - * completion. - * @param[in,out] work work item to cancel - */ - -static inline void -WaitForDelayedWork(CommOSWork *work) -{ - cancel_delayed_work_sync(work); -} - - -/** - * @brief Discards work items queued to the specified workqueue. - * @param[in,out] wq work queue to flush. - */ - -static inline void -FlushWorkqueue(CommOSWorkQueue *wq) -{ - flush_workqueue(wq); -} - - -/** - * @brief Schedules dispatcher threads for immediate execution. - */ - -void -CommOS_ScheduleDisp(void) -{ - CommOSWork *work = &dispatchWorksNow[get_cpu()]; - - put_cpu(); - if (running) { - QueueDelayedWork(dispatchWQ, work, 0); - } -} - - -/** - * @brief Default delayed work callback function implementation. - * Calls the input function specified at initialization. - * @param[in,out] work work item. - */ - -static void -DispatchWrapper(CommOSWork *work) -{ - unsigned int misses; - - for (misses = 0; running && (misses < dispatchMaxCycles); ) { - /* We run for at most dispatchMaxCycles worth of channel no-ops. */ - - if (!dispatch()) { - /* No useful work was done, on any of the channels. */ - - misses++; - if ((misses % 32) == 0) { - CommOS_Yield(); - } - } else { - misses = 0; - } - } - - if (running && - (work >= &dispatchWorks[0]) && - (work <= &dispatchWorks[NR_CPUS - 1])) { - /* - * If still running _and_ this was a regular, time-based run, then - * re-arm the timer. - */ - - QueueDelayedWork(dispatchWQ, work, dispatchInterval); - } -} - - -/** - * @brief Initializes work item with specified callback function. - * @param[in,out] work work queue to initialize. - * @param func work item to initialize the queue with. - */ - -void -CommOS_InitWork(CommOSWork *work, - CommOSWorkFunc func) -{ - INIT_DELAYED_WORK(work, (work_func_t)func); -} - - -/** - * @brief Flush execution of a work item - * @param{in,out] work work item to dequeue - */ -void -CommOS_FlushAIOWork(CommOSWork *work) -{ - if (aioWQ && work) { - FlushDelayedWork(work); - } -} - - -/** - * @brief Queue a work item to the AIO workqueue. - * @param[in,out] work work item to enqueue. - * @return zero if work enqueued, non-zero otherwise. - */ - -int -CommOS_ScheduleAIOWork(CommOSWork *work) -{ - if (running && aioWQ && work) { - return QueueDelayedWork(aioWQ, work, 0); - } - return -1; -} - - -/** - * @brief Initializes the base IO system. - * @param dispatchTaskName dispatch thread(s) name. - * @param dispatchFunc dispatch function. - * @param intervalMillis periodic interval in milliseconds to call dispatch. - * The floor is 1 jiffy, regardless of how small intervalMillis is - * @param maxCycles number of cycles to do adaptive polling before scheduling. - * The maximum number of cycles is DISPATCH_MAX_CYCLES. - * @param aioTaskName AIO thread(s) name. If NULL, AIO threads aren't started. - * @return zero is successful, -1 otherwise. - * @sideeffects Dispatch threads, and if applicable, AIO threads are started. - */ - -int -CommOS_StartIO(const char *dispatchTaskName, // IN - CommOSDispatchFunc dispatchFunc, // IN - unsigned int intervalMillis, // IN - unsigned int maxCycles, // IN - const char *aioTaskName) // IN -{ - int rc; - int cpu; - - if (running) { - CommOS_Debug(("%s: I/O tasks already running.\n", __FUNCTION__)); - return 0; - } - - /* - * OK, let's test the handler against NULL. Though, the whole concept - * of checking for NULL pointers, outside cases where NULL is meaningful - * to the implementation, is relatively useless: garbage, random pointers - * rarely happen to be all-zeros. - */ - - if (!dispatchFunc) { - CommOS_Log(("%s: a NULL Dispatch handler was passed.\n", __FUNCTION__)); - return -1; - } - dispatch = dispatchFunc; - - if (intervalMillis == 0) { - intervalMillis = 4; - } - if ((dispatchInterval = msecs_to_jiffies(intervalMillis)) < 1) { - dispatchInterval = 1; - } - if (maxCycles > DISPATCH_MAX_CYCLES) { - dispatchMaxCycles = DISPATCH_MAX_CYCLES; - } else if (maxCycles > 0) { - dispatchMaxCycles = maxCycles; - } - CommOS_Debug(("%s: Interval millis %u (jif:%u).\n", __FUNCTION__, - intervalMillis, dispatchInterval)); - CommOS_Debug(("%s: Max cycles %u.\n", __FUNCTION__, dispatchMaxCycles)); - - numCpus = num_present_cpus(); - dispatchWQ = CreateWorkqueue(dispatchTaskName); - if (!dispatchWQ) { - CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__, - dispatchTaskName)); - return -1; - } - - if (aioTaskName) { - aioWQ = CreateWorkqueue(aioTaskName); - if (!aioWQ) { - CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__, - aioTaskName)); - DestroyWorkqueue(dispatchWQ); - return -1; - } - } else { - aioWQ = NULL; - } - - running = 1; - for (cpu = 0; cpu < numCpus; cpu++) { - CommOS_InitWork(&dispatchWorksNow[cpu], DispatchWrapper); - CommOS_InitWork(&dispatchWorks[cpu], DispatchWrapper); - rc = QueueDelayedWorkOn(cpu, dispatchWQ, - &dispatchWorks[cpu], - dispatchInterval); - if (rc != 0) { - CommOS_StopIO(); - return -1; - } - } - CommOS_Log(("%s: Created I/O task(s) successfully.\n", __FUNCTION__)); - return 0; -} - - -/** - * @brief Stops the base IO system. - * @sideeffects Dispatch threads, and if applicable, AIO threads are stopped. - */ - -void -CommOS_StopIO(void) -{ - int cpu; - - if (running) { - running = 0; - if (aioWQ) { - FlushWorkqueue(aioWQ); - DestroyWorkqueue(aioWQ); - aioWQ = NULL; - } - FlushWorkqueue(dispatchWQ); - for (cpu = 0; cpu < numCpus; cpu++) { - WaitForDelayedWork(&dispatchWorksNow[cpu]); - WaitForDelayedWork(&dispatchWorks[cpu]); - } - DestroyWorkqueue(dispatchWQ); - dispatchWQ = NULL; - CommOS_Log(("%s: I/O tasks stopped.\n", __FUNCTION__)); - } -} diff --git a/arch/arm/mvp/commkm/comm_os_linux.h b/arch/arm/mvp/commkm/comm_os_linux.h deleted file mode 100644 index f92c8bd..0000000 --- a/arch/arm/mvp/commkm/comm_os_linux.h +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Contains linux-specific type definitions and function declarations - */ - -#ifndef _COMM_OS_LINUX_H_ -#define _COMM_OS_LINUX_H_ - -#include -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -#error "Kernel versions lower than 2.6.20 are not supported" -#endif - -#include -#include -#include -#include -#include -#include - - -/* - * Type definitions. - */ - -typedef atomic_t CommOSAtomic; -typedef spinlock_t CommOSSpinlock; -typedef struct mutex CommOSMutex; -typedef wait_queue_head_t CommOSWaitQueue; -typedef struct delayed_work CommOSWork; -typedef void (*CommOSWorkFunc)(CommOSWork *work); -typedef struct list_head CommOSList; -typedef struct module *CommOSModule; - - -/* - * Initializers. - */ - -#define CommOSSpinlock_Define DEFINE_SPINLOCK - - -#define COMM_OS_DOLOG(...) printk(KERN_INFO __VA_ARGS__) - - -/** - * @brief Logs given arguments in debug builds. - */ - -#if defined(COMM_OS_DEBUG) - #define CommOS_Debug(args) COMM_OS_DOLOG args -#else - #define CommOS_Debug(args) -#endif - - -/** - * @brief Logs given arguments. - */ - -#define CommOS_Log(args) COMM_OS_DOLOG args - - -/** - * @brief Logs function name and location. - */ - -#if defined(COMM_OS_TRACE) -#define TRACE(ptr) \ - do { \ - CommOS_Debug(("%p:%s: at [%s:%d] with arg ptr [0x%p].\n", current, \ - __FUNCTION__, __FILE__, __LINE__, (ptr))); \ - } while (0) -#else -#define TRACE(ptr) -#endif - - -/** - * @brief Write atomic variable - * @param[in,out] atomic variable to write - * @param val new value - */ - -static inline void -CommOS_WriteAtomic(CommOSAtomic *atomic, - int val) -{ - atomic_set(atomic, val); -} - - -/** - * @brief Reads atomic variable - * @param atomic variable to read - * @return value - */ - -static inline int -CommOS_ReadAtomic(CommOSAtomic *atomic) -{ - return atomic_read(atomic); -} - - -/** - * @brief Atomically add value to atomic variable, return new value. - * @param[in,out] atomic variable - * @param val value to add - * @return new value - */ - -static inline int -CommOS_AddReturnAtomic(CommOSAtomic *atomic, - int val) -{ - return atomic_add_return(val, atomic); -} - - -/** - * @brief Atomically substract value from atomic variable, return new value. - * @param[in,out] atomic variable - * @param val value to substract - * @return new value - */ - -static inline int -CommOS_SubReturnAtomic(CommOSAtomic *atomic, - int val) -{ - return atomic_sub_return(val, atomic); -} - - -/** - * @brief Initializes a given lock. - * @param[in,out] lock lock to initialize - */ - -static inline void -CommOS_SpinlockInit(CommOSSpinlock *lock) -{ - spin_lock_init(lock); -} - - -/** - * @brief Locks given lock and disables bottom half processing. - * @param[in,out] lock lock to lock - */ - -static inline void -CommOS_SpinLockBH(CommOSSpinlock *lock) -{ - spin_lock_bh(lock); -} - - -/** - * @brief Attempts to lock the given lock and disable BH processing. - * @param[in,out] lock lock to lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_SpinTrylockBH(CommOSSpinlock *lock) -{ - return !spin_trylock_bh(lock); -} - - -/** - * @brief Unlocks given lock and re-enables BH processing. - * @param[in,out] lock lock to unlock - */ - -static inline void -CommOS_SpinUnlockBH(CommOSSpinlock *lock) -{ - spin_unlock_bh(lock); -} - - -/** - * @brief Locks the given lock. - * @param[in,out] lock lock to lock - */ - -static inline void -CommOS_SpinLock(CommOSSpinlock *lock) -{ - spin_lock(lock); -} - - -/** - * @brief Attempts to lock the given lock. - * @param[in,out] lock lock to try-lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_SpinTrylock(CommOSSpinlock *lock) -{ - return !spin_trylock(lock); -} - - -/** - * @brief Unlocks given lock. - * @param[in,out] lock lock to unlock - */ - -static inline void -CommOS_SpinUnlock(CommOSSpinlock *lock) -{ - spin_unlock(lock); -} - - -/** - * @brief Initializes given mutex. - * @param[in,out] mutex mutex to initialize - */ - -static inline void -CommOS_MutexInit(CommOSMutex *mutex) -{ - mutex_init(mutex); -} - - -/** - * @brief Acquires mutex. - * @param[in,out] mutex mutex to lock - * @return zero if successful, non-zero otherwise (interrupted) - */ - -static inline int -CommOS_MutexLock(CommOSMutex *mutex) -{ - return mutex_lock_interruptible(mutex); -} - - -/** - * @brief Acquires mutex in uninterruptible mode. - * @param[in,out] mutex mutex to lock - */ - -static inline void -CommOS_MutexLockUninterruptible(CommOSMutex *mutex) -{ - mutex_lock(mutex); -} - - -/** - * @brief Attempts to acquire given mutex. - * @param[in,out] mutex mutex to try-lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_MutexTrylock(CommOSMutex *mutex) -{ - return !mutex_trylock(mutex); -} - - -/** - * @brief Releases a given mutex. - * @param[in,out] mutex mutex to unlock - */ - -static inline void -CommOS_MutexUnlock(CommOSMutex *mutex) -{ - mutex_unlock(mutex); -} - - -/** - * @brief Initializes a wait queue. - * @param[in,out] wq workqueue to initialize - */ - -static inline void -CommOS_WaitQueueInit(CommOSWaitQueue *wq) -{ - init_waitqueue_head(wq); -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * - a signal is pending - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @param interruptible enable/disable signal pending check - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, if a signal is pending or other error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_DoWait(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis, - int interruptible) -{ - int rc; - DEFINE_WAIT(wait); - long timeout; -#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) - long tmpTimeout; - long retTimeout; - const unsigned int interval = 50; -#endif - - if (!timeoutMillis) { - return -1; - } - if ((rc = cond(condArg1, condArg2)) != 0) { - return rc; - } - -#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) - timeout = msecs_to_jiffies(interval < *timeoutMillis ? - interval : (unsigned int)*timeoutMillis); - retTimeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); - - for (; retTimeout >= 0; ) { - prepare_to_wait(wq, &wait, - (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); - if ((rc = cond(condArg1, condArg2))) { - break; - } - if (interruptible && signal_pending(current)) { - rc = -EINTR; - break; - } - if ((tmpTimeout = schedule_timeout(timeout))) { - retTimeout -= (timeout - tmpTimeout); - } else { - retTimeout -= timeout; - } - if (retTimeout < 0) { - retTimeout = 0; - } - } - finish_wait(wq, &wait); - if (rc == 0) { - rc = cond(condArg1, condArg2); - if (rc && (retTimeout == 0)) { - retTimeout = 1; - } - } - *timeoutMillis = (unsigned long long)jiffies_to_msecs(retTimeout); -#else // !defined(COMM_OS_LINUX_WAIT_WORKAROUND) - timeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); - - for (;;) { - prepare_to_wait(wq, &wait, - (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); - if ((rc = cond(condArg1, condArg2)) != 0) { - break; - } - if (interruptible && signal_pending(current)) { - rc = -EINTR; - break; - } - if ((timeout = schedule_timeout(timeout)) == 0) { - rc = 0; - break; - } - } - finish_wait(wq, &wait); - if (rc == 0) { - rc = cond(condArg1, condArg2); - if (rc && (timeout == 0)) { - timeout = 1; - } - } - *timeoutMillis = (unsigned long long)jiffies_to_msecs(timeout); -#endif - - return rc; -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * - a signal is pending - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, if a signal is pending or other error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_Wait(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis) -{ - return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 1); -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_WaitUninterruptible(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis) -{ - return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 0); -} - - -/** - * @brief Wakes up task(s) waiting on the given wait queue. - * @param[in,out] wq wait queue. - */ - -static inline void -CommOS_WakeUp(CommOSWaitQueue *wq) -{ - wake_up(wq); -} - - -/** - * @brief Allocates kernel memory of specified size; does not sleep. - * @param size size to allocate. - * @return Address of allocated memory or NULL if the allocation fails. - */ - -static inline void * -CommOS_KmallocNoSleep(unsigned int size) -{ - return kmalloc(size, GFP_ATOMIC); -} - - -/** - * @brief Allocates kernel memory of specified size; may sleep. - * @param size size to allocate. - * @return Address of allocated memory or NULL if the allocation fails. - */ - -static inline void * -CommOS_Kmalloc(unsigned int size) -{ - return kmalloc(size, GFP_KERNEL); -} - - -/** - * @brief Frees previously allocated kernel memory. - * @param obj object to free. - */ - -static inline void -CommOS_Kfree(void *obj) -{ - if (obj) { - kfree(obj); - } -} - - -/** - * @brief Yields the current cpu to other runnable tasks. - */ - -static inline void -CommOS_Yield(void) -{ - cond_resched(); -} - - -/** - * @brief Gets the current time in milliseconds. - * @return Current time in milliseconds, with precision of at most one tick. - */ - -static inline unsigned long long -CommOS_GetCurrentMillis(void) -{ - return (unsigned long long)jiffies_to_msecs(jiffies); -} - - -/** - * @brief Initializes given list. - * @param list list to initialize. - */ - -static inline void -CommOS_ListInit(CommOSList *list) -{ - INIT_LIST_HEAD(list); -} - - -/** - * @brief Tests if list is empty. - * @param list list to test. - * @return non-zero if empty, zero otherwise. - */ - -#define CommOS_ListEmpty(list) list_empty((list)) - - -/** - * @brief Adds given element to beginning of list. - * @param list list to add to. - * @param elem element to add. - */ - -#define CommOS_ListAdd(list, elem) list_add((elem), (list)) - - -/** - * @brief Adds given element to end of list. - * @param list list to add to. - * @param elem element to add. - */ - -#define CommOS_ListAddTail(list, elem) list_add_tail((elem), (list)) - - -/** - * @brief Deletes given element from its list. - * @param elem element to delete. - */ - -#define CommOS_ListDel(elem) \ - do { \ - list_del((elem)); \ - INIT_LIST_HEAD((elem)); \ - } while (0) - - -/** - * @brief Iterates over a list. - * @param list list to iterate over. - * @param[out] item stores next element. - * @param itemListFieldName name in the item structure storing the list head. - */ - -#define CommOS_ListForEach(list, item, itemListFieldName) \ - list_for_each_entry((item), (list), itemListFieldName) - - -/** - * @brief Iterates safely over a list. - * @param list list to iterate over. - * @param[out] item stores next element. May be deleted in the loop. - * @param[out] tmpItem saves iteration element. - * @param itemListFieldName name in the item structure storing the list head. - */ - -#define CommOS_ListForEachSafe(list, item, tmpItem, itemListFieldName) \ - list_for_each_entry_safe((item), (tmpItem), (list), itemListFieldName) - - -/** - * @brief Combines two lists, adds second list to beginning of first one. - * @param list list to add to. - * @param list2 list to add. - */ - -#define CommOS_ListSplice(list, list2) list_splice((list2), (list)) - - -/** - * @brief Combines two lists, adds second list to end of first one. - * @param list list to add to. - * @param list2 list to add. - */ - -#define CommOS_ListSpliceTail(list, list2) list_splice_tail((list2), (list)) - - -/** - * @brief Gets current module handle. - * @return module handle. - */ - -static inline CommOSModule -CommOS_ModuleSelf(void) -{ - return THIS_MODULE; -} - - -/** - * @brief Retains module. - * @param[in,out] module to retain. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -CommOS_ModuleGet(CommOSModule module) -{ - int rc = 0; - - if (!module) { - goto out; - } - if (!try_module_get(module)) { - rc = -1; - } - -out: - return rc; -} - - -/** - * @brief Releases module. - * @param[in,out] module to release. - */ - -static inline void -CommOS_ModulePut(CommOSModule module) -{ - if (module) { - module_put(module); - } -} - - -/** - * @brief Inserts r/w memory barrier. - */ - -#define CommOS_MemBarrier smp_mb - -#endif /* _COMM_OS_LINUX_H_ */ diff --git a/arch/arm/mvp/commkm/comm_os_mod_linux.c b/arch/arm/mvp/commkm/comm_os_mod_linux.c deleted file mode 100644 index 8470de6..0000000 --- a/arch/arm/mvp/commkm/comm_os_mod_linux.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Linux-specific module loading, unloading functions. - */ - -#include "comm_os.h" -#include "comm_os_mod_ver.h" - -#include - - -/* Module parameters -- passed as one 'name=value'-list string. */ - -static char modParams[256]; -module_param_string(COMM_OS_MOD_SHORT_NAME, modParams, sizeof modParams, 0644); - - -/** - * @brief Module initialization entry point. Calls the commOSModInit - * function pointer to perform upper layer initialization. - * @return zero if successful, non-zero otherwise. - */ - -static int __init -ModInit(void) -{ - int rc; - - if (!commOSModInit) { - CommOS_Log(("%s: Can't find \'init\' function for module \'" \ - COMM_OS_MOD_SHORT_NAME_STRING "\'.\n", __FUNCTION__)); - return -1; - } - - CommOS_Debug(("%s: Module parameters: [%s].\n", __FUNCTION__, modParams)); - - rc = (*commOSModInit)(modParams); - if (rc == 0) { - CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ - "\' has been successfully initialized.\n", __FUNCTION__)); - } else { - CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ - "\' could not be initialized [%d].\n", __FUNCTION__, rc)); - } - - return rc > 0 ? -rc : rc; -} - - -/** - * @brief Module exit function. Calls the commOSModExit function pointer - * to perform upper layer cleanup. - */ - -static void __exit -ModExit(void) -{ - if (!commOSModExit) { - CommOS_Log(("%s: Can't find \'fini\' function for module \'" \ - COMM_OS_MOD_SHORT_NAME_STRING "\'.\n", __FUNCTION__)); - return; - } - - (*commOSModExit)(); - CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ - "\' has been stopped.\n", __FUNCTION__)); -} - - -module_init(ModInit); -module_exit(ModExit); - -/* Module information. */ -MODULE_AUTHOR("VMware, Inc."); -MODULE_DESCRIPTION(COMM_OS_MOD_NAME_STRING); -MODULE_VERSION(COMM_OS_MOD_VERSION_STRING); -MODULE_LICENSE("GPL v2"); -/* - * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement - * with them and mark their kernel modules as externally supported via a - * change to the module header. If this isn't done, the module will not load - * by default (i.e., neither mkinitrd nor modprobe will accept it). - */ -MODULE_INFO(supported, "external"); diff --git a/arch/arm/mvp/commkm/comm_os_mod_ver.h b/arch/arm/mvp/commkm/comm_os_mod_ver.h deleted file mode 100644 index 059854c..0000000 --- a/arch/arm/mvp/commkm/comm_os_mod_ver.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Version definitions for the Comm module. - */ - -#ifndef _COMM_OS_MOD_VER_H_ -#define _COMM_OS_MOD_VER_H_ - -#define COMM_OS_MOD_NAME_STRING "VMware communication module" -#define COMM_OS_MOD_SHORT_NAME comm -#define COMM_OS_MOD_SHORT_NAME_STRING "comm" - -#define COMM_OS_MOD_VERSION 1.0.0.0 -#define COMM_OS_MOD_VERSION_COMMAS 1,0,0,0 -#define COMM_OS_MOD_VERSION_STRING "1.0.0.0" - -#endif /* _COM_OS_MOD_VER_H_ */ diff --git a/arch/arm/mvp/commkm/comm_svc.c b/arch/arm/mvp/commkm/comm_svc.c deleted file mode 100644 index 18f62bd..0000000 --- a/arch/arm/mvp/commkm/comm_svc.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Communication functions based on transport functionality. - */ - -#include "comm_os.h" -#include "comm_os_mod_ver.h" -#include "comm_svc.h" - - -/* - * Initialization of module entry and exit callbacks expected by module - * loading/unloading functions in comm_os. - */ - -static int Init(void *args); -static void Exit(void); - -COMM_OS_MOD_INIT(Init, Exit); - -static volatile int running; // Initialized and running. - - -/** - * @brief Allocates and initializes comm global state. - * Starts input dispatch and aio threads. - * @param argsIn arguments - * @return zero if successful, non-zero otherwise. - */ - -static int -Init(void *argsIn) -{ - int rc = -1; - unsigned int maxChannels = 8; - /* - * Infinite timeout, 1 polling cycle - * see kernel/time.c: msecs_to_jiffies() - */ - unsigned int pollingMillis = (unsigned int)-1; - unsigned int pollingCycles = 1; - const char *args = argsIn; - - if (args && *args) { - /* coverity[secure_coding] */ - sscanf(args, - "max_channels:%u,poll_millis:%u,poll_cycles:%u", - &maxChannels, &pollingMillis, &pollingCycles); - CommOS_Debug(("%s: arguments [%s].\n", __FUNCTION__, args)); - } - - rc = Comm_Init(maxChannels); - if (rc) { - goto out; - } - - rc = CommOS_StartIO(COMM_OS_MOD_SHORT_NAME_STRING "-disp", - Comm_DispatchAll, pollingMillis, pollingCycles, - COMM_OS_MOD_SHORT_NAME_STRING "-aio"); - if (rc) { - unsigned long long timeout = 0; - - Comm_Finish(&timeout); /* Nothing started, guaranteed to succeed. */ - goto out; - } - running = 1; - rc = 0; - -out: - return rc; -} - - -/** - * @brief Attempts to close all channels. - * @return zero if successful, non-zero otherwise. - */ - -static int -Halt(void) -{ - unsigned int maxTries = 10; - int rc = -1; - - if (!running) { - rc = 0; - goto out; - } - - for ( ; maxTries; maxTries--) { - unsigned long long timeout = 2000ULL; - - CommOS_Debug(("%s: Attempting to halt...\n", __FUNCTION__)); - if (!Comm_Finish(&timeout)) { - running = 0; - rc = 0; - break; - } - } - -out: - return rc; -} - - -/** - * @brief Stops the comm_rt module. - * If Halt() call successful, stops input dispatch and aio threads. - */ - -static void -Exit(void) -{ - if (!Halt()) { - CommOS_StopIO(); - } -} - - -/** - * @brief Registers an implementation block used when attaching to channels - * in response to transport attach events. - * @param impl implementation block. - * @return 0 if successful, non-zero otherwise. - */ - -int -CommSvc_RegisterImpl(const CommImpl *impl) -{ - return Comm_RegisterImpl(impl); -} - - -/** - * @brief Unregisters an implementation block used when attaching to channels - * in response to transport attach events. - * @param impl implementation block. - */ - -void -CommSvc_UnregisterImpl(const CommImpl *impl) -{ - Comm_UnregisterImpl(impl); -} - - -/** - * @brief Finds a free entry and initializes it with the information provided. - * May be called from BH. It doesn't call potentially blocking functions. - * @param transpArgs transport initialization arguments. - * @param impl implementation block. - * @param inBH non-zero if called in bottom half. - * @param[out] newChannel newly allocated channel. - * @return zero if successful, non-zero otherwise. - * @sideeffects Initializes the communications channel with given parameters - */ - -int -CommSvc_Alloc(const CommTranspInitArgs *transpArgs, - const CommImpl *impl, - int inBH, - CommChannel *newChannel) -{ - return Comm_Alloc(transpArgs, impl, inBH, newChannel); -} - - -/** - * @brief Zombifies a channel. May fail if channel isn't active. - * @param channel channel to zombify. - * @param inBH non-zero if called in bottom half. - * @return zero if channel zombified, non-zero otherwise. - */ - -int -CommSvc_Zombify(CommChannel channel, - int inBH) -{ - return Comm_Zombify(channel, inBH); -} - - -/** - * @brief Reports whether a channel is active. - * @param channel channel to report on. - * @return non-zero if channel active, zero otherwise. - */ - -int -CommSvc_IsActive(CommChannel channel) -{ - return Comm_IsActive(channel); -} - - -/** - * @brief Retrieves a channel's transport initialization arguments. - * It doesn't lock, the caller must ensure the channel may be accessed. - * @param channel CommChannel structure to get initialization arguments from. - * @return initialization arguments used to allocate/attach to channel. - */ - -CommTranspInitArgs -CommSvc_GetTranspInitArgs(CommChannel channel) -{ - return Comm_GetTranspInitArgs(channel); -} - - -/** - * @brief Retrieves upper layer state (pointer). It doesn't lock, the caller - * must ensure the channel may be accessed. - * @param channel CommChannel structure to get state from. - * @return pointer to upper layer state. - */ - -void * -CommSvc_GetState(CommChannel channel) -{ - return Comm_GetState(channel); -} - - -/** - * @brief Writes a fully formatted packet (containing payload data, if - * applicable) to the specified channel. - * - * The operation may block until enough write space is available, but no - * more than the specified interval. The operation either writes the full - * amount of bytes, or it fails. Warning: callers must _not_ use the - * _Lock/_Unlock functions to bracket calls to this function. - * @param[in,out] channel channel to write to. - * @param packet packet to write. - * @param[in,out] timeoutMillis interval in milliseconds to wait. - * @return number of bytes written, 0 if it times out, -1 error. - * @sideeffects Data may be written to the channel. - */ - -int -CommSvc_Write(CommChannel channel, - const CommPacket *packet, - unsigned long long *timeoutMillis) -{ - return Comm_Write(channel, packet, timeoutMillis); -} - - -/** - * @brief Writes a packet and associated payload data to the specified channel. - * - * The operation may block until enough write space is available, but not - * more than the specified interval. The operation either writes the full - * amount of bytes, or it fails. Users may call this function successively - * to write several packets from large {io|k}vecs. If that's the case, the - * packet header needs to be updated in between calls, for the different - * (total) lengths. Warning: callers must _not_ use the _Lock/_Unlock - * functions to bracket calls to this function. - * @param[in,out] channel the specified channel - * @param packet packet to write - * @param[in,out] vec kvec to write from - * @param[in,out] vecLen length of kvec - * @param[in,out] timeoutMillis interval in milliseconds to wait - * @param[in,out] iovOffset must be set to 0 before first call (internal cookie) - * @return number of bytes written, 0 if it timed out, -1 error - * @sideeffects data may be written to the channel - */ - -int -CommSvc_WriteVec(CommChannel channel, - const CommPacket *packet, - struct kvec **vec, - unsigned int *vecLen, - unsigned long long *timeoutMillis, - unsigned int *iovOffset) -{ - return Comm_WriteVec(channel, packet, vec, vecLen, timeoutMillis, iovOffset); -} - - -/** - * @brief Releases channel ref count. This function is exported for the upper - * layer's 'activateNtf' callback which may be run asynchronously. The - * callback is protected from concurrent channel releases until it calls - * this function. - * @param[in,out] channel CommChannel structure to release. - */ - -void -CommSvc_Put(CommChannel channel) -{ - Comm_Put(channel); -} - - -/** - * @brief Uses the read lock. This function is exported for the upper layer - * such that it can order acquisition of a different lock (socket) with - * the release of the dispatch lock. - * @param[in,out] channel CommChannel structure to unlock. - */ - -void -CommSvc_DispatchUnlock(CommChannel channel) -{ - Comm_DispatchUnlock(channel); -} - - -/** - * @brief Lock the channel. - * - * Uses the writer lock. This function is exported for the upper layer - * to ensure that channel isn't closed while updating the layer state. - * It also guarantees that if the lock is taken, the entry is either ACTIVE - * or ZOMBIE. Operations using this function are expected to be short, - * since unlike the _Write functions, these callers cannot be signaled. - * @param[in,out] channel CommChannel structure to lock. - * @return zero if successful, -1 otherwise. - */ - -int -CommSvc_Lock(CommChannel channel) -{ - return Comm_Lock(channel); -} - - -/** - * @brief Unlock the channel. - * - * Uses the writer lock. This function is exported for the upper layer - * to ensure that channel isn't closed while updating the layer state. - * See Comm_WriteLock for details). - * @param[in,out] channel CommChannel structure to unlock. - */ - -void -CommSvc_Unlock(CommChannel channel) -{ - Comm_Unlock(channel); -} - - -/** - * @brief Schedules a work item on the AIO thread(s). - * @param[in,out] work work item to be scheduled. - * @return zero if successful, -1 otherwise. - */ - -int -CommSvc_ScheduleAIOWork(CommOSWork *work) -{ - return CommOS_ScheduleAIOWork(work); -} - - -/** - * @brief Requests events be posted in-line after the function completes. - * @param channel channel object. - * @return current number of requests for inline event posting, or -1 on error. - */ - -unsigned int -CommSvc_RequestInlineEvents(CommChannel channel) -{ - return Comm_RequestInlineEvents(channel); -} - - -/** - * @brief Requests events be posted out-of-band after the function completes. - * @param channel channel object. - * @return current number of requests for inline event posting, or -1 on error. - */ - -unsigned int -CommSvc_ReleaseInlineEvents(CommChannel channel) -{ - return Comm_ReleaseInlineEvents(channel); -} - - -#if defined(__linux__) -EXPORT_SYMBOL(CommSvc_RegisterImpl); -EXPORT_SYMBOL(CommSvc_UnregisterImpl); -EXPORT_SYMBOL(CommSvc_Alloc); -EXPORT_SYMBOL(CommSvc_Zombify); -EXPORT_SYMBOL(CommSvc_IsActive); -EXPORT_SYMBOL(CommSvc_GetTranspInitArgs); -EXPORT_SYMBOL(CommSvc_GetState); -EXPORT_SYMBOL(CommSvc_Write); -EXPORT_SYMBOL(CommSvc_WriteVec); -EXPORT_SYMBOL(CommSvc_Put); -EXPORT_SYMBOL(CommSvc_DispatchUnlock); -EXPORT_SYMBOL(CommSvc_Lock); -EXPORT_SYMBOL(CommSvc_Unlock); -EXPORT_SYMBOL(CommSvc_ScheduleAIOWork); -EXPORT_SYMBOL(CommSvc_RequestInlineEvents); -EXPORT_SYMBOL(CommSvc_ReleaseInlineEvents); -#endif // defined(__linux__) diff --git a/arch/arm/mvp/commkm/comm_svc.h b/arch/arm/mvp/commkm/comm_svc.h deleted file mode 100644 index c4f3292..0000000 --- a/arch/arm/mvp/commkm/comm_svc.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Communication functions exported by the comm_rt module. - */ - -#ifndef _COMM_SVC_H_ -#define _COMM_SVC_H_ - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "comm.h" - -int CommSvc_RegisterImpl(const CommImpl *impl); -void CommSvc_UnregisterImpl(const CommImpl *impl); -int CommSvc_Zombify(CommChannel channel, int inBH); -int CommSvc_IsActive(CommChannel channel); -CommTranspInitArgs CommSvc_GetTranspInitArgs(CommChannel channel); -void *CommSvc_GetState(CommChannel channel); -void CommSvc_Put(CommChannel channel); -void CommSvc_DispatchUnlock(CommChannel channel); -int CommSvc_Lock(CommChannel channel); -void CommSvc_Unlock(CommChannel channel); -int CommSvc_ScheduleAIOWork(CommOSWork *work); - -int -CommSvc_Alloc(const CommTranspInitArgs *transpArgs, - const CommImpl *impl, - int inBH, - CommChannel *newChannel); - -int -CommSvc_Write(CommChannel channel, - const CommPacket *packet, - unsigned long long *timeoutMillis); - -int -CommSvc_WriteVec(CommChannel channel, - const CommPacket *packet, - struct kvec **vec, - unsigned int *vecLen, - unsigned long long *timeoutMillis, - unsigned int *iovOffset); - -unsigned int CommSvc_RequestInlineEvents(CommChannel channel); -unsigned int CommSvc_ReleaseInlineEvents(CommChannel channel); - -#endif // _COMM_SVC_H_ diff --git a/arch/arm/mvp/commkm/comm_transp.h b/arch/arm/mvp/commkm/comm_transp.h deleted file mode 100644 index 6cc58ae..0000000 --- a/arch/arm/mvp/commkm/comm_transp.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Generic shared memory transport API. - */ - -#ifndef _COMM_TRANSP_H_ -#define _COMM_TRANSP_H_ - -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/* - * Common shared memory identifier. - * External handle that makes sense to both hypervisor and guest. - */ - -#define COMM_TRANSP_ID_8_ANY ((unsigned char)-1) -#define COMM_TRANSP_ID_32_ANY ((unsigned int)-1) -#define COMM_TRANSP_ID_64_ANY ((unsigned long long)-1) - - -typedef struct CommTranspID { - union { - unsigned char d8[8]; - unsigned int d32[2]; - unsigned long long d64; - }; -} CommTranspID; - - -/* Basic initialization arguments. */ - -typedef enum CommTranspInitMode { - COMM_TRANSP_INIT_CREATE = 0x0, - COMM_TRANSP_INIT_ATTACH = 0x1 -} CommTranspInitMode; - -typedef struct CommTranspInitArgs { - unsigned int capacity; // Shared memory capacity. - unsigned int type; // Type / implementation using this area. - CommTranspID id; // ID (name) of shared memory area. - CommTranspInitMode mode; // Init mode (above). -} CommTranspInitArgs; - - -/** - * @brief Generate a type id from description (protocol) string. This function - * uses djb2, a string hashing algorithm by Dan Bernstein. - * (see http://www.cse.yorku.ca/~oz/hash.html) - * @param str string to hash - * @return 32-bit hash value - */ - -static inline unsigned int -CommTransp_GetType(const char *str) -{ - unsigned int hash = 5381; - int c; - - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - return hash; -} - -#endif // _COMM_TRANSP_H_ diff --git a/arch/arm/mvp/commkm/comm_transp_impl.h b/arch/arm/mvp/commkm/comm_transp_impl.h deleted file mode 100644 index 113cd21..0000000 --- a/arch/arm/mvp/commkm/comm_transp_impl.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Generic shared memory transport private API. - */ - -#ifndef _COMM_TRANSP_IMPL_H_ -#define _COMM_TRANSP_IMPL_H_ - -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "comm_transp.h" - - -/* Shared memory opaque descriptor/handle. Only meaningful locally. */ - -typedef struct CommTranspPriv *CommTransp; - - -/* Asynchronous signaling initialization arguments. */ - -typedef enum CommTranspIOEvent { - COMM_TRANSP_IO_DETACH = 0x0, - COMM_TRANSP_IO_IN = 0x1, - COMM_TRANSP_IO_OUT = 0x2, - COMM_TRANSP_IO_INOUT = 0x3 -} CommTranspIOEvent; - -typedef struct CommTranspEvent { - void (*ioEvent)(CommTransp transp, CommTranspIOEvent event, void *data); - void *ioEventData; -} CommTranspEvent; - - -/* - * Mechanism to detect and optionally attach to, created shared memory regions. - */ - -typedef struct CommTranspListener { - int (*probe)(CommTranspInitArgs *transpArgs, void *probeData); - void *probeData; -} CommTranspListener; - - - -/* - * Function prototypes. - */ - -int CommTranspEvent_Init(void); -void CommTranspEvent_Exit(void); -int CommTranspEvent_Process(CommTranspID *transpID, CommTranspIOEvent event); -int -CommTranspEvent_Raise(unsigned int peerEvID, - CommTranspID *transpID, - CommTranspIOEvent event); - -int CommTransp_Init(void); -void CommTransp_Exit(void); - -int CommTransp_Register(const CommTranspListener *listener); -void CommTransp_Unregister(const CommTranspListener *listener); -int -CommTransp_Notify(const CommTranspID *notificationCenterID, - CommTranspInitArgs *transpArgs); - -int -CommTransp_Open(CommTransp *transp, - CommTranspInitArgs *transpArgs, - CommTranspEvent *transpEvent); -void CommTransp_Close(CommTransp transp); - -int CommTransp_EnqueueSpace(CommTransp transp); -int CommTransp_EnqueueReset(CommTransp transp); -int CommTransp_EnqueueCommit(CommTransp transp); -int -CommTransp_EnqueueSegment(CommTransp transp, - const void *buf, - unsigned int bufLen); - -int CommTransp_DequeueSpace(CommTransp transp); -int CommTransp_DequeueReset(CommTransp transp); -int CommTransp_DequeueCommit(CommTransp transp); -int -CommTransp_DequeueSegment(CommTransp transp, - void *buf, - unsigned int bufLen); - -unsigned int CommTransp_RequestInlineEvents(CommTransp transp); -unsigned int CommTransp_ReleaseInlineEvents(CommTransp transp); - - -/** - * @brief Enqueues data into the transport object, data is available for - * reading immediately. - * @param transp handle to the transport object. - * @param buf bytes to enqueue. - * @param bufLen number of bytes to enqueue. - * @return number of bytes enqueued on success, < 0 otherwise. - */ - -static inline int -CommTransp_EnqueueAtomic(CommTransp transp, - const void *buf, - unsigned int bufLen) -{ - int rc; - - CommTransp_EnqueueReset(transp); - rc = CommTransp_EnqueueSegment(transp, buf, bufLen); - if (CommTransp_EnqueueCommit(transp)) { - rc = -1; - } - return rc; -} - - -/** - * @brief Dequeues data from the transport object into a buffer. - * @param transp handle to the transport object. - * @param[out] buf buffer to copy to. - * @param bufLen number of bytes to dequeue. - * @return number of bytes dequeued on success, < 0 otherwise, - */ - -static inline int -CommTransp_DequeueAtomic(CommTransp transp, - void *buf, - unsigned int bufLen) -{ - int rc; - - CommTransp_DequeueReset(transp); - rc = CommTransp_DequeueSegment(transp, buf, bufLen); - if (CommTransp_DequeueCommit(transp)) { - rc = -1; - } - return rc; -} - -#endif // _COMM_TRANSP_IMPL_H_ diff --git a/arch/arm/mvp/commkm/comm_transp_mvp.c b/arch/arm/mvp/commkm/comm_transp_mvp.c deleted file mode 100644 index f755de9..0000000 --- a/arch/arm/mvp/commkm/comm_transp_mvp.c +++ /dev/null @@ -1,944 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Generic shared memory transport API. - */ -#include - -#include "comm_os.h" -#include "comm_transp_impl.h" - -#include "mvp_types.h" -#include "qp.h" - - -/* - * Opaque CommTransp structure. See comm_transp.h - */ - -struct CommTranspPriv { - QPHandle *qp; - CommTranspEvent event; - unsigned int peerEvID; - unsigned int writeSize; - unsigned int readSize; - uint32 backRef; - CommOSWork work; - CommOSAtomic raiseInline; -}; - -/* - * Transport table object accounting - */ - -typedef struct TranspTableEntry { - CommOSAtomic holds; - CommTransp transp; - CommOSWaitQueue wq; -} TranspTableEntry; - -TranspTableEntry transpTable[QP_MAX_QUEUE_PAIRS]; -static CommOSSpinlock_Define(transpTableLock); - -/** - * @brief Destroy the transport object - * @param transp transport object to destroy - * @sideeffects detaches from queue pair - */ - -static void -DestroyTransp(CommTransp transp) -{ - CommTranspID transpID; - int32 rc; - - if (!transp) { - CommOS_Debug(("Failed to close channel: Bad handle\n")); - return; - } - - CommOS_Log(("%s: Detaching channel [%u:%u]\n", - __FUNCTION__, - transp->qp->id.context, - transp->qp->id.resource)); - - transpID.d32[0] = transp->qp->id.context; - transpID.d32[1] = transp->qp->id.resource; - -#if !defined(COMM_BUILDING_SERVER) - /* - * Tell the host to detach, will block in the host - * until the host has unmapped memory. Once the - * host has unmapped, it is safe to free. - */ - CommTranspEvent_Raise(transp->peerEvID, - &transpID, - COMM_TRANSP_IO_DETACH); -#endif - - rc = QP_Detach(transp->qp); - -#if defined(COMM_BUILDING_SERVER) - /* - * Wake up waiters now that unmapping is complete - */ - CommOS_WakeUp(&transpTable[transp->backRef].wq); -#endif - - CommOS_Kfree(transp); - if (rc != QP_SUCCESS) { - CommOS_Log(("%s: Failed to detach. rc: %d\n", __FUNCTION__, rc)); - } else { - CommOS_Log(("%s: Channel detached.\n", __FUNCTION__)); - } -} - - -/** - * @brief Initialize the transport object table - */ - -static void -TranspTableInit(void) -{ - uint32 i; - CommOS_SpinLock(&transpTableLock); - for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { - CommOS_WriteAtomic(&transpTable[i].holds, -1); - transpTable[i].transp = NULL; - } - CommOS_SpinUnlock(&transpTableLock); -} - - -/** - * @brief Add a transport object into the table - * @param transp handle to the transport object - * @return 0 on success, -1 otherwise - * @sideeffects increments entry refcount - */ - -static inline int32 -TranspTableAdd(CommTransp transp) -{ - uint32 i; - - if (!transp) { - return -1; - } - - CommOS_SpinLock(&transpTableLock); - for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { - if ((transpTable[i].transp) == NULL) { - transpTable[i].transp = transp; - CommOS_WriteAtomic(&transpTable[i].holds, 1); - CommOS_WaitQueueInit(&transpTable[i].wq); - transp->backRef = i; - break; - } - } - CommOS_SpinUnlock(&transpTableLock); - - return 0; -} - -/** - * @brief retrieve a transport object and increment its ref count - * @param id transport id to retrieve - * @return transport object, or NULL if not found - * @sideeffects increments entry ref count - */ - -static inline CommTransp -TranspTableGet(CommTranspID *id) -{ - CommTransp transp; - uint32 i; - - if (!id) { - return NULL; - } - - for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { - transp = transpTable[i].transp; - if (transp && - (transp->qp->id.context == id->d32[0]) && - (transp->qp->id.resource == id->d32[1])) { - CommOS_AddReturnAtomic(&transpTable[i].holds, 1); - return transp; - } - } - CommOS_Debug(("%s: couldn't find transport object\n", __FUNCTION__)); - - return NULL; -} - -/** - * @brief Puts back a previously TranspGet-ed transport object. - * @param transp the transport object. - * @sideeffects decrements the transport reference count. - * frees object if refcount now zero - */ - -static inline void -TranspTablePut(CommTransp transp) -{ - int32 holds; - int32 backRef; - if (!transp) { - return; - } - - backRef = transp->backRef; - BUG_ON(backRef >= QP_MAX_QUEUE_PAIRS); - - holds = CommOS_SubReturnAtomic(&transpTable[backRef].holds, 1); - if (holds > 0) { - return; - } - BUG_ON(holds < 0); - - CommOS_SpinLock(&transpTableLock); - CommOS_WriteAtomic(&transpTable[backRef].holds, -1); - transpTable[backRef].transp = NULL; - CommOS_SpinUnlock(&transpTableLock); - DestroyTransp(transp); -} - - -/** - * @brief Puts back a previously TranspGet-ed transport object. - * @param transp the transport object. - * @sideeffects decrements the transport reference count. - * asserts that remaining count > 0 - */ - -static inline void -TranspTablePutNF(CommTransp transp) -{ - int32 holds; - int32 backRef; - if (!transp) { - return; - } - - backRef = transp->backRef; - BUG_ON(backRef >= QP_MAX_QUEUE_PAIRS); - - holds = CommOS_SubReturnAtomic(&transpTable[backRef].holds, 1); - BUG_ON(holds <= 0); -} - - -/** - * @brief Raises INOUT event in-line or out-of-band. Note that this function - * expects the transport object to be held prior to being called. - * @param arg work item of transport object. - */ - -static void -RaiseEvent(CommOSWork *arg) -{ -#if !defined(__linux__) -#error "RaiseEvent() is only supported on linux. Port 'container_of'!" -#endif - CommTransp transp = container_of(arg, struct CommTranspPriv, work); - CommTranspID transpID = {{ - .d32 = { - [0] = transp->qp->id.context, - [1] = transp->qp->id.resource - } - }}; - - CommTranspEvent_Raise(transp->peerEvID, - &transpID, - COMM_TRANSP_IO_INOUT); - TranspTablePut(transp); -} - - -/** - * @brief Requests events be posted in-line after the function completes. - * @param transp transport object. - * @return current number of requests for inline event posting. - * @sideeffects posts an event on the first transition to in-line processing. - */ - -unsigned int -CommTransp_RequestInlineEvents(CommTransp transp) -{ - unsigned int res = CommOS_AddReturnAtomic(&transp->raiseInline, 1); - if (res == 1) { - /* On the first (effective) transition, make sure an event is raised. */ - - CommOS_AddReturnAtomic(&transpTable[transp->backRef].holds, 1); - RaiseEvent(&transp->work); - } - return res; -} - - -/** - * @brief Requests events be posted out-of-band after the function completes. - * @param transp transport object. - * @return current number of requests for inline event posting. - */ - -unsigned int -CommTransp_ReleaseInlineEvents(CommTransp transp) -{ - return CommOS_SubReturnAtomic(&transp->raiseInline, 1); -} - - -/* - * Comm Offload server callbacks. - */ - -#if defined(COMM_BUILDING_SERVER) - -#define COMM_MAX_LISTENERS QP_MAX_LISTENERS - -static int32 NotifyCB(const QPInitArgs *args); -static void DetachCB(void *data); - -static CommOSSpinlock_Define(listenersLock); -static CommTranspListener listeners[COMM_MAX_LISTENERS]; -static uint32 numListeners = 0; - - -/** - * @brief Notify callback when guests attach to queue pairs. Notifies any - * registered listeners (e.g. Comm layer). - * @param args Initialization arguments used by the guest to initialize - * its queue pair - * @return 0 on success, <0 otherwise. see qp.h for error codes. - */ - -static int32 -NotifyCB(const QPInitArgs* args) -{ - CommTranspInitArgs transpArgs; - uint32 i; - int32 rc = -1; - - if (!args) { - return QP_ERROR_INVALID_ARGS; - } - - transpArgs.id.d32[0] = args->id.context; - transpArgs.id.d32[1] = args->id.resource; - transpArgs.capacity = args->capacity; - transpArgs.type = args->type; - - CommOS_SpinLock(&listenersLock); - for (i = 0; i < COMM_MAX_LISTENERS; i++) { - if (listeners[i].probe && - (listeners[i].probe(&transpArgs, listeners[i].probeData) == 0)) { - CommOS_Debug(("%s: Delivered notify event to listener %u\n", - __FUNCTION__, - i)); - rc = 0; - break; - } - } - CommOS_SpinUnlock(&listenersLock); - return rc; -} - - -/** - * @brief Detach callback when guests detach from queue pairs. Notifies - * any registered listeners (e.g. CommComm layer). - * @param data Transport object passed when the callback was registered - */ - -static void -DetachCB(void *data) -{ - CommTransp transp = data; - if (!transp || !(transp->event.ioEvent)) { - return; - } - CommOS_Debug(("%s: Guest detached from [%u:%u]\n", - __FUNCTION__, - transp->qp->id.context, - transp->qp->id.resource)); - transp->event.ioEvent(transp, COMM_TRANSP_IO_DETACH, transp->event.ioEventData); -} -#endif - - -/** - * @brief Performs one-time initialization of mvp transport provider. - * @return 0 on success, < 0 otherwise. - */ - -int -CommTransp_Init(void) -{ - int32 rc; - TranspTableInit(); - - rc = CommTranspEvent_Init(); - -#if defined(COMM_BUILDING_SERVER) - if (!rc) { - QP_RegisterListener(NotifyCB); - } -#endif - return rc; -} - - -/** - * @brief Performs clean-up of mvp transport provider. - */ - -void -CommTransp_Exit(void) -{ - CommTranspEvent_Exit(); -#if defined(COMM_BUILDING_SERVER) - QP_UnregisterListener(NotifyCB); -#endif -} - -#if defined(COMM_BUILDING_SERVER) - -/** - * @brief Checks for a successful detach from Comm - * @param arg1 back reference index for channel in transport table - * @param arg2 ignored - * @return 1 if detach completed, 0 otherwise - */ - -static int -DetachCondition(void *arg1, void *arg2) -{ - uint32 backRef = (uint32)arg1; - - return (CommOS_ReadAtomic(&transpTable[backRef].holds) == -1); -} -#endif - - -/** - * @brief Processes a raised signal event. This is a callback function called - * from a comm_transp_ev plugin when a signal is received. Delivers an event - * to one or more channels. If id->d32[1] == COMM_TRANSP_ID_32_ANY, the event - * will be delivered to all registered channels associated with vmID - * id->d32[0]. - * @param id identifies a transport object to signal. - * @param event type of event. - * @return 0 if delivered to at least one channel, -1 on failure. - */ - -int -CommTranspEvent_Process(CommTranspID *id, - CommTranspIOEvent event) -{ - int rc = 0; - unsigned int delivered = 0; - unsigned int backRef; - int i = 0; - - CommTransp transp; - uint32 raiseOnAllChannels = (id->d32[1] == COMM_TRANSP_ID_32_ANY); - uint32 channels = raiseOnAllChannels ? QP_MAX_QUEUE_PAIRS : 1; - - while (channels--) { - if (raiseOnAllChannels) { - id->d32[1] = i++; - } - transp = TranspTableGet(id); - if (transp) { - if (transp->event.ioEvent) { - transp->event.ioEvent(transp, event, transp->event.ioEventData); - } - backRef = transp->backRef; - TranspTablePut(transp); - -#if defined(COMM_BUILDING_SERVER) - /* - * Wait for unmap on IO_DETACH, return to monitor. - */ - if (event == COMM_TRANSP_IO_DETACH) { - unsigned long long timeout = 30000; - - rc = CommOS_Wait(&transpTable[backRef].wq, - DetachCondition, - (void*)backRef, - NULL, - &timeout); - switch (rc) { - case 1: // Memory successfully unmapped - rc = 0; - break; - default: // Timed out or other error. - return -1; - } - } -#endif - delivered++; - } - } - - rc = (delivered > 0) ? 0 : -1; - return rc; -} - - -/** - * @brief Register a listener to be notified when guests attach to the Comm - * offload server - * @param listener the listener to be notified - * @return 0 on success, -1 on failure - */ - -int -CommTransp_Register(const CommTranspListener *listener) -{ - int32 rc = -1; -#if defined(COMM_BUILDING_SERVER) - uint32 i; - - if (!listener) { - return -1; - } - - CommOS_SpinLock(&listenersLock); - for (i = 0; i < COMM_MAX_LISTENERS; i++) { - if ((listeners[i].probe == NULL) && - (listeners[i].probeData == NULL)) { - listeners[i] = *listener; - numListeners++; - rc = 0; - CommOS_Debug(("%s: Registered listener %u\n", __FUNCTION__, i)); - break; - } - } - CommOS_SpinUnlock(&listenersLock); -#endif - return rc; -} - - -/** - * @brief Unregisters a listener from the transport event notification system - * @param listener listener to unregister - * @return 0 on success - */ - -void -CommTransp_Unregister(const CommTranspListener *listener) -{ -#if defined(COMM_BUILDING_SERVER) - uint32 i; - - if (!listener || !listener->probe) { - return; - } - - - CommOS_SpinLock(&listenersLock); - for (i = 0; i < COMM_MAX_LISTENERS; i++) { - if ((listeners[i].probe == listener->probe) && - (listeners[i].probeData == listener->probeData)) { - listeners[i].probe = NULL; - listeners[i].probeData = NULL; - numListeners--; - CommOS_Debug(("%s: Unregistered listener %u\n", __FUNCTION__, i)); - } - } - CommOS_SpinUnlock(&listenersLock); -#endif -} - - -/** - * @brief Allocates and initializes a transport object - * @param[in,out] transp handle to the transport to allocate and initialize - * @param transpArgs initialization arguments (see pvtcpTransp.h) - * @param transpEvent event callback to be delivered when events occur (e.g. - * detach events) - * @return 0 on success, <0 otherwise. See qp.h for error codes. - * @sideeffects Allocates memory - */ - -int -CommTransp_Open(CommTransp *transp, - CommTranspInitArgs *transpArgs, - CommTranspEvent *transpEvent) -{ - int32 rc = -1; - QPHandle *qp = NULL; - CommTransp transpOut = NULL; - QPInitArgs qpInitArgs; - - if (!transp || !transpArgs) { - return -1; - } - - CommOS_Log(("%s: Attaching to [%u:%u]. Capacity: %u\n", - __FUNCTION__, - transpArgs->id.d32[1], - transpArgs->id.d32[0], - transpArgs->capacity)); - - qpInitArgs.id.context = transpArgs->id.d32[0]; - qpInitArgs.id.resource = transpArgs->id.d32[1]; - qpInitArgs.capacity = transpArgs->capacity; - qpInitArgs.type = transpArgs->type; - - if (!(transpOut = CommOS_Kmalloc(sizeof *transpOut))) { - rc = -1; - goto out; - } - - /* - * Attach to the queue pair - */ - rc = QP_Attach(&qpInitArgs, &qp); - if (rc < 0) { - rc = -1; - goto out; - } - - transpOut->qp = qp; - - /* - * Reassign ID so Comm knows what ID was actually given - */ - transpArgs->id.d32[0] = qp->id.context; - transpArgs->id.d32[1] = qp->id.resource; - - if (transpEvent) { - transpOut->event = *transpEvent; - } else { - transpOut->event.ioEvent = NULL; - transpOut->event.ioEventData = NULL; - } - -#if defined(COMM_BUILDING_SERVER) - CommOS_Debug(("%s: Registering detach CB on id %u...\n", - __FUNCTION__, transpArgs->id.d32[1])); - QP_RegisterDetachCB(transpOut->qp, DetachCB, transpOut); -#endif - - transpOut->peerEvID = COMM_TRANSP_ID_32_ANY; - transpOut->writeSize = 0; - transpOut->readSize = 0; - CommOS_InitWork(&transpOut->work, RaiseEvent); - CommOS_WriteAtomic(&transpOut->raiseInline, 0); - - if (TranspTableAdd(transpOut)) { - CommOS_Log(("%s: Exceeded max limit of transport objects!\n", - __FUNCTION__)); - DestroyTransp(transpOut); - rc = -1; - goto out; - } - - *transp = transpOut; - rc = 0; - - CommOS_Log(("%s: Channel attached.\n", __FUNCTION__)); - -out: - if (rc && transpOut) { - CommOS_Log(("%s: Failed to attach: %d\n", __FUNCTION__, rc)); - CommOS_Kfree(transpOut); - } - - return rc; -} - - -/** - * @brief Tear down the transport channel, destroy the object if the refcount - * drops to zero - * @param transp handle to the transport channel - * @sideeffects decrements the entry's refcount - */ - -void -CommTransp_Close(CommTransp transp) { - if (!transp) { - return; - } - CommOS_FlushAIOWork(&transp->work); - TranspTablePut(transp); -} - - -/** - * @brief Returns available space for enqueue, in bytes - * @param transp handle to the transport object - * @return available space in the queue for enqueue operations, <0 - * on error conditions. see qp.h for error codes. - */ - -int -CommTransp_EnqueueSpace(CommTransp transp) -{ - if (!transp) { - return -1; - } - return QP_EnqueueSpace(transp->qp); -} - - -/** - * @brief Discards any pending enqueues - * @param transp handle to the transport object - * @return 0 on success, <0 otherwise. see qp.h for error codes - */ - -int -CommTransp_EnqueueReset(CommTransp transp) -{ - if (!transp) { - return -1; - } - transp->writeSize = 0; - return QP_EnqueueReset(transp->qp); -} - - -/** - * @brief Enqueues a segment of data into the transport object - * @param transp handle to the transport object - * @param buf data to enqueue - * @param bufLen number of bytes to enqueue - * @return number of bytes enqueued on success, <0 otherwise. see qp.h - * for error codes - */ - -int -CommTransp_EnqueueSegment(CommTransp transp, - const void *buf, - unsigned int bufLen) -{ - int rc; - - if (!transp) { - return -1; - } - rc = QP_EnqueueSegment(transp->qp, (void*)buf, bufLen); - if (rc >= 0) { - transp->writeSize += (unsigned int)rc; - } else { - transp->writeSize = 0; - } - return rc; -} - - -/** - * @brief Commits any previous EnqueueSegment operations to the transport - * object. - * @param transp handle to the transport object. - * @return 0 on success, < 0 otherwise. - */ - -int -CommTransp_EnqueueCommit(CommTransp transp) -{ - int rc; - - if (!transp) { - return -1; - } - - rc = QP_EnqueueCommit(transp->qp); - if (rc >= 0) { - const unsigned int fudge = 4; - int writable = CommTransp_EnqueueSpace(transp); - - if ((writable >= 0) && - ((transp->writeSize + (unsigned int)writable + fudge) >= - transp->qp->queueSize)) { - /* - * If bytes written since last commit + writable space 'almost' - * equal write queue size, then signal. The 'almost' fudge factor - * accounts for a possibly inaccurate CommTransp_EnqueueSpace() - * return value. Most of the time, this is inconsequential. In - * rare, borderline occasions, it results in a few extra signals. - * The scheme essentially means this: if this is the first packet - * to be write-committed, we signal. Otherwise, the remote end is - * supposed to keep going for as long as it can read. - * - */ - - BUG_ON(transp->backRef >= QP_MAX_QUEUE_PAIRS); - CommOS_AddReturnAtomic(&transpTable[transp->backRef].holds, 1); - if (CommOS_ReadAtomic(&transp->raiseInline)) { - RaiseEvent(&transp->work); - } else if (CommOS_ScheduleAIOWork(&transp->work)) { - TranspTablePutNF(transp); - } - } - } else { - rc = -1; - } - transp->writeSize = 0; - return rc; -} - - -/** - * @brief Returns any available bytes for dequeue - * @param transp handle to the transport object - * @return available bytes for dequeue, <0 otherwise. see qp.h for error codes - */ - -int -CommTransp_DequeueSpace(CommTransp transp) -{ - if (!transp) { - return -1; - } - return QP_DequeueSpace(transp->qp); -} - - -/** - * @brief Discards any pending dequeues - * @param transp handle to the transport object - * @return 0 on success, <0 otherwise, see qp.h for error codes - */ - -int -CommTransp_DequeueReset(CommTransp transp) -{ - if (!transp) { - return -1; - } - transp->readSize = 0; - return QP_DequeueReset(transp->qp); -} - - -/** - * @brief Dequeues a segment of data from the consumer queue into - * a buffer - * @param transp handle to the transport object - * @param[out] buf buffer to copy to - * @param bufLen number of bytes to dequeue - * @return number of bytes dequeued on success, <0 otherwise, - * see qp.h for error codes - */ - -int -CommTransp_DequeueSegment(CommTransp transp, - void *buf, - unsigned bufLen) -{ - int rc; - - if (!transp) { - return -1; - } - rc = QP_DequeueSegment(transp->qp, buf, bufLen); - if (rc >= 0) { - transp->readSize += (unsigned int)rc; - } else { - transp->readSize = 0; - } - return rc; -} - - -/** - * @brief Commits any previous DequeueSegment operations to the - * transport object. - * @param transp handle to the transport object. - * @return 0 on success, < 0 otherwise. - */ - -int -CommTransp_DequeueCommit(CommTransp transp) -{ - int rc; - - if (!transp) { - return -1; - } - rc = QP_DequeueCommit(transp->qp); - if (rc >= 0) { - int readable = CommTransp_DequeueSpace(transp); - const unsigned int limit = transp->qp->queueSize / 2; - - if ((readable >= 0) && - (transp->readSize + (unsigned int)readable >= limit) && - ((unsigned int)readable < limit)) { - /* - * Minimize the number of likely 'peer write OK' signalling: - * only do it, if reading crossed half-way down. - * - */ - - BUG_ON(transp->backRef >= QP_MAX_QUEUE_PAIRS); - CommOS_AddReturnAtomic(&transpTable[transp->backRef].holds, 1); - if (CommOS_ReadAtomic(&transp->raiseInline)) { - RaiseEvent(&transp->work); - } else if (CommOS_ScheduleAIOWork(&transp->work)) { - TranspTablePut(transp); - } - } - } else { - rc = -1; - } - /* coverity[deref_after_free] */ - transp->readSize = 0; - return rc; -} - - -/** - * @brief Notify any registered listeners for the given queue pair - * @param notificationCenterID noop, unused on MVP - * @param transpArgs initialization arguments used by the guest for this - * channel - * @sideeffects the host may attach to the queue pair - */ - -int -CommTransp_Notify(const CommTranspID *notificationCenterID, - CommTranspInitArgs *transpArgs) -{ - QPInitArgs args; - - args.id.context = transpArgs->id.d32[0]; - args.id.resource = transpArgs->id.d32[1]; - args.capacity = transpArgs->capacity; - args.type = transpArgs->type; - - CommOS_Debug(("%s: d32[0]: %u d32[1]: %u\n", - __FUNCTION__, - transpArgs->id.d32[0], - transpArgs->id.d32[1])); - QP_Notify(&args); - return 0; -} diff --git a/arch/arm/mvp/commkm/fatalerror.h b/arch/arm/mvp/commkm/fatalerror.h deleted file mode 100644 index 9676ff3..0000000 --- a/arch/arm/mvp/commkm/fatalerror.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief fatal error handlers. They all post fatal errors regardless of build - * type. - */ - -#ifndef _FATALERROR_H -#define _FATALERROR_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mvp_compiler.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum FECode { - FECodeMisc, ///< generic FATAL() call of sorts - FECodeOOM, ///< FATAL_OOM() call of sorts - FECodeAssert, ///< ASSERT() call of sorts - FECodeNR, ///< NOT_REACHED() call of sorts - FECodeNI, ///< NOT_IMPLEMENTED() call of sorts - FECodeNT, ///< NOT_TESTED() call of sorts - FECodeCF ///< COMPILE_FAIL() call of sorts -}; -typedef enum FECode FECode; - -#define FATAL() FatalError(__FILE__, __LINE__, FECodeMisc, 0, NULL) -#define FATAL_IF(x) do { if (UNLIKELY(x)) FATAL(); } while (0) -#define FATAL_OOM() FatalError(__FILE__, __LINE__, FECodeOOM, 0, NULL) -#define FATAL_OOM_IF(x) do { if (UNLIKELY(x)) FATAL_OOM(); } while (0) - -extern _Bool FatalError_hit; - -void NORETURN FatalError(char const *file, - int line, - FECode feCode, - int bugno, - char const *fmt, - ...) FORMAT(printf,5,6); - -#define FATALERROR_COMMON(printFunc, \ - printFuncV, \ - file, \ - line, \ - feCode, \ - bugno, \ - fmt) { \ - va_list ap; \ - \ - printFunc("FatalError: %s:%d, code %d, bugno %d\n", \ - file, line, feCode, bugno); \ - if (fmt != NULL) { \ - va_start(ap, fmt); \ - printFuncV(fmt, ap); \ - va_end(ap); \ - } \ - } - -#if defined IN_HOSTUSER || defined IN_GUESTUSER || defined IN_WORKSTATION - -#define FATALERROR_POSIX_USER \ -void \ -FatalError_VErrPrintf(const char *fmt, va_list ap) \ -{ \ - vfprintf(stderr, fmt, ap); \ -} \ -\ -void \ -FatalError_ErrPrintf(const char *fmt, ...) \ -{ \ - va_list ap; \ - va_start(ap, fmt); \ - FatalError_VErrPrintf(fmt, ap); \ - va_end(ap); \ -} \ -\ -void NORETURN \ -FatalError(char const *file, \ - int line, \ - FECode feCode, \ - int bugno, \ - const char *fmt, \ - ...) \ -{ \ - FATALERROR_COMMON(FatalError_ErrPrintf, FatalError_VErrPrintf, file, line, feCode, bugno, fmt); \ - exit(EXIT_FAILURE); \ -} -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/arch/arm/mvp/commkm/include_check.h b/arch/arm/mvp/commkm/include_check.h deleted file mode 100644 index 2eeafe7..0000000 --- a/arch/arm/mvp/commkm/include_check.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for Empty File Placeholder - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ diff --git a/arch/arm/mvp/commkm/mksck.h b/arch/arm/mvp/commkm/mksck.h deleted file mode 100644 index e9e10bc..0000000 --- a/arch/arm/mvp/commkm/mksck.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#ifndef _MKSCK_H -#define _MKSCK_H - -/** - * @file - * - * @brief The monitor-kernel socket interface definitions. - * - * The monitor kernel socket interface was created for (what the name - * says) communications between the monitor and host processes. On the - * monitor side a special API is introduced, see mksck_vmm.h. On the - * host side the API is the standard Berkeley socket interface. Host - * process to host process or monitor to monitor communication is not - * supported. - * - * A generic address consists of two 16 bit fields: the vm id and the - * port id. Both hosts (vmx) and monitors (vmm) get their vm id - * automatically. The host vm id is assigned at the time the host - * process opens the mvpkm file descriptor, while the monitor vm id is - * assigned when the vmx.c:SetupWorldSwitchPage() calls - * Mvpkm_SetupIds(). As a vmx may create multiple monitors to service - * an MP guest, a vmx vm id may be associated with multiple monitor vm - * ids. A monitor id, however, has a single associated vmx host id, - * the id of its canonical vmx. - * - * Sockets on the host get their addresses either by explicit user - * call (the bind command) or implicitly by (issuing a send command - * first). At an explicit bind the user may omit one or both fields by - * providing MKSCK_VMID_UNDEF/MKSCK_PORT_UNDEF respectively. An - * implicit bind behaves as if both fields were omitted in an explicit - * bind. The default value of the vmid field is the vmid computed from - * the thread group id while that of a port is a new number. It is not - * invalid to bind a host process socket with a vm id different from - * the vmid computed from the tgid. - * - * Sockets of the monitor are automatically assigned a vmid, that of their - * monitor, at the time of their creation. The port id can be assigned by the - * user or left to the implementation to assign an unused one (by specifying - * MKSCK_PORT_UNDEF at @ref Mksck_Open). - * - * Host unconnected sockets may receive from any monitor sender, may send to any - * monitor socket. A socket can be connected to a peer address, that enables the - * use of the send command. - * - * One of many special predefined port (both host and monitor) is - * MKSCK_PORT_MASTER. It is used for initialization. - * - * Monitor sockets have to send their peer address explicitly (by - * Mksck_SetPeer()) or implicitly by receiving first. After the peer - * is set, monitor sockets may send or receive only to/from their - * peer. - */ - - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "vmid.h" - -/* - * The interface limits the size of transferable packets. - */ -#define MKSCK_XFER_MAX 1024 - -#define MKSCK_ADDR_UNDEF (uint32)0xffffffff - -#define MKSCK_PORT_UNDEF (uint16)0xffff -#define MKSCK_PORT_MASTER (MKSCK_PORT_UNDEF-1) -#define MKSCK_PORT_HOST_FB (MKSCK_PORT_UNDEF-2) -#define MKSCK_PORT_BALLOON (MKSCK_PORT_UNDEF-3) -#define MKSCK_PORT_HOST_HID (MKSCK_PORT_UNDEF-4) -#define MKSCK_PORT_CHECKPOINT (MKSCK_PORT_UNDEF-5) -#define MKSCK_PORT_COMM_EV (MKSCK_PORT_UNDEF-6) -#define MKSCK_PORT_HIGH (MKSCK_PORT_UNDEF-7) - -#define MKSCK_VMID_UNDEF VMID_UNDEF -#define MKSCK_VMID_HIGH (MKSCK_VMID_UNDEF-1) - -#define MKSCK_DETACH 3 - -typedef uint16 Mksck_Port; -typedef VmId Mksck_VmId; - -/** - * @brief Page descriptor for typed messages. Each page describes a region of - * the machine address space with base mpn and size 2^(12 + order) bytes. - */ -typedef struct { - uint32 mpn : 20; ///< Base MPN of region described by page - uint32 order : 12; ///< Region is 2^(12 + order) bytes. -} Mksck_PageDesc; - -/** - * @brief Typed message template macro. Allows us to avoid having two message - * types, one with page descriptor vector (for VMM), one without (for - * VMX). - * - * @param type C type of uninterpreted component of the message (following the - * page descriptor vector). - * @param pages number of page descriptors in vector. - */ -#define MKSCK_DESC_TYPE(type,pages) \ - struct { \ - type umsg; \ - Mksck_PageDesc page[pages]; \ - } - -/** - * @brief The monitor kernel socket interface address format - */ -typedef union { - uint32 addr; ///< the address - struct { /* The address is decomposed to two shorts */ - Mksck_Port port; ///< port unique within a vmid - Mksck_VmId vmId; ///< unique vmid - }; -} Mksck_Address; - -static inline uint32 -Mksck_AddrInit(Mksck_VmId vmId, Mksck_Port port) -{ - Mksck_Address aa; - aa.vmId = vmId; - aa.port = port; - return aa.addr; -} -#endif diff --git a/arch/arm/mvp/commkm/mksck_sockaddr.h b/arch/arm/mvp/commkm/mksck_sockaddr.h deleted file mode 100644 index 82df240..0000000 --- a/arch/arm/mvp/commkm/mksck_sockaddr.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Host user space definitions for mksck sockets. - */ - -#ifndef _MKSCK_SOCKADDR_H_ -#define _MKSCK_SOCKADDR_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mksck.h" - -/* no one ever uses DECnet anymore? */ -#define AF_MKSCK AF_DECnet -#define PF_MKSCK PF_DECnet - -/* Address structure used by the host user socket interface. */ -struct sockaddr_mk { - sa_family_t mk_family; - Mksck_Address mk_addr; -}; - -#endif diff --git a/arch/arm/mvp/commkm/mvp.h b/arch/arm/mvp/commkm/mvp.h deleted file mode 100644 index a57f8cc..0000000 --- a/arch/arm/mvp/commkm/mvp.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief top-level include for all basic includes. - * This file should not define anything of its own. - */ - -#ifndef _MVP_H -#define _MVP_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mvp_compiler.h" -#include "utils.h" -#include "mvp_assert.h" -#include "mvp_types.h" -#include "platdefx.h" - -#endif diff --git a/arch/arm/mvp/commkm/mvp_assert.h b/arch/arm/mvp/commkm/mvp_assert.h deleted file mode 100644 index cbc5ed8..0000000 --- a/arch/arm/mvp/commkm/mvp_assert.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief ASSERT() and related macros. - */ - -#ifndef _MVP_ASSERT_H -#define _MVP_ASSERT_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define ASSERT(_x) ASSERT_BUG((_x),0) - -#ifndef NDEBUG -#define ASSERT_BUG(_x,_tkt) do { \ - if (UNLIKELY(!(_x))) { \ - FatalError(__FILE__, __LINE__, FECodeAssert, _tkt, NULL); \ - } \ -} while (0) - -#define ASSERTF(_x, ...) do { \ - if (UNLIKELY(!(_x))) { \ - FatalError(__FILE__, \ - __LINE__, \ - FECodeAssert, \ - 0, \ - __VA_ARGS__); \ - } \ -} while (0) -#else - -#define ASSERT_BUG(_x,_tkt) (void)sizeof((int)(_x)) -#define ASSERTF(_x, ...) ASSERT_BUG(_x, 0) - -#endif - -/* - * Compile-time assertions. - * - * ASSERT_ON_COMPILE does not use the common - * switch (0) { case 0: case (e): ; } trick because some compilers (e.g. MSVC) - * generate code for it. - * - * The implementation uses both enum and typedef because the typedef alone is - * insufficient; gcc allows arrays to be declared with non-constant expressions - * (even in typedefs, where it makes no sense). - */ -#ifdef __COVERITY__ -#define ASSERT_ON_COMPILE(e) ASSERT(e) -#else -#define ASSERT_ON_COMPILE(e) \ - do { \ - enum { AssertOnCompileMisused = ((e) ? 1 : -1) }; \ - typedef char AssertOnCompileFailed[AssertOnCompileMisused]; \ - } while (0) -#endif - -/* - * To put an ASSERT_ON_COMPILE() outside a function, wrap it - * in MY_ASSERTS(). The first parameter must be unique in - * each .c file where it appears. For example, - * - * MY_ASSERTS(FS3_INT, - * ASSERT_ON_COMPILE(sizeof(FS3_DiskLock) == 128); - * ASSERT_ON_COMPILE(sizeof(FS3_DiskLockReserved) == DISK_BLOCK_SIZE); - * ASSERT_ON_COMPILE(sizeof(FS3_DiskBlock) == DISK_BLOCK_SIZE); - * ASSERT_ON_COMPILE(sizeof(Hardware_DMIUUID) == 16); - * ) - * - * Caution: ASSERT() within MY_ASSERTS() is silently ignored. - * The same goes for anything else not evaluated at compile time. - */ - -#define MY_ASSERTS(name, assertions) \ - static inline void name(void) { \ - assertions \ - } - -#define KNOWN_BUG(_tkt) - -#define NOT_IMPLEMENTED() NOT_IMPLEMENTED_JIRA(0) -#define NOT_IMPLEMENTED_JIRA(_tkt,...) FatalError(__FILE__, __LINE__, FECodeNI, _tkt, NULL) - -#define NOT_IMPLEMENTED_IF(_x) NOT_IMPLEMENTED_IF_JIRA((_x),0) -#define NOT_IMPLEMENTED_IF_JIRA(_x,_tkt,...) do { if (UNLIKELY(_x)) NOT_IMPLEMENTED_JIRA(_tkt); } while (0) -/* - * All sites tagged with this are @knownjira{MVP-1855}. - */ -#define NOT_IMPLEMENTEDF(...) FatalError(__FILE__, __LINE__, FECodeNI, 0, __VA_ARGS__) - -#define NOT_REACHED() FatalError(__FILE__, __LINE__, FECodeNR, 0, NULL) - -#include "fatalerror.h" -#include "nottested.h" - -#endif diff --git a/arch/arm/mvp/commkm/mvp_compiler.h b/arch/arm/mvp/commkm/mvp_compiler.h deleted file mode 100644 index 21af455..0000000 --- a/arch/arm/mvp/commkm/mvp_compiler.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Compiler-related definitions and directives. - */ - -#ifndef _MVP_COMPILER_H_ -#define _MVP_COMPILER_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#ifdef __GNUC__ -#include "mvp_compiler_gcc.h" -#else /* __GNUC__ */ -#include "mvp_compiler_other.h" -#endif /* __GNUC__ */ - -/** - * @brief Find last set bit. - * - * @param n unsigned 32-bit integer. - * - * @return 0 if n == 0 otherwise 32 - the number of leading zeroes in n. - */ -#define FLS(n) (32 - CLZ(n)) - -#endif /// ifndef _MVP_COMPILER_H_ diff --git a/arch/arm/mvp/commkm/mvp_compiler_gcc.h b/arch/arm/mvp/commkm/mvp_compiler_gcc.h deleted file mode 100644 index fbc96e3..0000000 --- a/arch/arm/mvp/commkm/mvp_compiler_gcc.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief common definitions for GCC - */ - -#ifndef _MVP_COMPILER_GCC_H -#define _MVP_COMPILER_GCC_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @brief Count leading zeroes. - * - * @param n unsigned 32-bit integer. - * - * @return 32 if n == 0 otherwise 31 - the bit position of the most significant 1 - * in n. - */ -#ifdef __COVERITY__ -static inline int -CLZ(unsigned int n) -{ - unsigned int r = 0; - - while (n) { - r++; - n >>= 1; - } - - return 32 - r; -} -#else -#define CLZ(n) __builtin_clz(n) -#endif - -#define PACKED __attribute__ ((packed)) -#define ALLOC __attribute__ ((malloc, warn_unused_result)) -#define UNUSED __attribute__ ((unused)) -#define PURE __attribute__ ((pure)) -#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) -#define FORMAT(x,y,z) __attribute__ ((format(x,y,z))) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect((x), 0) - -/* - * For debug builds, we want to omit __attribute__((noreturn)) so that gcc will - * keep stack linkages and then we will have useful core dumps. For non-debug - * builds, we don't care about the stack frames and want the little bit of - * optimization that noreturn gives us. - */ -#if defined(__COVERITY__) || !defined(MVP_DEBUG) -#define NORETURN __attribute__((noreturn)) -#else -#define NORETURN -#endif - -#endif diff --git a/arch/arm/mvp/commkm/mvp_types.h b/arch/arm/mvp/commkm/mvp_types.h deleted file mode 100644 index ba5c04c..0000000 --- a/arch/arm/mvp/commkm/mvp_types.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief basic type definitions. - * These may need to be conditionalized for different compilers/platforms. - */ - -#ifndef _MVPTYPES_H -#define _MVPTYPES_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; -typedef unsigned long long uint64; - -typedef signed char int8; -typedef short int16; -typedef int int32; -typedef long long int64; - -typedef uint32 CVA; // whatever we are compiling the code as -typedef uint32 GVA; // guest virtual addresses -typedef uint32 MVA; // monitor virtual addresses -typedef uint32 HKVA; // host kernel virtual addresses -typedef uint32 HUVA; // host user virtual addresses -typedef uint64 PA; // (guest) physical addresses (40-bit) -typedef uint32 MA; // (host) machine addresses - -typedef uint32 PPN; // PA/PAGE_SIZE -typedef uint32 MPN; // MA/PAGE_SIZE - -typedef uint64 cycle_t; - -/** - * @brief Page segment. - * - * Specifies a segment within a single page. - */ -typedef struct { - uint16 off; - uint16 len; -} PageSeg; - -/* - * GCC's argument checking for printf-like functions - * - * fmtPos is the position of the format string argument, beginning at 1 - * varPos is the position of the variable argument, beginning at 1 - */ - -#if defined(__GNUC__) -# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos))) -#else -# define PRINTF_DECL(fmtPos, varPos) -#endif - -#if defined(__GNUC__) -# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos))) -#else -# define SCANF_DECL(fmtPos, varPos) -#endif - -#endif /* _MVPTYPES_H */ diff --git a/arch/arm/mvp/commkm/mvpkm_comm_ev.h b/arch/arm/mvp/commkm/mvpkm_comm_ev.h deleted file mode 100644 index b220a9b..0000000 --- a/arch/arm/mvp/commkm/mvpkm_comm_ev.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief mvpkm kernel hooks for comm event signaling - */ - -#ifndef _MVPKM_COMM_EV_H -#define _MVPKM_COMM_EV_H - -extern int (*CommTranspEvProcess)(CommTranspID* id, CommTranspIOEvent event); - -/** - * @brief Forward any guest signal requests to the commkm module - * @param id transport channel id - * @param event comm event type - */ - -static inline void -Mvpkm_CommEvSignal(CommTranspID *id, CommTranspIOEvent event) -{ - if (CommTranspEvProcess) { - CommTranspEvProcess(id, event); - } -} - -void -Mvpkm_CommEvRegisterProcessCB(int (*commProcessFunc)(CommTranspID*, - CommTranspIOEvent)); -void Mvpkm_CommEvUnregisterProcessCB(void); - - - -#endif diff --git a/arch/arm/mvp/commkm/nottested.h b/arch/arm/mvp/commkm/nottested.h deleted file mode 100644 index c5c1e26..0000000 --- a/arch/arm/mvp/commkm/nottested.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief NOT_TESTED() and related. - */ - -#ifndef _NOTTESTED_H -#define _NOTTESTED_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include - -#ifdef NOT_TESTED_ENABLED -#define NotTestedEnabled true -#else -#define NotTestedEnabled false -#endif - -#define NOT_TESTED() NOT_TESTED_JIRA(0) -#define NOT_TESTED_JIRA(_tkt,...) NotTested(_tkt, __FILE__, __LINE__) - -void NotTested(int tkt, char const *file, int line); - -#endif diff --git a/arch/arm/mvp/commkm/platdefx.h b/arch/arm/mvp/commkm/platdefx.h deleted file mode 100644 index 42953e6..0000000 --- a/arch/arm/mvp/commkm/platdefx.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Basic platform definitions needed various places. - */ - -#ifndef _PLATDEFX_H -#define _PLATDEFX_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define PAGE_ORDER 12 - -#ifndef PAGE_SIZE -#define PAGE_SIZE (1UL << PAGE_ORDER) -#endif -#if PAGE_SIZE != 4096 -#error bad page size PAGE_SIZE -#endif - -#define PA_2_PPN(_pa) ((_pa) / PAGE_SIZE) -#define PPN_2_PA(_ppn) ((_ppn) * PAGE_SIZE) - -#define VMM_DOMAIN 0x0 -#define VMM_DOMAIN_NO_ACCESS 0x3 -#define VMM_DOMAIN_CLIENT 0x1 -#define VMM_DOMAIN_MANAGER 0x4 - -#define INVALID_CVA (-(CVA)1) -#define INVALID_GVA (-(GVA)1) -#define INVALID_MVA (-(MVA)1) -#define INVALID_HKVA (-(HKVA)1) -#define INVALID_HUVA (-(HUVA)1) - -#define INVALID_MPN (((MPN)-1) >> ARM_L2D_SMALL_ORDER) -#define INVALID_PPN (((PPN)-1) >> ARM_L2D_SMALL_ORDER) - -#endif diff --git a/arch/arm/mvp/commkm/qp.h b/arch/arm/mvp/commkm/qp.h deleted file mode 100644 index d4a50ec..0000000 --- a/arch/arm/mvp/commkm/qp.h +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MVP Queue Pairs function and structure declarations - * - * MVP Queue Pairs: - * - * Queue pairs are intended to be a generic bulk data transport mechanism - * between the guest and host kernels. The queue pair abstraction is based - * on two ring buffers (queues) placed on a shared memory region mapped - * into both guest and host kernel address spaces. - * - * NOTE: Queue pairs are SINGLE-READER, SINGLE-WRITER. Any caller is - * responsible for multi-reader/writer serialization!!! - * - * There are a maximum of QP_MAX_QUEUE_PAIRS in the system, with a maximum - * size of QP_MAX_CAPACITY per pair. Each queue pair is identified by - * an ID. - * - * Each peer follows a producer-consumer model in which one side is the - * producer on one queue, and the other side is the consumer on that queue - * (and vice-versa for its pair). - * - * Data is enqueued and dequeued into the pair in transactional stages, - * meaning each enqueue/dequeue can be followed by zero or more - * enqueue/dequeues, but the enqueue/dequeue is not visible to the peer - * until it has been committed with the *Commit() function. - * In PVTCP, for example, this is used to enqueue a short header, then - * followed by 'segments' of iovecs, then followed by a commit. This - * model prevents a peer from reading the header, expecting a payload, - * but not being able to read the payload because it hasn't been - * enqueued yet. - * - * Queue Pair setup: - * - * Before data can be passed, the guest and host kernel must perform - * the following connection handshake: - * - * 1). A host kernel service registers a listener with the queue pair - * subsystem with a callback to be called when guests create - * and attach to a shared memory region. - * - * 2). Guest initiates an QP_Attach() operation to a shared memory region - * keyed by ID. This step allocates memory, maps it into the host - * address space, and optionally notifies any host services who are - * listening for attach requests from the guest (see previous step). - * Host listeners are provided with a copy of the initialization - * arguments used by the guest (id, size, service type). All registered - * listeners are iterated over until one of them handles the attach - * request and acknowledges with QP_SUCCESS. - * - * 3). The registered host callback is called, notifying the host that - * the guest has attached. - * - * 4). The host can now QP_Attach() to the shared memory region with the same - * arguments as the guest. The queue pair is now well formed and enqueues - * and dequeues can proceed on either side. - * - * Queue Pair teardown: - * - * 1). As before, teardowns are initiated by the guest. Hosts can register - * a callback to be called upon detach. Guests initiate a teardown - * through a call to QP_Detach(). - * - * 2). Registered hosts are notified through the aforementioned callback. - * 3). The host service can call QP_Detach() at its own leisure. Memory - * is freed, the queue pair is destroyed. - * - * If at any point the guest unexpectedly shuts down, the host will be - * notified at monitor shutdown time. Memory is freed, and the queue - * pair is destroyed. - * - */ - -#ifndef _QP_H -#define _QP_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -//#define QP_DEBUG 1 - -typedef enum QPState { - QP_STATE_FREE = 0x1, ///< No peers, not memory-backed - QP_STATE_CONNECTED, ///< Both peers attached , memory backed - QP_STATE_GUEST_ATTACHED, ///< Guest allocated memory, host not yet attached - QP_STATE_MAX // leave this at the end! -} QPState; - -typedef struct QPId { - uint32 context; - uint32 resource; -} QPId; - -/* - * Initialization arguments for each queue pair - */ -typedef struct QPInitArgs { - QPId id; ///< Shared memory region ID - uint32 capacity; ///< Total size of shared region in bytes - uint32 type; ///< Type of queue pair (PVTCP, other)... -} QPInitArgs; - -/* - * Placed on the shared region, two per region - */ -typedef struct QHandle { - volatile uint32 head; ///< queue head offset - volatile uint32 tail; ///< queue tail offset - volatile uint32 phantom_head; ///< queue shadow head offset - volatile uint32 phantom_tail; ///< queue shadow tail offset - uint8 data[0]; ///< start of data, runs off - // the struct -} QHandle; - -/* - * Local to each peer - */ -typedef struct QPHandle { - QPId id; ///< shared memory region ID - uint32 capacity; ///< size of region in bytes - QHandle *produceQ; ///< producer queue - QHandle *consumeQ; ///< consumer queue - uint32 queueSize; ///< size of each queue in bytes - uint32 type; ///< type of queue pair - - /* - * Following fields unused by guest - */ - QPState state; - void (*peerDetachCB)(void* data); ///< detach notification callback - void *detachData; ///< data for the detach cb - struct page **pages; ///< page pointers for shared region -} QPHandle; - -/* - * QP Error codes - */ -#define QP_SUCCESS 0 -#define QP_ERROR_NO_MEM (-1) -#define QP_ERROR_INVALID_HANDLE (-2) -#define QP_ERROR_INVALID_ARGS (-3) -#define QP_ERROR_ALREADY_ATTACHED (-4) - -/* - * Hard-coded limits - */ -#define QP_MIN_CAPACITY (PAGE_SIZE * 2) -#define QP_MAX_CAPACITY (1024*1024) // 1M -#define QP_MAX_QUEUE_PAIRS 32 -#define QP_MAX_ID QP_MAX_QUEUE_PAIRS -#define QP_MAX_LISTENERS QP_MAX_QUEUE_PAIRS -#define QP_MAX_PAGES (QP_MAX_CAPACITY/PAGE_SIZE) // 256 pages - -#define QP_INVALID_ID 0xFFFFFFFF -#define QP_INVALID_SIZE 0xFFFFFFFF -#define QP_INVALID_REGION 0xFFFFFFFF -#define QP_INVALID_TYPE 0xFFFFFFFF - -#ifdef __KERNEL__ -/** - * @brief Utility function to sanity check arguments - * @param args argument structure to check - * @return true if arguments are sane, false otherwise - */ -static inline -_Bool QP_CheckArgs(QPInitArgs *args) -{ - if (!args || - !is_power_of_2(args->capacity) || - (args->capacity < QP_MIN_CAPACITY) || - (args->capacity > QP_MAX_CAPACITY) || - !(args->id.resource < QP_MAX_ID || args->id.resource == QP_INVALID_ID) || - (args->type == QP_INVALID_TYPE)) { - return false; - } else { - return true; - } -} -#endif - - -/** - * @brief Utility function to sanity check a queue pair handle - * @param qp handle to the queue pair - * @return true if the handle is sane, false otherwise - */ -static inline -_Bool QP_CheckHandle(QPHandle *qp) -{ -#ifdef MVP_DEBUG - if (!(qp) || - !(qp->produceQ) || - !(qp->consumeQ) || - (qp->state >= (uint32)QP_STATE_MAX) || - !(qp->queueSize < (QP_MAX_CAPACITY/2))) { - return false; - } else { - return true; - } -#else - return true; -#endif -} - - -/** - * @brief Initializes an invalid handle - * @param[in, out] qp handle to the queue pair - */ -static inline void -QP_MakeInvalidQPHandle(QPHandle *qp) -{ - if (!qp) { - return; - } - - qp->id.context = QP_INVALID_ID; - qp->id.resource = QP_INVALID_ID; - qp->capacity = QP_INVALID_SIZE; - qp->produceQ = NULL; - qp->consumeQ = NULL; - qp->queueSize = QP_INVALID_SIZE; - qp->type = QP_INVALID_TYPE; - qp->state = QP_STATE_FREE; - qp->peerDetachCB = NULL; - qp->detachData = NULL; -} - -/* - * Host only - */ -typedef int32 (*QPListener)(const QPInitArgs*); -int32 QP_RegisterListener(const QPListener); -int32 QP_UnregisterListener(const QPListener); -int32 QP_RegisterDetachCB(QPHandle *qp, void (*callback)(void*), void *data); - - -/* - * Host and guest specific implementations, see qp_host.c and qp_guest.c - */ -int32 QP_Attach(QPInitArgs *args, QPHandle** qp); -int32 QP_Detach(QPHandle* qp); -int32 QP_Notify(QPInitArgs *args); - -/* - * Common implementation, see qp_common.c - */ -int32 QP_EnqueueSpace(QPHandle *qp); -int32 QP_EnqueueSegment(QPHandle *qp, const void *buf, size_t length); -int32 QP_EnqueueCommit(QPHandle *qp); -int32 QP_EnqueueReset(QPHandle *qp); - -static inline int32 -QP_EnqueueAtomic(QPHandle *qp, const void *buf, size_t length) -{ - int32 rc; - QP_EnqueueReset(qp); - rc = QP_EnqueueSegment(qp, buf, length); - if (rc < 0) { - return rc; - } else { - QP_EnqueueCommit(qp); - } - return rc; -} - -int32 QP_DequeueSpace(QPHandle *qp); -int32 QP_DequeueSegment(QPHandle *qp, const void *buf, size_t length); -int32 QP_DequeueReset(QPHandle *qp); -int32 QP_DequeueCommit(QPHandle *qp); - -static inline int32 -QP_DequeueAtomic(QPHandle *qp, const void *buf, size_t length) -{ - int32 rc; - QP_DequeueReset(qp); - rc = QP_DequeueSegment(qp, buf, length); - if (rc < 0) { - return rc; - } else { - QP_DequeueCommit(qp); - } - return rc; -} - -/* - * HVC methods and signatures - */ -#define MVP_QP_SIGNATURE 0x53525051 ///< 'QPRS' -#define MVP_QP_ATTACH (MVP_OBJECT_CUSTOM_BASE + 0) ///< attach to a queue pair -#define MVP_QP_DETACH (MVP_OBJECT_CUSTOM_BASE + 1) ///< detach from a queue pair -#define MVP_QP_NOTIFY (MVP_OBJECT_CUSTOM_BASE + 2) ///< notify host of attach -#define MVP_QP_LAST (MVP_OBJECT_CUSTOM_BASE + 3) ///< Number of methods - -/* - * Debug macros - */ -#ifdef QP_DEBUG - #ifdef IN_MONITOR - #define QP_DBG(...) Log(__VA_ARGS__) - #else - #define QP_DBG(...) printk(KERN_INFO __VA_ARGS__) - #endif -#else - #define QP_DBG(...) -#endif - -#endif diff --git a/arch/arm/mvp/commkm/utils.h b/arch/arm/mvp/commkm/utils.h deleted file mode 100644 index b5f1e18..0000000 --- a/arch/arm/mvp/commkm/utils.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief General architecture-independent definitions, typedefs, and macros. - */ - -#ifndef _UTILS_H -#define _UTILS_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define MAX_FILENAME 128 - -// Round address up to given size boundary -// Note: ALIGN() conflicts with Linux - -#define MVP_ALIGN(_v, _n) (((_v) + (_n) - 1) & -(_n)) - -#define ALIGNVA(_addr, _size) MVP_ALIGN(_addr, _size) - -#define alignof(t) offsetof(struct { char c; typeof(t) x; }, x) - -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#define MAX(x,y) ((x) > (y) ? (x) : (y)) - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#define KB(_X_) ((_X_)*1024U) -#define MB(_X_) (KB(_X_)*1024) -#define GB(_X_) (MB(_X_)*1024) - -#define NELEM(x) (sizeof(x)/sizeof((x)[0])) - -/* - * x in [low,high) - * args evaluated once - */ -#define RANGE(x,low,high) \ - ({ \ - typeof(x) _x = (x); \ - typeof(x) _low = (typeof(x))(low); \ - typeof(x) _high =(typeof(x))(high); \ - (_Bool)( (_low <= _x) && (_x < _high)); \ - }) - -#define OBJECTS_PER_PAGE(_type) (PAGE_SIZE / sizeof(_type)) - -#define MA_2_MPN(_ma) ((MPN)((_ma) / PAGE_SIZE)) -#define MPN_2_MA(_mpn) ((MA)((_mpn) * PAGE_SIZE)) - -#define VA_2_VPN(_va) ((_va) / PAGE_SIZE) -#define VPN_2_vA(_vpn) ((_vpn) * PAGE_SIZE) - -/* - * The following convenience macro can be used in a following situation - * - * send(..., &foo, sizeof(foo)) --> send(..., PTR_N_SIZE(foo)) - */ - -#define PTR_N_SIZE(_var) &(_var), sizeof(_var) - - -/* - * - * BIT-PULLING macros - * - */ -#define MVP_BIT(val,n) ( ((val)>>(n))&1) -#define MVP_BITS(val,m,n) (((val)<<(31-(n))) >> ((31-(n))+(m)) ) -#define MVP_EXTRACT_FIELD(w, m, n) MVP_BITS((w), (m), ((m) + (n) - 1)) -#define MVP_MASK(m, n) (MVP_EXTRACT_FIELD(~(uint32)0U, (m), (n)) << (m)) -#define MVP_UPDATE_FIELD(old_val, field_val, m, n) \ - (((old_val) & ~MVP_MASK((m), (n))) | (MVP_EXTRACT_FIELD((field_val), 0, (n)) << (m))) - -/* - * - * 64BIT-PULLING macros - * - */ -#define MVP_BITS64(val,m,n) (((val)<<(63-(n))) >> ((63-(n))+(m)) ) -#define MVP_EXTRACT_FIELD64(w, m, n) MVP_BITS64((w), (m), ((m) + (n) - 1)) -#define MVP_MASK64(m, n) (MVP_EXTRACT_FIELD64(~(uint64)0ULL, (m), (n)) << (m)) -#define MVP_UPDATE_FIELD64(old_val, field_val, m, n) \ - (((old_val) & ~MVP_MASK64((m), (n))) | (MVP_EXTRACT_FIELD64(((uint64)(field_val)), 0ULL, (n)) << (m))) - -/* - * - * BIT-CHANGING macros - * - */ -#define MVP_SETBIT(val,n) ((val)|=(1<<(n))) -#define MVP_CLRBIT(val,n) ((val)&=(~(1<<(n)))) - -/* - * Fixed bit-width sign extension. - */ -#define MVP_SIGN_EXTEND(val,width) \ - (((val) ^ (1 << ((width) - 1))) - (1 << ((width) - 1))) - - -/* - * Assembler helpers. - */ -#define _MVP_HASH # -#define MVP_HASH() _MVP_HASH - -#define _MVP_STRINGIFY(...) #__VA_ARGS__ -#define MVP_STRINGIFY(...) _MVP_STRINGIFY(__VA_ARGS__) - -#ifndef __ASSEMBLER__ - -#include -#include - -/* - * Constant equivalents of build-flags. - * - * Test these when possible instead of using #ifdef so that your code - * gets parsed. - */ -#ifdef MVP_DEBUG -static const _Bool mvpDebug = true; -#else -static const _Bool mvpDebug = false; -#endif - -#ifdef MVP_STATS -static const _Bool mvpStats = true; -#else -static const _Bool mvpStats = false; -#endif - -#ifdef MVP_DEVEL -static const _Bool mvpDevel = true; -#else -static const _Bool mvpDevel = false; -#endif - -#endif - -#endif diff --git a/arch/arm/mvp/commkm/vmid.h b/arch/arm/mvp/commkm/vmid.h deleted file mode 100644 index f24a650..0000000 --- a/arch/arm/mvp/commkm/vmid.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Guest Communications - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#ifndef _VMID_H -#define _VMID_H - -/** - * @file - * - * @brief The vmid definition - */ - - - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define VMID_UNDEF (uint16)0xffff -typedef uint16 VmId; - -#endif diff --git a/arch/arm/mvp/mvpkm/COPYING b/arch/arm/mvp/mvpkm/COPYING deleted file mode 100644 index 10828e0..0000000 --- a/arch/arm/mvp/mvpkm/COPYING +++ /dev/null @@ -1,341 +0,0 @@ - - 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/arch/arm/mvp/mvpkm/Kbuild b/arch/arm/mvp/mvpkm/Kbuild deleted file mode 100644 index 7b2dc10..0000000 --- a/arch/arm/mvp/mvpkm/Kbuild +++ /dev/null @@ -1,32 +0,0 @@ -# Warning: autogenerated -obj-m := mvpkm.o -mvpkm-objs := check_kconfig.o cpufreq_kernel.o mksck_kernel.o montimer_kernel.o mutex_kernel.o mvpkm_comm_ev.o mvpkm_main.o qp_host_kernel.o qp_common.o mksck_shared.o vfp_switch.o - -ccflags-y += -fno-pic -fno-dwarf2-cfi-asm -march=armv7-a -D__linux__ -ccflags-y += -mfpu=neon -DLIB_ARM_VERSION=7 -DIN_MODULE -DGPLED_CODE -ccflags-y += --std=gnu89 -O2 -g2 -ggdb -mapcs -fno-optimize-sibling-calls -mno-sched-prolog -ccflags-$(CONFIG_VMWARE_MVP_DEBUG) += -DMVP_DEBUG - -asflags-y += -mfpu=neon -DLIB_ARM_VERSION=7 -DIN_MODULE -DGPLED_CODE -asflags-y += -mfloat-abi=softfp - -# Detect MD5SUM executable -HOST_OS := $(shell uname -s) -ifeq ($(HOST_OS),Darwin) - MD5SUM_EXEC := md5 -else - MD5SUM_EXEC := md5sum -endif - -LOWMEMKILLER_PATH := $(srctree)/drivers/staging/android/lowmemorykiller.c -ifeq ($(wildcard $(LOWMEMKILLER_PATH)),) -$(error "Unable to find lowmemorykiller.c at $(LOWMEMKILLER_PATH)") -endif -LOWMEMKILLER_MD5 := $(shell $(MD5SUM_EXEC) $(LOWMEMKILLER_PATH) | cut -f1 -d\ ) -LOWMEMKILLER_SUPPORT := $(srctree)/arch/arm/mvp/mvpkm/lowmemkiller_variant.sh -LOWMEMKILLER_SHRINK_MD5 := $(shell $(SHELL) $(LOWMEMKILLER_SUPPORT) $(LOWMEMKILLER_PATH) | cut -f1 -d\ ) -LOWMEMKILLER_VARIANT := $(shell $(SHELL) $(LOWMEMKILLER_SUPPORT) $(LOWMEMKILLER_PATH) | cut -f2 -d\ ) -ccflags-y += \ - -DLOWMEMKILLER_VARIANT=$(LOWMEMKILLER_VARIANT) \ - -DLOWMEMKILLER_SHRINK_MD5=$(LOWMEMKILLER_SHRINK_MD5) \ - -DLOWMEMKILLER_MD5=$(LOWMEMKILLER_MD5) diff --git a/arch/arm/mvp/mvpkm/Makefile b/arch/arm/mvp/mvpkm/Makefile deleted file mode 100644 index 16eb389..0000000 --- a/arch/arm/mvp/mvpkm/Makefile +++ /dev/null @@ -1 +0,0 @@ -# Warning: autogenerated diff --git a/arch/arm/mvp/mvpkm/actions.h b/arch/arm/mvp/mvpkm/actions.h deleted file mode 100644 index 0e89892..0000000 --- a/arch/arm/mvp/mvpkm/actions.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Bit definitions for instrBActions. - */ - -#ifndef _ACTIONS_H -#define _ACTIONS_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define L2_ACTION_GDB 0 ///< drop into guest debugger GDB -#define L2_ACTION_MKSCK 1 ///< scan the mksck pipes for incoming messages -#define L2_ACTION_ABORT 2 ///< abort the monitor cleanly -#define L2_ACTION_HALT 3 ///< halt the monitor -#define L2_ACTION_FIQ 6 ///< the VCPU's FIQ pin is active -#define L2_ACTION_IRQ 7 ///< the VCPU's IRQ pin is active -#define L2_ACTION_CKPT 8 ///< do a checkpoint -#define L2_ACTION_WFI 9 ///< wait for interrupt -#define L2_ACTION_TIMER 10 ///< timer event -#define L2_ACTION_BALLOON 11 ///< balloon trigger - -#define ACTION_GDB (1 << L2_ACTION_GDB) -#define ACTION_MKSCK (1 << L2_ACTION_MKSCK) -#define ACTION_ABORT (1 << L2_ACTION_ABORT) -#define ACTION_HALT (1 << L2_ACTION_HALT) -#define ACTION_IRQ (1 << L2_ACTION_IRQ) -#define ACTION_FIQ (1 << L2_ACTION_FIQ) -#define ACTION_CKPT (1 << L2_ACTION_CKPT) -#define ACTION_WFI (1 << L2_ACTION_WFI) -#define ACTION_TIMER (1 << L2_ACTION_TIMER) -#define ACTION_BALLOON (1 << L2_ACTION_BALLOON) - -#endif diff --git a/arch/arm/mvp/mvpkm/arm_as_macros.h b/arch/arm/mvp/mvpkm/arm_as_macros.h deleted file mode 100644 index 5a0b7fc..0000000 --- a/arch/arm/mvp/mvpkm/arm_as_macros.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Macro definitions meta-ops to be used in assembler files - * - * This header contains asm macro definitions to be used in asm - * files only. This is intended to be the equivalent of arm_gcc_inline.h - */ - -#ifndef _ARM_AS_MACROS_H_ -#define _ARM_AS_MACROS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "coproc_defs.h" - -/** - * @name The following macros re-arrange the order of the mcr/mrc operands - * making it suitable to be used with the macros defined in coproc_defs.h - * - * @par For example - * mcr_p15 DOMAIN_CONTROL, r3 - * @par replaces - * mcr p15, 0, r3, c3, c0, 0 - * @{ - */ -.macro mcr_p15 op1, op2, op3, op4, reg, cond=al - mcr\cond p15, \op1, \reg, \op2, \op3, \op4 -.endm - -.macro mrc_p15 op1, op2, op3, op4, reg, cond=al - mrc\cond p15, \op1, \reg, \op2, \op3, \op4 -.endm - -.macro mcrr_p15 op1, op2, reg1, reg2 - mcrr p15, \op1, \reg1, \reg2, \op2 -.endm - -.macro mrrc_p15 op1, op2, reg1, reg2 - mrrc p15, \op1, \reg1, \reg2, \op2 -.endm -/*@}*/ - -/** - * @name Our toolchain does not include support for the VE instructions yet. - * @{ - */ -.macro hvc imm16 - .word ARM_INSTR_HVC_A1_ENC(\imm16) -.endm - -.macro eret - .word ARM_INSTR_ERET_A1_ENC(ARM_INSTR_COND_AL) -.endm - -.macro msr_ext rm, rn - .word ARM_INSTR_MSR_EXT_A1_ENC(ARM_INSTR_COND_AL, \rm, \rn) -.endm - -.macro mrs_ext rd, rm - .word ARM_INSTR_MRS_EXT_A1_ENC(ARM_INSTR_COND_AL, \rd, \rm) -.endm -/*@}*/ - -#endif /// ifndef _ARM_AS_MACROS_H_ diff --git a/arch/arm/mvp/mvpkm/arm_defs.h b/arch/arm/mvp/mvpkm/arm_defs.h deleted file mode 100644 index 2c39f6a..0000000 --- a/arch/arm/mvp/mvpkm/arm_defs.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Umbrella header file for all ARM-related definitions. By - * including this you gain access to all such definitions in - * lib/arm and are guaranteed a stable include. - */ - -#ifndef _ARM_DEFS_H_ -#define _ARM_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define ARM_V4 4 -#define ARM_V5 5 -#define ARM_V6 6 -#define ARM_V7 7 -#define ARM_V8 8 - -#include "coproc_defs.h" -#include "exc_defs.h" -#include "instr_defs.h" -#include "mmu_defs.h" -#include "lpae_defs.h" -#include "ve_defs.h" -#include "psr_defs.h" - -#endif /// _ARM_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/arm_gcc_inline.h b/arch/arm/mvp/mvpkm/arm_gcc_inline.h deleted file mode 100644 index 33ffe69..0000000 --- a/arch/arm/mvp/mvpkm/arm_gcc_inline.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief GCC inline stubs for ARM assembler instructions. - */ - -#ifndef _ARM_GCC_INLINE_H_ -#define _ARM_GCC_INLINE_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "coproc_defs.h" - -/* - * Macros for accessing CP10. - */ -#define _ARM_CP10_MRCMCR_STR(_op1,_cr1,_cr2,_op2,_var) \ - " p10, " #_op1 ","#_var"," #_cr1 "," #_cr2 "," #_op2 "\n\t" - -#define _ARM_MRC_CP10(_op1,_cr1,_cr2,_op2,_var) \ - asm volatile ("mrc" _ARM_CP10_MRCMCR_STR(_op1,_cr1,_cr2,_op2,%0) \ - : "=r" (_var) ) - -#define ARM_MRC_CP10(_cp_reg,_var) _ARM_MRC_CP10(_cp_reg,_var) - -#define _ARM_MCR_CP10(_op1,_cr1,_cr2,_op2,_val) \ - asm volatile ("mcr" _ARM_CP10_MRCMCR_STR(_op1,_cr1,_cr2,_op2,%0) \ - : \ - : "r" (_val) ) - -#define ARM_MCR_CP10(_cp_reg,_val) _ARM_MCR_CP10(_cp_reg,_val) - - -/* - * Macros for accessing CP15. - */ -#define _ARM_CP15_MRCMCR_STR(_op1,_cr1,_cr2,_op2,_var) \ - " p15, " #_op1 ","#_var"," #_cr1 "," #_cr2 "," #_op2 "\n\t" - -#define ARM_CP15_MRCMCR_STR(_cp_reg,_var) _ARM_CP15_MRCMCR_STR(_cp_reg,_var) - -#ifdef __COVERITY__ -static uint32 __cp15; -#define _ARM_MRC_CP15(_op1,_cr1,_cr2,_op2,_var) \ - (_var) = (uint32)__cp15 -#else -#define _ARM_MRC_CP15(_op1,_cr1,_cr2,_op2,_var) \ - asm volatile ("mrc" _ARM_CP15_MRCMCR_STR(_op1,_cr1,_cr2,_op2,%0) \ - : "=r" (_var) \ - : \ - : "memory") -#endif - -#define ARM_MRC_CP15(_cp_reg,_var) _ARM_MRC_CP15(_cp_reg,_var) - - -#ifdef __COVERITY__ -#define _ARM_MCR_CP15(_op1,_cr1,_cr2,_op2,_val) \ - __cp15 = (_val) -#else -#define _ARM_MCR_CP15(_op1,_cr1,_cr2,_op2,_val) \ - asm volatile ("mcr" _ARM_CP15_MRCMCR_STR(_op1,_cr1,_cr2,_op2,%0) \ - : \ - : "r" (_val)\ - : "memory") -#endif - -#define ARM_MCR_CP15(_cp_reg,_val) _ARM_MCR_CP15(_cp_reg,_val) - -#define _ARM_MRRC_CP15(_op,_cr,_val1,_val2) \ - asm volatile ("mrrc p15, " #_op ",%0,%1," #_cr "\n\t" \ - : "=r" (_val1), "=r" (_val2) \ - : \ - : "memory") - -#define ARM_MRRC_CP15(_cp_reg,_val1,_val2) _ARM_MRRC_CP15(_cp_reg,_val1,_val2) - -#define ARM_MRRC64_CP15(_cp_reg,_val) \ - _ARM_MRRC_CP15(_cp_reg,_val,*((uint8 *)&(_val) + 4)) - -#define _ARM_MCRR_CP15(_op,_cr,_val1,_val2) \ - asm volatile ("mcrr p15, " #_op ",%0,%1," #_cr "\n\t" \ - : \ - : "r" (_val1), "r" (_val2) \ - : "memory") - -#define ARM_MCRR_CP15(_cp_reg,_val1,_val2) _ARM_MCRR_CP15(_cp_reg,_val1,_val2) - -#define ARM_MCRR64_CP15(_cp_reg,_val) \ - _ARM_MCRR_CP15(_cp_reg,_val,*((uint8 *)&(_val) + 4)) - -#define DMB() asm volatile ("dmb" : : : "memory") -#define DSB() asm volatile ("dsb" : : : "memory") -#define ISB() asm volatile ("isb" : : : "memory") - -/** - * @name 64-bit multiplies - * @{ - */ - -// rdhi:rdlo = rm * rs + rdhi + rdlo -#define ARM_UMAAL(rdlo,rdhi,rm,rs) asm ("umaal %0,%1,%2,%3" \ - : "+r" (rdlo), "+r" (rdhi) \ - : "r" (rm), "r" (rs)) - -// rdhi:rdlo += rm * rs -#define ARM_UMLAL(rdlo,rdhi,rm,rs) asm ("umlal %0,%1,%2,%3" \ - : "+r" (rdlo), "+r" (rdhi) \ - : "r" (rm), "r" (rs)) - -// rdhi:rdlo = rm * rs -#define ARM_UMULL(rdlo,rdhi,rm,rs) asm ("umull %0,%1,%2,%3" \ - : "=r" (rdlo), "=r" (rdhi) \ - : "r" (rm), "r" (rs)) -/*@}*/ - -/** - * @brief Disable interrupts (IRQ + FIQ) - * - * @return CPSR status prior to disabling - suitable for passing to - * ARM_RestoreInterrupts() to restore IRQ/FIQ levels to - * pre-call values - */ -static inline uint32 -ARM_DisableInterrupts(void) -{ - register uint32 status; - - asm volatile ("mrs %0, cpsr \n\t" - "orr r1, %0, %1 \n\t" - "msr cpsr_c, r1 \n\t" - : "=&r" (status) - : "i" (ARM_PSR_I | ARM_PSR_F) - : "r1", "memory"); - - return status; -} - -/** - * @brief Restore interrupts - * - * @param status return value from a previous call to ARM_DisableInterrupts() - */ -static inline void -ARM_RestoreInterrupts(uint32 status) -{ - asm volatile ("msr cpsr_c, %0 \n\t" : : "r" (status) : "memory"); -} - -/** - * @brief Read current CPSR value - * - * @return current CPSR value - */ -static inline uint32 -ARM_ReadCPSR(void) -{ - uint32 status; - - asm volatile ("mrs %0, cpsr \n\t" : "=r" (status)); - - return status; -} - -/** - * @brief Read current stack pointer - * - * @return stack pointer value - */ -static inline uint32 -ARM_ReadSP(void) -{ - uint32 sp; - - asm volatile ("mov %0, sp \n\t" : "=r" (sp)); - - return sp; -} - -#endif /// ifndef _ARM_GCC_INLINE_H_ diff --git a/arch/arm/mvp/mvpkm/arm_inline.h b/arch/arm/mvp/mvpkm/arm_inline.h deleted file mode 100644 index 3689a7f..0000000 --- a/arch/arm/mvp/mvpkm/arm_inline.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Inline stubs for ARM assembler instructions. - */ - -#ifndef _ARM_INLINE_H_ -#define _ARM_INLINE_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "arm_types.h" -#include "arm_defs.h" - -/* - * Compiler specific include - we get the actual inline assembler macros here. - */ -#include "arm_gcc_inline.h" - -/* - * Some non-compiler specific helper functions for inline assembler macros - * included above. - */ - -/** - * @brief Predicate giving whether interrupts are currently enabled - * - * @return TRUE if enabled, FALSE otherwise - */ -static inline _Bool -ARM_InterruptsEnabled(void) -{ - return !(ARM_ReadCPSR() & ARM_PSR_I); -} - -/** - * @brief Read current TTBR0 base machine address - * - * @return machine address given by translation table base register 0 - */ -static inline MA -ARM_ReadTTBase0(void) -{ - MA ttbase; - - ARM_MRC_CP15(TTBASE0_POINTER, ttbase); - - return ttbase & ARM_CP15_TTBASE_MASK; -} - -/** - * @brief Read VFP/Adv.SIMD Extension System Register - * - * @param specReg which VFP/Adv. SIMD Extension System Register - * - * @return Read value - */ -static inline uint32 -ARM_ReadVFPSystemRegister(uint8 specReg) -{ - uint32 value = 0; - - /* - * VMRS is the instruction used to read VFP System Registers. - * VMRS is the new UAL-syntax equivalent for the FMRX instruction. - * At the end of the day, all these are just synonyms for MRC - * instructions on CP10, as the VFP system registers sit in CP10 - * and MRC is the Co-processor register read instruction. - * We use the primitive MRC synonym for VMRS here as VMRS/FMRX - * don't seem to be working when used inside asm volatile blocks, - * as, for some reason, the inline assembler seems to be setting - * the VFP mode to soft-float. Moreover, we WANT the monitor code - * to be compiled with soft-float so that the compiler doesn't use - * VFP instructions for the monitor's own use, such as for 64-bit - * integer operations, etc., since we pass-through the use of the - * underlying hardware's VFP/SIMD state to the guest. - */ - - switch (specReg) { - case ARM_VFP_SYSTEM_REG_FPSID: - ARM_MRC_CP10(VFP_FPSID, value); - break; - case ARM_VFP_SYSTEM_REG_MVFR0: - ARM_MRC_CP10(VFP_MVFR0, value); - break; - case ARM_VFP_SYSTEM_REG_MVFR1: - ARM_MRC_CP10(VFP_MVFR1, value); - break; - case ARM_VFP_SYSTEM_REG_FPEXC: - ARM_MRC_CP10(VFP_FPEXC, value); - break; - case ARM_VFP_SYSTEM_REG_FPSCR: - ARM_MRC_CP10(VFP_FPSCR, value); - break; - case ARM_VFP_SYSTEM_REG_FPINST: - ARM_MRC_CP10(VFP_FPINST, value); - break; - case ARM_VFP_SYSTEM_REG_FPINST2: - ARM_MRC_CP10(VFP_FPINST2, value); - break; - default: - NOT_IMPLEMENTED_JIRA(1849); - break; - } - - return value; -} - -/** - * @brief Write to VFP/Adv.SIMD Extension System Register - * - * @param specReg which VFP/Adv. SIMD Extension System Register - * @param value desired value to be written to the System Register - */ -static inline void -ARM_WriteVFPSystemRegister(uint8 specReg, uint32 value) -{ - /* - * VMSR is the instruction used to write to VFP System Registers. - * VMSR is the new UAL-syntax equivalent for the FMXR instruction. - * At the end of the day, all these are just synonyms for MCR - * instructions on CP10, as the VFP system registers sit in CP10 - * and MCR is the Co-processor register write instruction. - * We use the primitive MCR synonym for VMSR here as VMSR/FMXR - * don't seem to be working when used inside asm volatile blocks, - * as, for some reason, the inline assembler seems to be setting - * the VFP mode to soft-float. Moreover, we WANT the monitor code - * to be compiled with soft-float so that the compiler doesn't use - * VFP instructions for the monitor's own use, such as for 64-bit - * integer operations, etc., since we pass-through the use of the - * underlying hardware's VFP/SIMD state to the guest. - */ - - switch (specReg) { - case ARM_VFP_SYSTEM_REG_FPEXC: - ARM_MCR_CP10(VFP_FPEXC, value); - break; - case ARM_VFP_SYSTEM_REG_FPSCR: - ARM_MCR_CP10(VFP_FPSCR, value); - break; - case ARM_VFP_SYSTEM_REG_FPINST: - ARM_MCR_CP10(VFP_FPINST, value); - break; - case ARM_VFP_SYSTEM_REG_FPINST2: - ARM_MCR_CP10(VFP_FPINST2, value); - break; - default: - NOT_IMPLEMENTED_JIRA(1849); - break; - } -} - -#endif /// ifndef _ARM_INLINE_H_ diff --git a/arch/arm/mvp/mvpkm/arm_types.h b/arch/arm/mvp/mvpkm/arm_types.h deleted file mode 100644 index 2075860..0000000 --- a/arch/arm/mvp/mvpkm/arm_types.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Umbrella header file for all ARM-related types. - */ - -#ifndef _ARM_TYPES_H_ -#define _ARM_TYPES_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "exc_types.h" -#include "mmu_types.h" -#include "lpae_types.h" - -#endif /// _ARM_TYPES_H_ diff --git a/arch/arm/mvp/mvpkm/atomic.h b/arch/arm/mvp/mvpkm/atomic.h deleted file mode 100644 index 987860f..0000000 --- a/arch/arm/mvp/mvpkm/atomic.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief bus-atomic operators. - * - * The 'atm' argument is the atomic memory cell being operated on and the - * remainder of the arguments are the values being applied to the atomic cell - * which is assumed to be located in shared normal memory. The operation is - * both atomic and visible to the default share-ability domain upon completion. - * - * The design of each macro is such that the compiler should check types - * correctly. For those macros that return a value, the return type should be - * the same as the 'atm' argument (with the exception of ATOMIC_SETIF which - * returns an int value of 0 or 1). - * - * Those names ending in 'M' return the modified value of 'atm'. - * Those names ending in 'O' return the original value of 'atm'. - * Those names ending in 'V' return void (ie, nothing). - */ - -#ifndef _ATOMIC_H -#define _ATOMIC_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#include "include_check.h" - -/* - * Wrap type 't' in an atomic struct. - * Eg, 'static ATOMIC(uint8) counter;'. - * - * The function macros use the atm_Normal member to clone the atom's type - * when the volatile semantic is not required. They use the atm_Volatl member - * when the volatile semantic is required. - */ -#define ATOMIC(t) union { t atm_Normal; t volatile atm_Volatl; } - -/* - * Static atomic variable initialization. - * Eg, 'static ATOMIC(uint8) counter = ATOMIC_INI(35);'. - */ -#define ATOMIC_INI(v) { .atm_Normal = v } - -/* - * Some commonly used atomic types. - */ -typedef ATOMIC(int32) AtmSInt32 __attribute__ ((aligned (4))); -typedef ATOMIC(uint32) AtmUInt32 __attribute__ ((aligned (4))); -typedef ATOMIC(uint64) AtmUInt64 __attribute__ ((aligned (8))); - -/* - * Architecture-dependent implementations. - */ -#if defined(__COVERITY__) -#include "atomic_coverity.h" -#elif defined(__arm__) -#include "atomic_arm.h" -#elif defined(__i386) || defined(__x86_64) -#include "atomic_x86.h" -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/atomic_arm.h b/arch/arm/mvp/mvpkm/atomic_arm.h deleted file mode 100644 index 447aa55..0000000 --- a/arch/arm/mvp/mvpkm/atomic_arm.h +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief bus-atomic operators, ARM implementation. - * Do not include directly, include 'atomic.h' instead. - * Memory where the atomic reside must be shared. - * - * These operations assume that the exclusive access monitor is cleared during - * abort entry but they do not assume that cooperative scheduling (e.g. Linux - * schedule()) clears the monitor and hence the use of "clrex" when required. - */ - -#ifndef _ATOMIC_ARM_H -#define _ATOMIC_ARM_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#include "include_check.h" - -#include "mvp_assert.h" - -/** - * @brief Atomic Add - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return the original value of 'atm' - */ -#define ATOMIC_ADDO(atm,modval) ATOMIC_OPO_PRIVATE(atm,modval,add) - -/** - * @brief Atomic Add - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return nothing - */ -#define ATOMIC_ADDV(atm,modval) ATOMIC_OPV_PRIVATE(atm,modval,add) - -/** - * @brief Atomic And - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return the original value of 'atm' - */ -#define ATOMIC_ANDO(atm,modval) ATOMIC_OPO_PRIVATE(atm,modval,and) - -/** - * @brief Atomic And - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return nothing - */ -#define ATOMIC_ANDV(atm,modval) ATOMIC_OPV_PRIVATE(atm,modval,and) - -/** - * @brief Retrieve an atomic value - * @param atm atomic cell to operate on - * @return the value of 'atm' - */ -#define ATOMIC_GETO(atm) ({ \ - typeof((atm).atm_Normal) _oldval; \ - switch (sizeof _oldval) { \ - case 4: \ - asm volatile ("ldrex %0, [%1]\n" \ - "clrex" \ - : "=&r" (_oldval) \ - : "r" (&((atm).atm_Volatl))); \ - break; \ - case 8: \ - asm volatile ("ldrexd %0, %H0, [%1]\n" \ - "clrex" \ - : "=&r" (_oldval) \ - : "r" (&((atm).atm_Volatl))); \ - break; \ - default: \ - FATAL(); \ - } \ - _oldval; \ -}) - -/** - * @brief Atomic Or - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return the original value of 'atm' - */ -#define ATOMIC_ORO(atm,modval) ATOMIC_OPO_PRIVATE(atm,modval,orr) - -/** - * @brief Atomic Or - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return nothing - */ -#define ATOMIC_ORV(atm,modval) ATOMIC_OPV_PRIVATE(atm,modval,orr) - -/** - * @brief Atomic Conditional Write, ie, - * set 'atm' to 'newval' iff it was 'oldval'. - * @param atm atomic cell to operate on - * @param newval value to possibly write to atomic cell - * @param oldval value that atomic cell must equal - * @return 0 if failed; 1 if successful - */ -#define ATOMIC_SETIF(atm,newval,oldval) ({ \ - int _failed; \ - typeof((atm).atm_Normal) _newval = newval; \ - typeof((atm).atm_Normal) _oldval = oldval; \ - ASSERT_ON_COMPILE(sizeof _newval == 4); \ - asm volatile ("1: ldrex %0, [%1] \n" \ - " cmp %0, %2 \n" \ - " mov %0, #2 \n" \ - " IT eq \n" \ - " strexeq %0, %3, [%1] \n" \ - " cmp %0, #1 \n" \ - " beq 1b \n" \ - " clrex" \ - : "=&r" (_failed) \ - : "r" (&((atm).atm_Volatl)), \ - "r" (_oldval), \ - "r" (_newval) \ - : "cc", "memory"); \ - !_failed; \ -}) - - -/** - * @brief Atomic Write (unconditional) - * @param atm atomic cell to operate on - * @param newval value to write to atomic cell - * @return the original value of 'atm' - */ -#define ATOMIC_SETO(atm,newval) ({ \ - int _failed; \ - typeof((atm).atm_Normal) _newval = newval; \ - typeof((atm).atm_Normal) _oldval; \ - switch (sizeof _newval) { \ - case 4: \ - asm volatile ("1: ldrex %0, [%2]\n" \ - " strex %1, %3, [%2]\n" \ - " teq %1, #0\n" \ - " bne 1b" \ - : "=&r" (_oldval), \ - "=&r" (_failed) \ - : "r" (&((atm).atm_Volatl)), \ - "r" (_newval) \ - : "cc", "memory"); \ - break; \ - case 8: \ - asm volatile ("1: ldrexd %0, %H0, [%2]\n" \ - " strexd %1, %3, %H3, [%2]\n"\ - " teq %1, #0\n" \ - " bne 1b" \ - : "=&r" (_oldval), \ - "=&r" (_failed) \ - : "r" (&((atm).atm_Volatl)), \ - "r" (_newval) \ - : "cc", "memory"); \ - break; \ - default: \ - FATAL(); \ - } \ - _oldval; \ -}) - -/** - * @brief Atomic Write (unconditional) - * @param atm atomic cell to operate on - * @param newval value to write to atomic cell - * @return nothing - */ -#define ATOMIC_SETV(atm,newval) do { ATOMIC_SETO((atm),(newval)); } while (0) - -/** - * @brief Atomic Subtract - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return the original value of 'atm' - */ -#define ATOMIC_SUBO(atm,modval) ATOMIC_OPO_PRIVATE(atm,modval,sub) - -/** - * @brief Atomic Subtract - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @return nothing - */ -#define ATOMIC_SUBV(atm,modval) ATOMIC_OPV_PRIVATE(atm,modval,sub) - -/** - * @brief Atomic Generic Binary Operation - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @param op ARM instruction (add, and, orr, etc) - * @return the original value of 'atm' - */ -#define ATOMIC_OPO_PRIVATE(atm,modval,op) ({ \ - int _failed; \ - typeof((atm).atm_Normal) _modval = modval; \ - typeof((atm).atm_Normal) _oldval; \ - typeof((atm).atm_Normal) _newval; \ - ASSERT_ON_COMPILE(sizeof _modval == 4); \ - asm volatile ("1: ldrex %0, [%3]\n" \ - #op " %1, %0, %4\n" \ - " strex %2, %1, [%3]\n" \ - " teq %2, #0\n" \ - " bne 1b" \ - : "=&r" (_oldval), \ - "=&r" (_newval), \ - "=&r" (_failed) \ - : "r" (&((atm).atm_Volatl)), \ - "r" (_modval) \ - : "memory"); \ - _oldval; \ -}) - -/** - * @brief Atomic Generic Binary Operation - * @param atm atomic cell to operate on - * @param modval value to apply to atomic cell - * @param op ARM instruction (add, and, orr, etc) - * @return nothing - */ -#define ATOMIC_OPV_PRIVATE(atm,modval,op) do { \ - int _failed; \ - typeof((atm).atm_Normal) _modval = modval; \ - typeof((atm).atm_Normal) _sample; \ - ASSERT_ON_COMPILE(sizeof _modval == 4); \ - asm volatile ("1: ldrex %0, [%2]\n" \ - #op " %0, %3\n" \ - " strex %1, %0, [%2]\n" \ - " teq %1, #0\n" \ - " bne 1b" \ - : "=&r" (_sample), \ - "=&r" (_failed) \ - : "r" (&((atm).atm_Volatl)), \ - "r" (_modval) \ - : "memory"); \ -} while (0) - -/** - * @brief Single-copy atomic word write. - * - * ARMv7 defines world-aligned word writes to be single-copy atomic. See - * A3-26 ARM DDI 0406A. - * - * @param p word aligned location to write to - * @param val word-sized value to write to p - */ -#define ATOMIC_SINGLE_COPY_WRITE32(p,val) \ - do { \ - ASSERT(sizeof(val) == 4); \ - ASSERT((MVA)(p) % sizeof(val) == 0); \ - asm volatile("str %0, [%1]" \ - : \ - : "r" (val), "r" (p) \ - : "memory"); \ - } while (0); - - -/** - * @brief Single-copy atomic word read. - * - * ARMv7 defines world-aligned word reads to be single-copy atomic. See - * A3-26 ARM DDI 0406A. - * - * @param p word aligned location to read from - * - * @return word-sized value from p - */ -#define ATOMIC_SINGLE_COPY_READ32(p) ({ \ - ASSERT((MVA)(p) % sizeof(uint32) == 0); \ - uint32 _val; \ - asm volatile("ldr %0, [%1]" \ - : "=r" (_val) \ - : "r" (p) \ - ); \ - _val; \ -}) - -/** - * @brief Single-copy atomic double word write. - * - * LPAE defines double world-aligned double word writes to be single-copy - * atomic. See 6.7 ARM PRD03-GENC-008469 13.0. - * - * @param p double word aligned location to write to - * @param val double word-sized value to write to p - */ -#define ATOMIC_SINGLE_COPY_WRITE64(p,val) \ - do { \ - ASSERT(sizeof(val) == 8); \ - ASSERT((MVA)(p) % sizeof(val) == 0); \ - asm volatile("mov r0, %0 \n" \ - "mov r1, %1 \n" \ - "strd r0, r1, [%2]" \ - : \ - : "r" ((uint32)(val)), \ - "r" (((uint64)(val)) >> 32),\ - "r" (p) \ - : "r0", "r1", "memory"); \ - } while (0); - -#endif diff --git a/arch/arm/mvp/mvpkm/check_kconfig.c b/arch/arm/mvp/mvpkm/check_kconfig.c deleted file mode 100644 index bab1b6e..0000000 --- a/arch/arm/mvp/mvpkm/check_kconfig.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * @brief Check for required kernel configuration - * - * Check to make sure that the kernel options that the MVP hypervisor requires - * have been enabled in the kernel that this kernel module is being built - * against. - */ -#include - -/* - * Minimum kernel version - * - network namespace support is only really functional starting in 2.6.29 - * - Android Gingerbread requires 2.6.35 - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -#error "MVP requires a host kernel newer than 2.6.35" -#endif - -/* module loading ability */ -#ifndef CONFIG_MODULES -#error "MVP requires kernel loadable module support be enabled (CONFIG_MODULES)" -#endif -#ifndef CONFIG_MODULE_UNLOAD -#error "MVP requires kernel module unload support be enabled (CONFIG_MODULE_UNLOAD)" -#endif - -/* sysfs */ -#ifndef CONFIG_SYSFS -#error "MVP requires sysfs support (CONFIG_SYSFS)" -#endif - -/* network traffic isolation */ -#ifndef CONFIG_NAMESPACES -#error "MVP networking support requires namespace support (CONFIG_NAMESPACES)" -#endif -#ifndef CONFIG_NET_NS -#error "MVP networking support requires Network Namespace support to be enabled (CONFIG_NET_NS)" -#endif - -/* TCP/IP networking */ -#ifndef CONFIG_INET -#error "MVP networking requires IPv4 support (CONFIG_INET)" -#endif -#ifndef CONFIG_IPV6 -#error "MVP networking requires IPv6 support (CONFIG_IPV6)" -#endif - -/* VPN support */ -#if !defined(CONFIG_TUN) && !defined(CONFIG_TUN_MODULE) -#error "MVP VPN support requires TUN device support (CONFIG_TUN)" -#endif - -#if !defined(CONFIG_NETFILTER) && !defined(PVTCP_DISABLE_NETFILTER) -#error "MVP networking support requires netfilter support (CONFIG_NETFILTER)" -#endif - -/* Force /proc/config.gz support for eng/userdebug builds */ -#ifdef MVP_DEBUG -#if !defined(CONFIG_IKCONFIG) || !defined(CONFIG_IKCONFIG_PROC) -#error "MVP kernel /proc/config.gz support required for debuggability (CONFIG_IKCONFIG_PROC)" -#endif -#endif - -/* Sanity check we're only dealing with the memory hotplug + migrate and/or - * compaction combo */ -#ifdef CONFIG_MIGRATION -#if defined(CONFIG_NUMA) || defined(CONFIG_CPUSETS) || defined(CONFIG_MEMORY_FAILURE) -#error "MVP not tested with migration features other than CONFIG_MEMORY_HOTPLUG and CONFIG_COMPACTION" -#endif -#endif diff --git a/arch/arm/mvp/mvpkm/comm_os.h b/arch/arm/mvp/mvpkm/comm_os.h deleted file mode 100644 index cf858f6..0000000 --- a/arch/arm/mvp/mvpkm/comm_os.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Cross-platform base type definitions and function declarations. - * Includes OS-specific base type definitions and function declarations. - */ - -#ifndef _COMM_OS_H_ -#define _COMM_OS_H_ - -/* For-ever timeout constant (in milliseconds). */ -#define COMM_OS_4EVER_TO ((unsigned long long)(~0UL >> 1)) - -/* Condition function prototype. Returns 1: true, 0: false, < 0: error code. */ -typedef int (*CommOSWaitConditionFunc)(void *arg1, void *arg2); - -/* Dispatch function prototype. Called by input (dispatch) kernel threads. */ -typedef unsigned int (*CommOSDispatchFunc)(void); - -/* Module initialization and exit callback functions. */ -extern int (*commOSModInit)(void *args); -extern void (*commOSModExit)(void); - -/* Macro to assign Init and Exit callbacks. */ -#define COMM_OS_MOD_INIT(init, exit) \ - int (*commOSModInit)(void *args) = init; \ - void (*commOSModExit)(void) = exit - - -/* - * OS-specific implementations must provide the following: - * 1. Types: - * CommOSAtomic - * CommOSSpinlock - * CommOSMutex - * CommOSWaitQueue - * CommOSWork - * CommOSWorkFunc - * CommOSList - * CommOSModule - * struct kvec - * - * 2. Definition, initializers: - * CommOSSpinlock_Define() - * - * 3. Functions: - * void CommOS_Debug(const char *format, ...); - * void CommOS_Log(const char *format, ...); - * void CommOS_WriteAtomic(CommOSAtomic *atomic, int val); - * int CommOS_ReadAtomic(CommOSAtomic *atomic); - * int CommOS_AddReturnAtomic(CommOSAtomic *atomic, int val); - * int CommOS_SubReturnAtomic(CommOSAtomic *atomic, int val); - * void CommOS_SpinlockInit(CommOSSpinlock *lock); - * void CommOS_SpinLockBH(CommOSSpinlock *lock); - * int CommOS_SpinTrylockBH(CommOSSpinlock *lock); - * void CommOS_SpinUnlockBH(CommOSSpinlock *lock); - * void CommOS_SpinLock(CommOSSpinlock *lock); - * int CommOS_SpinTrylock(CommOSSpinlock *lock); - * void CommOS_SpinUnlock(CommOSSpinlock *lock); - * void CommOS_MutexInit(CommOSMutex *mutex); - * void CommOS_MutexLock(CommOSMutex *mutex); - * int CommOS_MutexLockUninterruptible(CommOSMutex *mutex); - * int CommOS_MutexTrylock(CommOSMutex *mutex); - * void CommOS_MutexUnlock(CommOSMutex *mutex); - * void CommOS_WaitQueueInit(CommOSWaitQueue *wq); - * CommOS_DoWait(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc cond, - * void *condArg1, - * void *condArg2, - * unsigned long long *timeoutMillis, - * int interruptible); - * int CommOS_Wait(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc func, - * void *funcArg1, - * void *funcArg2, - * unsigned long long *timeoutMillis); - * int CommOS_WaitUninterruptible(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc func, - * void *funcArg1, - * void *funcArg2, - * unsigned long long *timeoutMillis); - * void CommOS_WakeUp(CommOSWaitQueue *wq); - * void *CommOS_KmallocNoSleep(unsigned int size); - * void *CommOS_Kmalloc(unsigned int size); - * void CommOS_Kfree(void *arg); - * void CommOS_Yield(void); - * unsigned long long CommOS_GetCurrentMillis(void); - * void CommOS_ListInit(CommOSList *list); - * int CommOS_ListEmpty(CommOSList *list); - * void CommOS_ListAdd(CommOSList *list, CommOSList *listElem); - * void CommOS_ListAddTail(CommOSList *list, CommOSList *listElem); - * void int CommOS_ListDel(CommOSList *listElem); - * Macros: - * CommOS_ListForEach(*list, *item, itemListFieldName); - * CommOS_ListForEachSafe(*list, *item, *tmp, itemListFieldName); - * void CommOS_ListSplice(CommOSList *list, CommOSList *listToAdd); - * void CommOS_ListSpliceTail(CommOSList *list, CommOSList *listToAdd); - * CommOSModule CommOS_ModuleSelf(void); - * int CommOS_ModuleGet(CommOSModule module); - * void CommOS_ModulePut(CommOSModule module); - * void CommOS_MemBarrier(void); - * - * These cannot be defined here: a) non-pointer type definitions need size - * information, and b) functions may or may not be inlined, or macros may - * be used instead. - */ - - -#ifdef __linux__ -#include "comm_os_linux.h" -#else -#error "Unsupported OS" -#endif - -/* Functions to start and stop the dispatch and aio kernel threads. */ -void CommOS_StopIO(void); -void CommOS_ScheduleDisp(void); -void CommOS_InitWork(CommOSWork *work, CommOSWorkFunc func); -int CommOS_ScheduleAIOWork(CommOSWork *work); -void CommOS_FlushAIOWork(CommOSWork *work); - -int -CommOS_StartIO(const char *dispatchTaskName, - CommOSDispatchFunc dispatchHandler, - unsigned int interval, - unsigned int maxCycles, - const char *aioTaskName); - - -#endif /* _COMM_OS_H_ */ diff --git a/arch/arm/mvp/mvpkm/comm_os_linux.h b/arch/arm/mvp/mvpkm/comm_os_linux.h deleted file mode 100644 index c9d4f26..0000000 --- a/arch/arm/mvp/mvpkm/comm_os_linux.h +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Contains linux-specific type definitions and function declarations - */ - -#ifndef _COMM_OS_LINUX_H_ -#define _COMM_OS_LINUX_H_ - -#include -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -#error "Kernel versions lower than 2.6.20 are not supported" -#endif - -#include -#include -#include -#include -#include -#include - - -/* - * Type definitions. - */ - -typedef atomic_t CommOSAtomic; -typedef spinlock_t CommOSSpinlock; -typedef struct mutex CommOSMutex; -typedef wait_queue_head_t CommOSWaitQueue; -typedef struct delayed_work CommOSWork; -typedef void (*CommOSWorkFunc)(CommOSWork *work); -typedef struct list_head CommOSList; -typedef struct module *CommOSModule; - - -/* - * Initializers. - */ - -#define CommOSSpinlock_Define DEFINE_SPINLOCK - - -#define COMM_OS_DOLOG(...) printk(KERN_INFO __VA_ARGS__) - - -/** - * @brief Logs given arguments in debug builds. - */ - -#if defined(COMM_OS_DEBUG) - #define CommOS_Debug(args) COMM_OS_DOLOG args -#else - #define CommOS_Debug(args) -#endif - - -/** - * @brief Logs given arguments. - */ - -#define CommOS_Log(args) COMM_OS_DOLOG args - - -/** - * @brief Logs function name and location. - */ - -#if defined(COMM_OS_TRACE) -#define TRACE(ptr) \ - do { \ - CommOS_Debug(("%p:%s: at [%s:%d] with arg ptr [0x%p].\n", current, \ - __FUNCTION__, __FILE__, __LINE__, (ptr))); \ - } while (0) -#else -#define TRACE(ptr) -#endif - - -/** - * @brief Write atomic variable - * @param[in,out] atomic variable to write - * @param val new value - */ - -static inline void -CommOS_WriteAtomic(CommOSAtomic *atomic, - int val) -{ - atomic_set(atomic, val); -} - - -/** - * @brief Reads atomic variable - * @param atomic variable to read - * @return value - */ - -static inline int -CommOS_ReadAtomic(CommOSAtomic *atomic) -{ - return atomic_read(atomic); -} - - -/** - * @brief Atomically add value to atomic variable, return new value. - * @param[in,out] atomic variable - * @param val value to add - * @return new value - */ - -static inline int -CommOS_AddReturnAtomic(CommOSAtomic *atomic, - int val) -{ - return atomic_add_return(val, atomic); -} - - -/** - * @brief Atomically substract value from atomic variable, return new value. - * @param[in,out] atomic variable - * @param val value to substract - * @return new value - */ - -static inline int -CommOS_SubReturnAtomic(CommOSAtomic *atomic, - int val) -{ - return atomic_sub_return(val, atomic); -} - - -/** - * @brief Initializes a given lock. - * @param[in,out] lock lock to initialize - */ - -static inline void -CommOS_SpinlockInit(CommOSSpinlock *lock) -{ - spin_lock_init(lock); -} - - -/** - * @brief Locks given lock and disables bottom half processing. - * @param[in,out] lock lock to lock - */ - -static inline void -CommOS_SpinLockBH(CommOSSpinlock *lock) -{ - spin_lock_bh(lock); -} - - -/** - * @brief Attempts to lock the given lock and disable BH processing. - * @param[in,out] lock lock to lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_SpinTrylockBH(CommOSSpinlock *lock) -{ - return !spin_trylock_bh(lock); -} - - -/** - * @brief Unlocks given lock and re-enables BH processing. - * @param[in,out] lock lock to unlock - */ - -static inline void -CommOS_SpinUnlockBH(CommOSSpinlock *lock) -{ - spin_unlock_bh(lock); -} - - -/** - * @brief Locks the given lock. - * @param[in,out] lock lock to lock - */ - -static inline void -CommOS_SpinLock(CommOSSpinlock *lock) -{ - spin_lock(lock); -} - - -/** - * @brief Attempts to lock the given lock. - * @param[in,out] lock lock to try-lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_SpinTrylock(CommOSSpinlock *lock) -{ - return !spin_trylock(lock); -} - - -/** - * @brief Unlocks given lock. - * @param[in,out] lock lock to unlock - */ - -static inline void -CommOS_SpinUnlock(CommOSSpinlock *lock) -{ - spin_unlock(lock); -} - - -/** - * @brief Initializes given mutex. - * @param[in,out] mutex mutex to initialize - */ - -static inline void -CommOS_MutexInit(CommOSMutex *mutex) -{ - mutex_init(mutex); -} - - -/** - * @brief Acquires mutex. - * @param[in,out] mutex mutex to lock - * @return zero if successful, non-zero otherwise (interrupted) - */ - -static inline int -CommOS_MutexLock(CommOSMutex *mutex) -{ - return mutex_lock_interruptible(mutex); -} - - -/** - * @brief Acquires mutex in uninterruptible mode. - * @param[in,out] mutex mutex to lock - */ - -static inline void -CommOS_MutexLockUninterruptible(CommOSMutex *mutex) -{ - mutex_lock(mutex); -} - - -/** - * @brief Attempts to acquire given mutex. - * @param[in,out] mutex mutex to try-lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_MutexTrylock(CommOSMutex *mutex) -{ - return !mutex_trylock(mutex); -} - - -/** - * @brief Releases a given mutex. - * @param[in,out] mutex mutex to unlock - */ - -static inline void -CommOS_MutexUnlock(CommOSMutex *mutex) -{ - mutex_unlock(mutex); -} - - -/** - * @brief Initializes a wait queue. - * @param[in,out] wq workqueue to initialize - */ - -static inline void -CommOS_WaitQueueInit(CommOSWaitQueue *wq) -{ - init_waitqueue_head(wq); -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * - a signal is pending - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @param interruptible enable/disable signal pending check - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, if a signal is pending or other error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_DoWait(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis, - int interruptible) -{ - int rc; - DEFINE_WAIT(wait); - long timeout; -#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) - long tmpTimeout; - long retTimeout; - const unsigned int interval = 50; -#endif - - if (!timeoutMillis) { - return -1; - } - if ((rc = cond(condArg1, condArg2)) != 0) { - return rc; - } - -#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) - timeout = msecs_to_jiffies(interval < *timeoutMillis ? - interval : (unsigned int)*timeoutMillis); - retTimeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); - - for (; retTimeout >= 0; ) { - prepare_to_wait(wq, &wait, - (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); - if ((rc = cond(condArg1, condArg2))) { - break; - } - if (interruptible && signal_pending(current)) { - rc = -EINTR; - break; - } - if ((tmpTimeout = schedule_timeout(timeout))) { - retTimeout -= (timeout - tmpTimeout); - } else { - retTimeout -= timeout; - } - if (retTimeout < 0) { - retTimeout = 0; - } - } - finish_wait(wq, &wait); - if (rc == 0) { - rc = cond(condArg1, condArg2); - if (rc && (retTimeout == 0)) { - retTimeout = 1; - } - } - *timeoutMillis = (unsigned long long)jiffies_to_msecs(retTimeout); -#else // !defined(COMM_OS_LINUX_WAIT_WORKAROUND) - timeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); - - for (;;) { - prepare_to_wait(wq, &wait, - (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); - if ((rc = cond(condArg1, condArg2)) != 0) { - break; - } - if (interruptible && signal_pending(current)) { - rc = -EINTR; - break; - } - if ((timeout = schedule_timeout(timeout)) == 0) { - rc = 0; - break; - } - } - finish_wait(wq, &wait); - if (rc == 0) { - rc = cond(condArg1, condArg2); - if (rc && (timeout == 0)) { - timeout = 1; - } - } - *timeoutMillis = (unsigned long long)jiffies_to_msecs(timeout); -#endif - - return rc; -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * - a signal is pending - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, if a signal is pending or other error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_Wait(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis) -{ - return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 1); -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_WaitUninterruptible(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis) -{ - return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 0); -} - - -/** - * @brief Wakes up task(s) waiting on the given wait queue. - * @param[in,out] wq wait queue. - */ - -static inline void -CommOS_WakeUp(CommOSWaitQueue *wq) -{ - wake_up(wq); -} - - -/** - * @brief Allocates kernel memory of specified size; does not sleep. - * @param size size to allocate. - * @return Address of allocated memory or NULL if the allocation fails. - */ - -static inline void * -CommOS_KmallocNoSleep(unsigned int size) -{ - return kmalloc(size, GFP_ATOMIC); -} - - -/** - * @brief Allocates kernel memory of specified size; may sleep. - * @param size size to allocate. - * @return Address of allocated memory or NULL if the allocation fails. - */ - -static inline void * -CommOS_Kmalloc(unsigned int size) -{ - return kmalloc(size, GFP_KERNEL); -} - - -/** - * @brief Frees previously allocated kernel memory. - * @param obj object to free. - */ - -static inline void -CommOS_Kfree(void *obj) -{ - if (obj) { - kfree(obj); - } -} - - -/** - * @brief Yields the current cpu to other runnable tasks. - */ - -static inline void -CommOS_Yield(void) -{ - cond_resched(); -} - - -/** - * @brief Gets the current time in milliseconds. - * @return Current time in milliseconds, with precision of at most one tick. - */ - -static inline unsigned long long -CommOS_GetCurrentMillis(void) -{ - return (unsigned long long)jiffies_to_msecs(jiffies); -} - - -/** - * @brief Initializes given list. - * @param list list to initialize. - */ - -static inline void -CommOS_ListInit(CommOSList *list) -{ - INIT_LIST_HEAD(list); -} - - -/** - * @brief Tests if list is empty. - * @param list list to test. - * @return non-zero if empty, zero otherwise. - */ - -#define CommOS_ListEmpty(list) list_empty((list)) - - -/** - * @brief Adds given element to beginning of list. - * @param list list to add to. - * @param elem element to add. - */ - -#define CommOS_ListAdd(list, elem) list_add((elem), (list)) - - -/** - * @brief Adds given element to end of list. - * @param list list to add to. - * @param elem element to add. - */ - -#define CommOS_ListAddTail(list, elem) list_add_tail((elem), (list)) - - -/** - * @brief Deletes given element from its list. - * @param elem element to delete. - */ - -#define CommOS_ListDel(elem) \ - do { \ - list_del((elem)); \ - INIT_LIST_HEAD((elem)); \ - } while (0) - - -/** - * @brief Iterates over a list. - * @param list list to iterate over. - * @param[out] item stores next element. - * @param itemListFieldName name in the item structure storing the list head. - */ - -#define CommOS_ListForEach(list, item, itemListFieldName) \ - list_for_each_entry((item), (list), itemListFieldName) - - -/** - * @brief Iterates safely over a list. - * @param list list to iterate over. - * @param[out] item stores next element. May be deleted in the loop. - * @param[out] tmpItem saves iteration element. - * @param itemListFieldName name in the item structure storing the list head. - */ - -#define CommOS_ListForEachSafe(list, item, tmpItem, itemListFieldName) \ - list_for_each_entry_safe((item), (tmpItem), (list), itemListFieldName) - - -/** - * @brief Combines two lists, adds second list to beginning of first one. - * @param list list to add to. - * @param list2 list to add. - */ - -#define CommOS_ListSplice(list, list2) list_splice((list2), (list)) - - -/** - * @brief Combines two lists, adds second list to end of first one. - * @param list list to add to. - * @param list2 list to add. - */ - -#define CommOS_ListSpliceTail(list, list2) list_splice_tail((list2), (list)) - - -/** - * @brief Gets current module handle. - * @return module handle. - */ - -static inline CommOSModule -CommOS_ModuleSelf(void) -{ - return THIS_MODULE; -} - - -/** - * @brief Retains module. - * @param[in,out] module to retain. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -CommOS_ModuleGet(CommOSModule module) -{ - int rc = 0; - - if (!module) { - goto out; - } - if (!try_module_get(module)) { - rc = -1; - } - -out: - return rc; -} - - -/** - * @brief Releases module. - * @param[in,out] module to release. - */ - -static inline void -CommOS_ModulePut(CommOSModule module) -{ - if (module) { - module_put(module); - } -} - - -/** - * @brief Inserts r/w memory barrier. - */ - -#define CommOS_MemBarrier smp_mb - -#endif /* _COMM_OS_LINUX_H_ */ diff --git a/arch/arm/mvp/mvpkm/comm_transp.h b/arch/arm/mvp/mvpkm/comm_transp.h deleted file mode 100644 index a90eb40..0000000 --- a/arch/arm/mvp/mvpkm/comm_transp.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Generic shared memory transport API. - */ - -#ifndef _COMM_TRANSP_H_ -#define _COMM_TRANSP_H_ - -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/* - * Common shared memory identifier. - * External handle that makes sense to both hypervisor and guest. - */ - -#define COMM_TRANSP_ID_8_ANY ((unsigned char)-1) -#define COMM_TRANSP_ID_32_ANY ((unsigned int)-1) -#define COMM_TRANSP_ID_64_ANY ((unsigned long long)-1) - - -typedef struct CommTranspID { - union { - unsigned char d8[8]; - unsigned int d32[2]; - unsigned long long d64; - }; -} CommTranspID; - - -/* Basic initialization arguments. */ - -typedef enum CommTranspInitMode { - COMM_TRANSP_INIT_CREATE = 0x0, - COMM_TRANSP_INIT_ATTACH = 0x1 -} CommTranspInitMode; - -typedef struct CommTranspInitArgs { - unsigned int capacity; // Shared memory capacity. - unsigned int type; // Type / implementation using this area. - CommTranspID id; // ID (name) of shared memory area. - CommTranspInitMode mode; // Init mode (above). -} CommTranspInitArgs; - - -/** - * @brief Generate a type id from description (protocol) string. This function - * uses djb2, a string hashing algorithm by Dan Bernstein. - * (see http://www.cse.yorku.ca/~oz/hash.html) - * @param str string to hash - * @return 32-bit hash value - */ - -static inline unsigned int -CommTransp_GetType(const char *str) -{ - unsigned int hash = 5381; - int c; - - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - return hash; -} - -#endif // _COMM_TRANSP_H_ diff --git a/arch/arm/mvp/mvpkm/comm_transp_impl.h b/arch/arm/mvp/mvpkm/comm_transp_impl.h deleted file mode 100644 index 6438ac9..0000000 --- a/arch/arm/mvp/mvpkm/comm_transp_impl.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Generic shared memory transport private API. - */ - -#ifndef _COMM_TRANSP_IMPL_H_ -#define _COMM_TRANSP_IMPL_H_ - -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "comm_transp.h" - - -/* Shared memory opaque descriptor/handle. Only meaningful locally. */ - -typedef struct CommTranspPriv *CommTransp; - - -/* Asynchronous signaling initialization arguments. */ - -typedef enum CommTranspIOEvent { - COMM_TRANSP_IO_DETACH = 0x0, - COMM_TRANSP_IO_IN = 0x1, - COMM_TRANSP_IO_OUT = 0x2, - COMM_TRANSP_IO_INOUT = 0x3 -} CommTranspIOEvent; - -typedef struct CommTranspEvent { - void (*ioEvent)(CommTransp transp, CommTranspIOEvent event, void *data); - void *ioEventData; -} CommTranspEvent; - - -/* - * Mechanism to detect and optionally attach to, created shared memory regions. - */ - -typedef struct CommTranspListener { - int (*probe)(CommTranspInitArgs *transpArgs, void *probeData); - void *probeData; -} CommTranspListener; - - - -/* - * Function prototypes. - */ - -int CommTranspEvent_Init(void); -void CommTranspEvent_Exit(void); -int CommTranspEvent_Process(CommTranspID *transpID, CommTranspIOEvent event); -int -CommTranspEvent_Raise(unsigned int peerEvID, - CommTranspID *transpID, - CommTranspIOEvent event); - -int CommTransp_Init(void); -void CommTransp_Exit(void); - -int CommTransp_Register(const CommTranspListener *listener); -void CommTransp_Unregister(const CommTranspListener *listener); -int -CommTransp_Notify(const CommTranspID *notificationCenterID, - CommTranspInitArgs *transpArgs); - -int -CommTransp_Open(CommTransp *transp, - CommTranspInitArgs *transpArgs, - CommTranspEvent *transpEvent); -void CommTransp_Close(CommTransp transp); - -int CommTransp_EnqueueSpace(CommTransp transp); -int CommTransp_EnqueueReset(CommTransp transp); -int CommTransp_EnqueueCommit(CommTransp transp); -int -CommTransp_EnqueueSegment(CommTransp transp, - const void *buf, - unsigned int bufLen); - -int CommTransp_DequeueSpace(CommTransp transp); -int CommTransp_DequeueReset(CommTransp transp); -int CommTransp_DequeueCommit(CommTransp transp); -int -CommTransp_DequeueSegment(CommTransp transp, - void *buf, - unsigned int bufLen); - -unsigned int CommTransp_RequestInlineEvents(CommTransp transp); -unsigned int CommTransp_ReleaseInlineEvents(CommTransp transp); - - -/** - * @brief Enqueues data into the transport object, data is available for - * reading immediately. - * @param transp handle to the transport object. - * @param buf bytes to enqueue. - * @param bufLen number of bytes to enqueue. - * @return number of bytes enqueued on success, < 0 otherwise. - */ - -static inline int -CommTransp_EnqueueAtomic(CommTransp transp, - const void *buf, - unsigned int bufLen) -{ - int rc; - - CommTransp_EnqueueReset(transp); - rc = CommTransp_EnqueueSegment(transp, buf, bufLen); - if (CommTransp_EnqueueCommit(transp)) { - rc = -1; - } - return rc; -} - - -/** - * @brief Dequeues data from the transport object into a buffer. - * @param transp handle to the transport object. - * @param[out] buf buffer to copy to. - * @param bufLen number of bytes to dequeue. - * @return number of bytes dequeued on success, < 0 otherwise, - */ - -static inline int -CommTransp_DequeueAtomic(CommTransp transp, - void *buf, - unsigned int bufLen) -{ - int rc; - - CommTransp_DequeueReset(transp); - rc = CommTransp_DequeueSegment(transp, buf, bufLen); - if (CommTransp_DequeueCommit(transp)) { - rc = -1; - } - return rc; -} - -#endif // _COMM_TRANSP_IMPL_H_ diff --git a/arch/arm/mvp/mvpkm/coproc_defs.h b/arch/arm/mvp/mvpkm/coproc_defs.h deleted file mode 100644 index 26218cf..0000000 --- a/arch/arm/mvp/mvpkm/coproc_defs.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Constant definitions for ARM CP15 coprocessor registers. - * - * Derived from tweety hypervisor/src/armv6/trango_macros.inc file - */ - -#ifndef _COPROC_DEFS_H_ -#define _COPROC_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @name CP10 registers. - * - * MCR/MRC format: @code #define , , , @endcode - * @{ - */ -#define VFP_FPSID 7, c0, c0, 0 -#define VFP_MVFR0 7, c7, c0, 0 -#define VFP_MVFR1 7, c6, c0, 0 -#define VFP_FPEXC 7, c8, c0, 0 -#define VFP_FPSCR 7, c1, c0, 0 -#define VFP_FPINST 7, c9, c0, 0 -#define VFP_FPINST2 7, c10, c0, 0 -/*@}*/ - - -/** - * @name CP15 registers. - * - * MCR/MRC format: @code #define , , , @endcode - * MCRR format: @code #define , @endcode - * @{ - */ -#define ID_CODE 0, c0, c0, 0 -#define CACHE_TYPE 0, c0, c0, 1 -#define MPIDR 0, c0, c0, 5 -#define CACHE_SIZE_ID 1, c0, c0, 0 -#define CACHE_LEVEL_ID 1, c0, c0, 1 -#define CACHE_SIZE_SELECTION 2, c0, c0, 0 -#define MEM_MODEL_FEATURE_0 0, c0, c1, 4 -#define CONTROL_REGISTER 0, c1, c0, 0 -#define TTBASE0_POINTER 0, c2, c0, 0 -#define TTBASE1_POINTER 0, c2, c0, 1 -#define TTCONTROL 0, c2, c0, 2 -#define DOMAIN_CONTROL 0, c3, c0, 0 -#define DATA_FAULT_STATUS 0, c5, c0, 0 -#define INST_FAULT_STATUS 0, c5, c0, 1 -#define AUX_DATA_FAULT_STATUS 0, c5, c1, 0 -#define AUX_INST_FAULT_STATUS 0, c5, c1, 1 -#define DATA_FAULT_ADDRESS 0, c6, c0, 0 -#define INST_FAULT_ADDRESS 0, c6, c0, 2 -#define WAIT_FOR_INTERRUPT 0, c7, c0, 4 -#define PHYSICAL_ADDRESS 0, c7, c4, 0 -#define ICACHE_INVALIDATE_POU 0, c7, c5, 0 -#define ICACHE_INVALIDATE_MVA_POU 0, c7, c5, 1 -#define ICACHE_INVALIDATE_INDEX 0, c7, c5, 2 -#define BTAC_INVALIDATE 0, c7, c5, 6 -#define BTAC_INVALIDATE_MVA 0, c7, c5, 7 -#define DCACHE_INVALIDATE 0, c7, c6, 0 -#define DCACHE_INVALIDATE_MVA_POC 0, c7, c6, 1 -#define DCACHE_INVALIDATE_INDEX 0, c7, c6, 2 -#define UCACHE_INVALIDATE 0, c7, c7, 0 -#define V2P_CURRENT_PRIV_READ 0, c7, c8, 0 -#define V2P_CURRENT_PRIV_WRITE 0, c7, c8, 1 -#define V2P_CURRENT_USER_READ 0, c7, c8, 2 -#define V2P_CURRENT_USER_WRITE 0, c7, c8, 3 -#define V2P_OTHER_PRIV_READ 0, c7, c8, 4 -#define V2P_OTHER_PRIV_WRITE 0, c7, c8, 5 -#define V2P_OTHER_USER_READ 0, c7, c8, 6 -#define V2P_OTHER_USER_WRITE 0, c7, c8, 7 -#define DCACHE_CLEAN 0, c7, c10, 0 -#define DCACHE_CLEAN_MVA_POC 0, c7, c10, 1 -#define DCACHE_CLEAN_INDEX 0, c7, c10, 2 -#define DCACHE_CLEAN_MVA_POU 0, c7, c11, 1 -#define DCACHE_CLEAN_INVALIDATE 0, c7, c14, 0 -#define DCACHE_CLEAN_INVALIDATE_MVA_POC 0, c7, c14, 1 -#define DCACHE_CLEAN_INVALIDATE_INDEX 0, c7, c14, 2 -#define ITLB_INVALIDATE_ALL 0, c8, c5, 0 -#define ITLB_INVALIDATE_SINGLE 0, c8, c5, 1 -#define ITLB_INVALIDATE_ASID 0, c8, c5, 2 -#define DTLB_INVALIDATE_ALL 0, c8, c6, 0 -#define DTLB_INVALIDATE_SINGLE 0, c8, c6, 1 -#define DTLB_INVALIDATE_ASID 0, c8, c6, 2 -#define UTLB_INVALIDATE_ALL 0, c8, c7, 0 -#define UTLB_INVALIDATE_SINGLE 0, c8, c7, 1 -#define UTLB_INVALIDATE_ASID 0, c8, c7, 2 -#define TLB_LOCKDOWN 0, c10, c0, 0 -#define PRIMARY_REGION_REMAP 0, c10, c2, 0 -#define MAIR0 PRIMARY_REGION_REMAP -#define NORMAL_MEMORY_REMAP 0, c10, c2, 1 -#define MAIR1 NORMAL_MEMORY_REMAP -#define VECTOR_BASE 0, c12, c0, 0 -#define INTERRUPT_STATUS 0, c12, c1, 0 -#define CONTEXT_ID 0, c13, c0, 1 -#define TID_USER_RW 0, c13, c0, 2 -#define TID_USER_RO 0, c13, c0, 3 -#define TID_PRIV_RW 0, c13, c0, 4 -#define CLEAR_FAULT_IN_EFSR 7, c15, c0, 1 -#define VBAR 0, c12, c0, 0 - -/* - * ARMv7 performance counters' registers (MVP related) - * - ARM Architecture Reference Manual v7-A and v7-R: DDI0406B - * - Cortex-A8 TRM, rev.r1p1: DDI0344B - */ -#define PERF_MON_CONTROL_REGISTER 0, c9, c12, 0 -#define CYCLE_COUNT 0, c9, c13, 0 -#define PERF_MON_COUNT_SET 0, c9, c12, 1 -#define PERF_MON_COUNT_CLR 0, c9, c12, 2 -#define PERF_MON_FLAG_RDCLR 0, c9, c12, 3 -#define PERF_MON_EVENT_SELECT 0, c9, c12, 5 -#define PERF_MON_EVENT_TYPE 0, c9, c13, 1 -#define PERF_MON_EVENT_COUNT 0, c9, c13, 2 -#define PERF_MON_INTEN_SET 0, c9, c14, 1 -#define PERF_MON_INTEN_CLR 0, c9, c14, 2 - -#define COPROC_ACCESS_CONTROL 0, c1, c0, 2 -#define NON_SECURE_ACCESS_CONTROL 0, c1, c1, 2 - -#define HYP_CFG 4, c1, c1, 0 -#define HYP_DEBUG_CONTROL 4, c1, c1, 1 -#define HYP_COPROC_TRAP 4, c1, c1, 2 -#define HYP_SYS_TRAP 4, c1, c1, 3 -#define VIRT_TCR 4, c2, c1, 2 -#define HYP_SYNDROME 4, c5, c2, 0 -#define HYP_DATA_FAULT_ADDRESS 4, c6, c0, 0 -#define HYP_INST_FAULT_ADDRESS 4, c6, c0, 2 -#define HYP_IPA_FAULT_ADDRESS 4, c6, c0, 4 -#define UTLB_INVALIDATE_ALL_HYP 4, c8, c7, 0 -#define UTLB_INVALIDATE_SINGLE_HYP 4, c8, c7, 1 -#define UTLB_INVALIDATE_ALL_NS_NON_HYP 4, c8, c7, 4 - -#define EXT_TTBR0 0, c2 -#define EXT_TTBR1 1, c2 -#define HYP_TTBR 4, c2 -#define VIRT_TTBR 6, c2 -#define EXT_PHYSICAL_ADDRESS 0, c7 -/*@}*/ - -/** - * @name CP15 configuration control register bits. - * @{ - */ -#define ARM_CP15_CNTL_M (1 << 0) -#define ARM_CP15_CNTL_A (1 << 1) -#define ARM_CP15_CNTL_C (1 << 2) -#define ARM_CP15_CNTL_B (1 << 7) -#define ARM_CP15_CNTL_Z (1 << 11) -#define ARM_CP15_CNTL_I (1 << 12) -#define ARM_CP15_CNTL_V (1 << 13) -#define ARM_CP15_CNTL_U (1 << 22) -#define ARM_CP15_CNTL_VE (1 << 24) -#define ARM_CP15_CNTL_EE (1 << 25) -#define ARM_CP15_CNTL_TRE (1 << 28) -#define ARM_CP15_CNTL_AFE (1 << 29) -#define ARM_CP15_CNTL_TE (1 << 30) - -/*@}*/ - -/** - * @brief Initial System Control Register (SCTLR) value. - * - * Magic described on B3-97 ARM DDI 0406B, it's the power-on - * value, e.g. caches/MMU/alignment checking/TEX remap etc. disabled. - */ -#define ARM_CP15_CNTL_INIT 0x00c50078 - -/** - * @name System control coprocessor primary registers. - * Each primary register is backed by potentially multiple - * physical registers in the vCPU CP15 register file. - * @{ - */ -#define ARM_CP15_CRN_ID 0 ///< Processor ID, cache, TCM and TLB type -#define ARM_CP15_CRN_CNTL 1 ///< System configuration bits -#define ARM_CP15_CRN_PT 2 ///< Page table control -#define ARM_CP15_CRN_DACR 3 ///< Domain access control -#define ARM_CP15_CRN_F_STATUS 5 ///< Fault status -#define ARM_CP15_CRN_F_ADDR 6 ///< Fault address -#define ARM_CP15_CRN_CACHE 7 ///< Cache/write buffer control -#define ARM_CP15_CRN_TLB 8 ///< TLB control -#define ARM_CP15_CRN_REMAP 10 ///< Memory Remap registers -#define ARM_CP15_CRN_SER 12 ///< Security Extension registers -#define ARM_CP15_CRN_PID 13 ///< Process ID -#define ARM_CP15_CRN_TIMER 14 ///< Architecture timers - -#define ARM_CP15_CRM_INVALIDATE_D_CACHE_RANGE 6 -#define ARM_CP15_CRM_CLEAN_AND_INVALIDATE_D_CACHE_RANGE 14 -/*@}*/ - -/** - * @name ARMv7 performance counter control/status register bits (MVP related) - * INTEN: counters overflow interrupt enable - * CNTEN: counters enable - * @{ - */ -#define ARMV7_PMNC_E (1 << 0) -#define ARMV7_PMNC_INTEN_P0 (1 << 0) -#define ARMV7_PMNC_INTEN_P1 (1 << 1) -#define ARMV7_PMNC_INTEN_P2 (1 << 2) -#define ARMV7_PMNC_INTEN_P3 (1 << 3) -#define ARMV7_PMNC_INTEN_C (1 << 31) -#define ARMV7_PMNC_INTEN_MASK 0x8000000f -#define ARMV7_PMNC_CNTEN_P0 (1 << 0) -#define ARMV7_PMNC_CNTEN_P1 (1 << 1) -#define ARMV7_PMNC_CNTEN_P2 (1 << 2) -#define ARMV7_PMNC_CNTEN_P3 (1 << 3) -#define ARMV7_PMNC_CNTEN_C (1 << 31) -#define ARMV7_PMNC_FLAG_P0 (1 << 0) -#define ARMV7_PMNC_FLAG_P1 (1 << 1) -#define ARMV7_PMNC_FLAG_P2 (1 << 2) -#define ARMV7_PMNC_FLAG_P3 (1 << 3) -#define ARMV7_PMNC_FLAG_C (1 << 31) -/*@}*/ - -/** - * @name TTBR masks. - * See B4.9.2 ARM DDI 0100I and B3.12.24 ARM DDI 0406A. - * @{ - */ -#define ARM_CP15_TTBASE_MASK MVP_MASK(14, 18) -#define ARM_CP15_TTBASE_SPLIT_MASK(ttbcrn) MVP_MASK(14-ttbcrn, 18+ttbcrn) -#define ARM_CP15_TTATTRIB_MASK MVP_MASK(0, 6) -/*@}*/ - -/** - * @name ARM fault status register encoding/decoding. - * See B4.6 and B4.9.6 in ARM DDI 0100I. - * @{ - */ -#define ARM_CP15_FSR_STATUS_POS 0 -#define ARM_CP15_FSR_STATUS_POS2 10 -#define ARM_CP15_FSR_DOMAIN_POS 4 -#define ARM_CP15_FSR_WR_POS 11 - -#define ARM_CP15_FSR_STATUS_LEN 4 -#define ARM_CP15_FSR_DOMAIN_LEN 4 - -#define ARM_CP15_FSR_STATUS_DEBUG_EVENT 0x2 -#define ARM_CP15_FSR_STATUS_ALIGNMENT 0x1 -#define ARM_CP15_FSR_STATUS_ICACHE_MAINT 0x4 -#define ARM_CP15_FSR_STATUS_TRANSLATION_SECT 0x5 -#define ARM_CP15_FSR_STATUS_TRANSLATION_PAGE 0x7 -#define ARM_CP15_FSR_STATUS_DOMAIN_SECT 0x9 -#define ARM_CP15_FSR_STATUS_DOMAIN_PAGE 0xb -#define ARM_CP15_FSR_STATUS_PERMISSION_SECT 0xd -#define ARM_CP15_FSR_STATUS_PERMISSION_PAGE 0xf -#define ARM_CP15_FSR_STATUS_ACCESS_FLAG_SECT 0x3 -#define ARM_CP15_FSR_STATUS_ACCESS_FLAG_PAGE 0x6 -#define ARM_CP15_FSR_STATUS_SYNC_EXT_ABORT 0x8 -#define ARM_CP15_FSR_STATUS_ASYNC_EXT_ABORT 0x16 -/*@}*/ - -/** - * @brief Generate ARM fault status register value. - * - * @param fs status from Table B4-1. Only implemented for fs <= 0xf. - * @param domain domain accessed when abort occurred. - * @param write write access caused abort. - */ -#define ARM_CP15_FSR(fs,domain,write) \ - (((fs) << ARM_CP15_FSR_STATUS_POS) | \ - ((domain) << ARM_CP15_FSR_DOMAIN_POS) | \ - ((write) ? (1 << ARM_CP15_FSR_WR_POS) : 0)) - -#define ARM_CP15_FSR_STATUS(r) \ - (MVP_EXTRACT_FIELD((r), ARM_CP15_FSR_STATUS_POS, ARM_CP15_FSR_STATUS_LEN) | \ - (MVP_BIT((r), ARM_CP15_FSR_STATUS_POS2) << ARM_CP15_FSR_STATUS_LEN)) -#define ARM_CP15_FSR_DOMAIN(r) \ - MVP_EXTRACT_FIELD((r), ARM_CP15_FSR_DOMAIN_POS, ARM_CP15_FSR_DOMAIN_LEN) -#define ARM_CP15_FSR_WR(r) \ - MVP_BIT((r), ARM_CP15_FSR_WR_POS) -/*@}*/ - -/* - * This should mask out the major and minor revision numbers. - * As per http://infocenter.arm.com/help/topic/com.arm.doc.ddi0211k/I65012.html - */ -#define ARM_CP15_MAIN_ID_NOREVISION_MASK 0xFF0FFFF0 - -// 2-8 ARM DDI 0151C -#define ARM_CP15_MAIN_ID_920_T 0x41129200 -// 3-18 ARM DDI 0211H -#define ARM_CP15_MAIN_ID_1136J_S 0x4107B362 - -/* Coprocessor Access Control Register */ -#define CPACR_ASEDIS (1 << 31) -#define CPACR_D32DIS (1 << 30) -#define CPACR_CP10_MASK (0x3 << (10*2)) -#define CPACR_CP10_CP11_MASK ( (0x3 << (10*2)) | (0x3 << (11*2)) ) -#define CPACR_CP10_CP11_PRIV_ONLY ( (0x1 << (10*2)) | (0x1 << (11*2)) ) - /* 2-bit access permission per Co-Proc */ - -/** - * @name ARM VFP/Adv. SIMD Extension System Registers - * @{ - */ -#define ARM_VFP_SYSTEM_REG_FPSID 0x0 -#define ARM_VFP_SYSTEM_REG_FPSCR 0x1 -#define ARM_VFP_SYSTEM_REG_MVFR1 0x6 -#define ARM_VFP_SYSTEM_REG_MVFR0 0x7 -#define ARM_VFP_SYSTEM_REG_FPEXC 0x8 -#define ARM_VFP_SYSTEM_REG_FPINST 0x9 -#define ARM_VFP_SYSTEM_REG_FPINST2 0xa - -#define ARM_VFP_SYSTEM_REG_FPEXC_EX (1 << 31) -#define ARM_VFP_SYSTEM_REG_FPEXC_EN (1 << 30) -#define ARM_VFP_SYSTEM_REG_FPEXC_FP2V (1 << 28) - -#define ARM_VFP_SYSTEM_REG_MVFR0_A_SIMD_BIT (0) -#define ARM_VFP_SYSTEM_REG_MVFR0_A_SIMD_MASK (0xf << ARM_VFP_SYSTEM_REG_MVFR0_A_SIMD_BIT) -/*@}*/ - -/** - * @name ARM Multi Processor ID Register (MPIDR) decoding - * @{ - */ -#define ARM_CP15_MPIDR_MP (0x1 << 31) -#define ARM_CP15_MPIDR_U (0x1 << 30) -/*@}*/ - -#endif /// ifndef _COPROC_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/cpufreq_kernel.c b/arch/arm/mvp/mvpkm/cpufreq_kernel.c deleted file mode 100644 index 4ba71f2..0000000 --- a/arch/arm/mvp/mvpkm/cpufreq_kernel.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MVP host kernel cpufreq related - * - * Track CPU frequency changes. - */ - -#include -#include -#include -#include -#include -#include - -#include "mvp.h" -#include "cpufreq_kernel.h" -#include "mvp_timer.h" - -DEFINE_PER_CPU(struct TscToRate64Cb, tscToRate64); - - -/** - * @brief Return current CPU frequency - * @param cpu CPU number - * @return CPU frequency in Hz - * - * When CPU_FREQ is not available, it uses hardcoded frequencies. - */ -static uint32 -GetCpuFrequency(unsigned int cpu) -{ - unsigned int counterKHZ; - -#ifdef CONFIG_CPU_FREQ - counterKHZ = cpufreq_quick_get(cpu); - if (counterKHZ == 0) { - counterKHZ = cpufreq_get(cpu); - FATAL_IF(counterKHZ == 0); - } -#elif defined(MVP_HOST_BOARD_ve) - /** - * @knownjira{MVP-143} - * We're only using this under the simulator, and it's almost non perceptible to - * provide a fixed TSC frequency as the instructions / second executed widely - * varies depending over time. While we resolve this issue we can use the - * BogoMIPS reported at boot for now. - */ - KNOWN_BUG(MVP-143); - counterKHZ = 125e3; - printk(KERN_INFO "mvpkm: CPU_FREQ not available, forcing TSC to %d KHz\n", counterKHZ); -#elif defined(MVP_HOST_BOARD_panda) - counterKHZ = 1e6; -#else - /* - * If the kernel can't tell us and we have no further host knowledge, - * time to die. - */ -#error "host TSC frequency unknown." -#endif - - return counterKHZ * 1000; -} - -/** - * @brief Compute TSC to RATE64 ratio - * @param cpuFreq TSC frequency in Hz - * @param[out] ttr tscToRate64 pointer - */ -static void -TscToRate64(uint32 cpuFreq, struct TscToRate64Cb *ttr) -{ - uint32 shift; - uint64 mult; - - /* - * A little bit of math ! - * - * We need here to convert the TSC value to our RATE64 timebase. - * - * In other words: - * - * tsc * MVP_TIMER_RATE64 - * rate64 = ---------------------- - * cpuFreq - * - * But we are limited by CPU performance (does not divide easily), CPU - * instruction set, and CPU register file width. To fit performance - * requirement, the math becomes: - * - * rate64 = (cpuFreq * mult) >> shift - * - * To respect instruction set, both cpuFreq and mult must be 32-bit - * numbers. Thus (cpuFreq * mult) will be a 64-bit number. - * - * - * Log2 rate64 = Log2 cpuFreq + Log2 mult - shift - * - * shift = Log2 mult + Log2 cpuFreq - Log2 rate64 - * - * && Log2 mult < 32 - * - * => shift < 32 + Log2 cpuFreq - Log2 rate64 - * - * rate64 << shift - * => mult = --------------- - * cpuFreq - * - * (rate64 << shift) must be a 64-bit number: - * - * Log2 rate64 + shift < 64 - * - * => shift < 64 - Log2 rate64 - * - * While cpuFreq is lower than 2^32 Hz, we have: - * - * shift < 32 + Log2 cpuFreq - Log2 rate64 < 64 - Log2 rate64 - * - * As (31 - CLZ32 x) <= Log2 x < (32 - CLZ32 x): - * - * 31 - CLZ32 cpuFreq <= Log2 cpuFreq && - * - * CLZ32 rate64 - 32 < - Log2 rate64 - * - * 31 + CLZ32 rate64 - CLZ32 cpuFreq < 32 + Log2 cpuFreq - Log2 rate64 - * - * As we want shift to be as great as possible: - * - * => shift = 31 + CLZ32 rate64 - CLZ32 cpuFreq - * - * rate64 << shift - * && mult = --------------- - * cpuFreq - * - * - */ - - /* CLZ(MVP_TIMER_RATE64) is optimized by compiler in a constant */ - shift = 31 + CLZ(MVP_TIMER_RATE64) - CLZ(cpuFreq); - mult = MVP_TIMER_RATE64; - mult <<= shift; - do_div(mult, cpuFreq); - - /* verify Log2 mult < 32 */ - ASSERT(mult < (1ULL<<32)); - - /* update global variables */ - ttr->mult = mult; - ttr->shift = shift; -} - -/** - * @brief Compute TSC to RATE64 ratio for the current cpu - * @param info TSC frequency in Hz - * @sideeffect Update local cpu tscToRate64 - */ -static void -TscToRate64IPI(void *info) -{ - uint32 cpuFreq = (uint32)info; - - TscToRate64(cpuFreq, &__get_cpu_var(tscToRate64)); -} - -/** - * @brief Handle cpufreq transition notifications. - * @param nb Notifier block - * @param val Notified event - * @param data Linux cpufreq_freqs info - * @return NOTIFY_OK - * - * @note A frequency change can fail in which case PRECHANGE and POSTCHANGE - * will not be paired and you get any number of PRECHANGE and maybe never a - * POSTCHANGE (i.e. there is not enough battery voltage available to support a - * high frequency). - * @note This is called once per cpu core that is changing but not always on - * the core that is changing. - */ -static int -CpuFreqNotifier(struct notifier_block *nb, - unsigned long val, - void *data) -{ - struct cpufreq_freqs *freq = data; - bool updateRequired; - - /* ASSUMPTION: Only freq. increases can fail and that it is ok to tell the - * guest a higher frequency than it really is but not the other way around - * as that just leads to time "jumping" forward in the guest not backwards. - */ - updateRequired = (val == CPUFREQ_PRECHANGE && freq->new > freq->old) || - (val == CPUFREQ_POSTCHANGE && freq->new < freq->old); - - /* Call TscToRate64() on the correct CPU core so that locking is not - * required. This also has the side-effect of forcing any currently running - * vCPU's to worldswitch back to the host and correctly update the world - * switch page. - */ - if (updateRequired) { - uint32 hz = freq->new * 1000; - smp_call_function_single(freq->cpu, TscToRate64IPI, (void *)hz, false); - } - - return NOTIFY_OK; -} - -/** - * @brief Notifier block for cpufreq transitions - */ -static struct notifier_block cpuFreqNotifierBlock = { - .notifier_call = CpuFreqNotifier -}; - -/** - * @brief Handle cpuUp notifications. - * @param nb Notifier block - * @param action Notified action, e.g., CPU_ONLINE - * @param hcpu cpu no - * @return NOTIFY_OK - */ -static int -CpuUpNotifier(struct notifier_block *nb, - unsigned long action, - void *hcpu) -{ - long cpu = (long)hcpu; - - switch (action) { - case CPU_ONLINE: - /* The new CPU core is not yet executing normal tasks, so it is safe - * to update it's scaling factors from a different core. */ - TscToRate64(GetCpuFrequency(cpu), &per_cpu(tscToRate64, cpu)); - break; - default: - /* - * Ignore all other action notifications, - * such as CPU_UP_PREPARE, CPU_UP_CANCELED, - * CPU_DOWN_PREPARE, CPU_DOWN_FAILED, - * CPU_DYING, CPU_DEAD, CPU_POST_DEAD, etc. - * as they are irrelevant here. - */ - break; - } - - return NOTIFY_OK; -} - -/** - * @brief Notifier block for cpus going online - */ -static struct notifier_block cpuUpNotifierBlock = { - .notifier_call = CpuUpNotifier -}; - -/** - * @brief Initialize TSC ratio and register cpufreq transitions. - */ -void -CpuFreq_Init(void) -{ - int ret; - int cpu; - - /* register callback on frequency change */ - ret = cpufreq_register_notifier(&cpuFreqNotifierBlock, - CPUFREQ_TRANSITION_NOTIFIER); - FATAL_IF(ret < 0); - - /* register callback on cpu core online */ - ret = register_cpu_notifier(&cpuUpNotifierBlock); - FATAL_IF(ret < 0); - - /* Make sure that things are correctly initialized. */ - for_each_online_cpu(cpu) { - TscToRate64(GetCpuFrequency(cpu), &per_cpu(tscToRate64, cpu)); - } -} - -/** - * @brief Exit cpufreq, unregister cpufreq transitions - */ -void -CpuFreq_Exit(void) -{ - cpufreq_unregister_notifier(&cpuFreqNotifierBlock, - CPUFREQ_TRANSITION_NOTIFIER); - unregister_cpu_notifier(&cpuUpNotifierBlock); -} diff --git a/arch/arm/mvp/mvpkm/cpufreq_kernel.h b/arch/arm/mvp/mvpkm/cpufreq_kernel.h deleted file mode 100644 index a84b6dd..0000000 --- a/arch/arm/mvp/mvpkm/cpufreq_kernel.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The monitor-kernel socket interface kernel-only definitions. - */ - -#ifndef _CPUFREQ_KERNEL_H -#define _CPUFREQ_KERNEL_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/* Scaling factors to convert CPU clock cycles to Rate64 value */ -struct TscToRate64Cb { - uint32 mult; - uint32 shift; -}; - -/* It is assumed that this is only accessed from the current CPU core and not - * "across cores" */ -DECLARE_PER_CPU(struct TscToRate64Cb, tscToRate64); - -void CpuFreq_Init(void); -void CpuFreq_Exit(void); - -#endif diff --git a/arch/arm/mvp/mvpkm/exc_defs.h b/arch/arm/mvp/mvpkm/exc_defs.h deleted file mode 100644 index 1d5d063..0000000 --- a/arch/arm/mvp/mvpkm/exc_defs.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Exception-related definitions. See A2.6 ARM DDI 0100I. - */ - -#ifndef _EXC_DEFS_H_ -#define _EXC_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define EXC_VECTOR_SIZE 0x20 - -#define EXC_RESET_VECTOR_OFFSET 0x00 -#define EXC_UNDEFINED_VECTOR_OFFSET 0x04 -#define EXC_SWI_VECTOR_OFFSET 0x08 -#define EXC_PREFETCH_ABORT_VECTOR_OFFSET 0x0c -#define EXC_DATA_ABORT_VECTOR_OFFSET 0x10 -#define EXC_HYP_VECTOR_OFFSET 0x14 -#define EXC_IRQ_VECTOR_OFFSET 0x18 -#define EXC_FIQ_VECTOR_OFFSET 0x1c - -#define EXC_ARM_UNDEFINED_SAVED_PC_OFFSET 4 -#define EXC_ARM_SWI_SAVED_PC_OFFSET 4 -#define EXC_ARM_PREFETCH_ABORT_SAVED_PC_OFFSET 4 -#define EXC_ARM_DATA_ABORT_SAVED_PC_OFFSET 8 -#define EXC_ARM_IRQ_SAVED_PC_OFFSET 4 -#define EXC_ARM_FIQ_SAVED_PC_OFFSET 4 - -#define EXC_THUMB_UNDEFINED_SAVED_PC_OFFSET 2 -#define EXC_THUMB_SWI_SAVED_PC_OFFSET 2 -#define EXC_THUMB_PREFETCH_ABORT_SAVED_PC_OFFSET 4 -#define EXC_THUMB_DATA_ABORT_SAVED_PC_OFFSET 8 -#define EXC_THUMB_IRQ_SAVED_PC_OFFSET 4 -#define EXC_THUMB_FIQ_SAVED_PC_OFFSET 4 - -#define EXC_SAVED_PC_OFFSET(exc, cpsr) \ - (((cpsr) & ARM_PSR_T) ? EXC_THUMB_##exc##_SAVED_PC_OFFSET : \ - EXC_ARM_##exc##_SAVED_PC_OFFSET) - -#endif /// _EXC_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/exc_types.h b/arch/arm/mvp/mvpkm/exc_types.h deleted file mode 100644 index ba835e5..0000000 --- a/arch/arm/mvp/mvpkm/exc_types.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Exception-related types. See A2.6 ARM DDI 0100I. - */ - -#ifndef _EXC_TYPES_H_ -#define _EXC_TYPES_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @brief ARM hardware exception enumeration. EXC_NONE is added to provide - * a distinguished value to flag non-exception states. - */ -typedef enum { - EXC_NONE, - EXC_RESET, - EXC_UNDEFINED, - EXC_SWI, - EXC_PREFETCH_ABORT, - EXC_DATA_ABORT, - EXC_IRQ, - EXC_FIQ -} ARM_Exception; - -#endif /// _EXC_TYPES_H_ diff --git a/arch/arm/mvp/mvpkm/exitstatus.h b/arch/arm/mvp/mvpkm/exitstatus.h deleted file mode 100644 index c53827a..0000000 --- a/arch/arm/mvp/mvpkm/exitstatus.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Exit Status values - */ - -#ifndef _EXITSTATUS_H -#define _EXITSTATUS_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_HOSTUSER -#include "include_check.h" - - -#define _EXIT_STATUS_DEF \ - _EXIT_STATUS_ITEM(Success, 0) \ - _EXIT_STATUS_ITEM(ReturnToHost, 1) \ - _EXIT_STATUS_ITEM(GuestExit, 2) \ - _EXIT_STATUS_ITEM(HostRequest, 3) \ - _EXIT_STATUS_ITEM(VMXFatalError, 4) \ - _EXIT_STATUS_ITEM(VMMFatalError, 5) \ - _EXIT_STATUS_ITEM(MVPDFatalError, 6) \ - _EXIT_STATUS_ITEM(VPNFatalError, 7) \ - _EXIT_STATUS_ITEM(VMXFindCause, 8) - - -enum ExitStatus { -#define _EXIT_STATUS_ITEM(name,num) ExitStatus##name = num, -_EXIT_STATUS_DEF -#undef _EXIT_STATUS_ITEM -}; - -typedef enum ExitStatus ExitStatus; - -#ifndef __cplusplus -static const char * ExitStatusName[] UNUSED = { -#define _EXIT_STATUS_ITEM(name,num) [ExitStatus##name] = #name, -_EXIT_STATUS_DEF -#undef _EXIT_STATUS_ITEM -}; -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/fatalerror.h b/arch/arm/mvp/mvpkm/fatalerror.h deleted file mode 100644 index 58e1f98..0000000 --- a/arch/arm/mvp/mvpkm/fatalerror.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief fatal error handlers. They all post fatal errors regardless of build - * type. - */ - -#ifndef _FATALERROR_H -#define _FATALERROR_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mvp_compiler.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum FECode { - FECodeMisc, ///< generic FATAL() call of sorts - FECodeOOM, ///< FATAL_OOM() call of sorts - FECodeAssert, ///< ASSERT() call of sorts - FECodeNR, ///< NOT_REACHED() call of sorts - FECodeNI, ///< NOT_IMPLEMENTED() call of sorts - FECodeNT, ///< NOT_TESTED() call of sorts - FECodeCF ///< COMPILE_FAIL() call of sorts -}; -typedef enum FECode FECode; - -#define FATAL() FatalError(__FILE__, __LINE__, FECodeMisc, 0, NULL) -#define FATAL_IF(x) do { if (UNLIKELY(x)) FATAL(); } while (0) -#define FATAL_OOM() FatalError(__FILE__, __LINE__, FECodeOOM, 0, NULL) -#define FATAL_OOM_IF(x) do { if (UNLIKELY(x)) FATAL_OOM(); } while (0) - -extern _Bool FatalError_hit; - -void NORETURN FatalError(char const *file, - int line, - FECode feCode, - int bugno, - char const *fmt, - ...) FORMAT(printf,5,6); - -#define FATALERROR_COMMON(printFunc, \ - printFuncV, \ - file, \ - line, \ - feCode, \ - bugno, \ - fmt) { \ - va_list ap; \ - \ - printFunc("FatalError: %s:%d, code %d, bugno %d\n", \ - file, line, feCode, bugno); \ - if (fmt != NULL) { \ - va_start(ap, fmt); \ - printFuncV(fmt, ap); \ - va_end(ap); \ - } \ - } - -#if defined IN_HOSTUSER || defined IN_GUESTUSER || defined IN_WORKSTATION - -#define FATALERROR_POSIX_USER \ -void \ -FatalError_VErrPrintf(const char *fmt, va_list ap) \ -{ \ - vfprintf(stderr, fmt, ap); \ -} \ -\ -void \ -FatalError_ErrPrintf(const char *fmt, ...) \ -{ \ - va_list ap; \ - va_start(ap, fmt); \ - FatalError_VErrPrintf(fmt, ap); \ - va_end(ap); \ -} \ -\ -void NORETURN \ -FatalError(char const *file, \ - int line, \ - FECode feCode, \ - int bugno, \ - const char *fmt, \ - ...) \ -{ \ - FATALERROR_COMMON(FatalError_ErrPrintf, FatalError_VErrPrintf, file, line, feCode, bugno, fmt); \ - exit(EXIT_FAILURE); \ -} -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/include_check.h b/arch/arm/mvp/mvpkm/include_check.h deleted file mode 100644 index 2eeafe7..0000000 --- a/arch/arm/mvp/mvpkm/include_check.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for Empty File Placeholder - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ diff --git a/arch/arm/mvp/mvpkm/instr_defs.h b/arch/arm/mvp/mvpkm/instr_defs.h deleted file mode 100644 index ce6b1c9..0000000 --- a/arch/arm/mvp/mvpkm/instr_defs.h +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief ARM instruction encoding/decoding macros. - */ - -#ifndef _INSTR_DEFS_H_ -#define _INSTR_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "utils.h" - -/** - * @name ARM register synonyms. - * @{ - */ -#define ARM_REG_R0 0 -#define ARM_REG_R1 1 -#define ARM_REG_R2 2 -#define ARM_REG_R3 3 -#define ARM_REG_R4 4 -#define ARM_REG_R5 5 -#define ARM_REG_R6 6 -#define ARM_REG_R7 7 -#define ARM_REG_R8 8 -#define ARM_REG_R9 9 -#define ARM_REG_R10 10 -#define ARM_REG_FP 11 -#define ARM_REG_IP 12 -#define ARM_REG_SP 13 -#define ARM_REG_LR 14 -#define ARM_REG_PC 15 -/*@}*/ - -/** - * @name Data-processing + load/store instruction register field decoding. - * - * @note The following constants and masks used to fetch register operands - * are meant to be used in strictly the following sets of instructions: - * data processing instructions and load/store instructions. - * If you want to fetch the RN, RD, RS and RM fields for some other - * type of instructions, please verify before using -- you may have - * to introduce special constants just for those sets of instructions. - * For instance, all the multiply and signed multiply instructions have - * RN and RD reversed. So, @b BEWARE ! - * - * @{ - */ -#define ARM_INSTR_RN_BIT_POS 16 -#define ARM_INSTR_RD_BIT_POS 12 -#define ARM_INSTR_RS_BIT_POS 8 -#define ARM_INSTR_RM_BIT_POS 0 - -#define ARM_INSTR_RN_LENGTH 4 -#define ARM_INSTR_RD_LENGTH 4 -#define ARM_INSTR_RS_LENGTH 4 -#define ARM_INSTR_RM_LENGTH 4 - -#define ARM_INSTR_RN(r) \ - MVP_EXTRACT_FIELD((r), ARM_INSTR_RN_BIT_POS, ARM_INSTR_RN_LENGTH) -#define ARM_INSTR_RD(r) \ - MVP_EXTRACT_FIELD((r), ARM_INSTR_RD_BIT_POS, ARM_INSTR_RD_LENGTH) -#define ARM_INSTR_RS(r) \ - MVP_EXTRACT_FIELD((r), ARM_INSTR_RS_BIT_POS, ARM_INSTR_RS_LENGTH) -#define ARM_INSTR_RM(r) \ - MVP_EXTRACT_FIELD((r), ARM_INSTR_RM_BIT_POS, ARM_INSTR_RM_LENGTH) - -#define ARM_INSTR_RN_SHIFT(word) ((word) << ARM_INSTR_RN_BIT_POS) -#define ARM_INSTR_RD_SHIFT(word) ((word) << ARM_INSTR_RD_BIT_POS) -#define ARM_INSTR_RS_SHIFT(word) ((word) << ARM_INSTR_RS_BIT_POS) -#define ARM_INSTR_RM_SHIFT(word) ((word) << ARM_INSTR_RM_BIT_POS) - -#define ARM_INSTR_RN_MASK (~(ARM_INSTR_RN_SHIFT(0xf))) -#define ARM_INSTR_RD_MASK (~(ARM_INSTR_RD_SHIFT(0xf))) -#define ARM_INSTR_RS_MASK (~(ARM_INSTR_RS_SHIFT(0xf))) -#define ARM_INSTR_RM_MASK (~(ARM_INSTR_RM_SHIFT(0xf))) -/*@}*/ - -/** - * @name Condition field -- common bit layout across all ARM instructions. - * @{ - */ -#define ARM_INSTR_COND(instr) (MVP_EXTRACT_FIELD(instr, 28, 4)) - -#define ARM_INSTR_COND_EQ 0x0 ///< Equal -#define ARM_INSTR_COND_NE 0x1 ///< Not equal -#define ARM_INSTR_COND_CS 0x2 ///< Carry set/unsigned higher or same -#define ARM_INSTR_COND_CC 0x3 ///< Carry clear/unsigned lower -#define ARM_INSTR_COND_MI 0x4 ///< Minus/negative -#define ARM_INSTR_COND_PL 0x5 ///< Plus/positive or zero -#define ARM_INSTR_COND_VS 0x6 ///< Overflow -#define ARM_INSTR_COND_VC 0x7 ///< No overflow -#define ARM_INSTR_COND_HI 0x8 ///< Unsigned higher -#define ARM_INSTR_COND_LS 0x9 ///< Unsigned lower or same -#define ARM_INSTR_COND_GE 0xa ///< Signed greater than or equal -#define ARM_INSTR_COND_LT 0xb ///< Signed less than -#define ARM_INSTR_COND_GT 0xc ///< Signed greater than -#define ARM_INSTR_COND_LE 0xd ///< Signed less than or equal -#define ARM_INSTR_COND_AL 0xe ///< Always (unconditional) -#define ARM_INSTR_COND_NV 0xf ///< Invalid -/*@}*/ - -/** - * @name Load/store instruction decoding. - * @{ - */ - -/* - * I bit indicating Register(1)/Immediate(0) addressing modes. - */ -#define ARM_INSTR_LDST_IBIT(instr) (MVP_BIT(instr, 25)) - -/* - * U bit indicating whether the offset is added to the base (U == 1) - * or is subtracted from the base (U == 0). - */ -#define ARM_INSTR_LDST_UBIT(instr) (MVP_BIT(instr, 23)) - -/* - * B bit indicating byte(1)/word(0). - */ -#define ARM_INSTR_LDST_BBIT(instr) (MVP_BIT(instr, 22)) - -/* - * L bit indicating ld(1)/st(0). - */ -#define ARM_INSTR_LDST_LBIT(instr) (MVP_BIT(instr, 20)) - -/* - * Shifter operand. - */ -#define ARM_INSTR_LDST_SHIFTER_OPERAND(instr) (MVP_EXTRACT_FIELD(instr, 0, 12)) - -/* - * Immediate offset (12-bits wide) for load/store. - */ -#define ARM_INSTR_LDST_IMMEDIATE(instr) (MVP_EXTRACT_FIELD(instr, 0, 12)) - -/* - * Register List for multiple ld/st. - */ -#define ARM_INSTR_LDMSTM_REGLIST(instr) (MVP_EXTRACT_FIELD(instr, 0, 16)) - -/* - * Immediate Offset for Miscellaneous ld/st instructions. - */ -#define ARM_INSTR_MISC_LDST_IMM_OFFSET(instr) \ - ((MVP_EXTRACT_FIELD(instr, 8, 4) << 4) | MVP_EXTRACT_FIELD(instr, 0, 4)) - -/*@}*/ - -/** - * @name Thumb ldrt/strt instruction decoding. - * @{ - */ - -/* - * L bit indicating ld(1)/st(0). - */ -#define ARM_THUMB_INSTR_LDST_LBIT(instr) (MVP_BIT(instr, 20)) - -/* - * W bit indicating word(1)/byte(0). - */ -#define ARM_THUMB_INSTR_LDST_WBIT(instr) (MVP_BIT(instr, 22)) - -/* - * Immediate offset (8-bits wide) for load/store. - */ -#define ARM_THUMB_INSTR_LDST_IMMEDIATE(instr) (MVP_EXTRACT_FIELD(instr, 0, 8)) - -/*@}*/ - -/** - * @name ARM instruction opcodes. - * @{ - */ -#define ARM_OP_BR_A1 0x0a000000 -#define ARM_OP_BX_A1 0x012fff10 -#define ARM_OP_LDR_LIT_A1 0x051f0000 -#define ARM_OP_MOV_A1 0x01a00000 -#define ARM_OP_MOVW_A2 0x03000000 -#define ARM_OP_MOVT_A1 0x03400000 -#define ARM_OP_MRS_A1 0x01000000 -#define ARM_OP_MSR_T1 0x8000f380 -#define ARM_OP_MSR_A1 0x0120f000 -#define ARM_OP_HVC_A1 0x01400070 -#define ARM_OP_ERET_T1 0x8f00f3de -#define ARM_OP_ERET_A1 0x0160006e - -/* - * Set SYSm[5] = 1 for VE MSR/MRS, see p77-78 ARM PRD03-GENC-008353 10.0. - */ -#define ARM_OP_MRS_EXT_A1 (ARM_OP_MRS_A1 | (1 << 9)) -#define ARM_OP_MSR_EXT_T1 (ARM_OP_MSR_T1 | (1 << 21)) -#define ARM_OP_MSR_EXT_A1 (ARM_OP_MSR_A1 | (1 << 9)) - -#define ARM_OP_I 0x02000000 -#define ARM_OP_S 0x00100000 -#define ARM_OP_W 0x00200000 -/*@}*/ - -/** - * @name ARM instruction class - see Figure A3-1 ARM DDI 0100I. - * @{ - */ -#define ARM_INSTR_CLASS(instr) MVP_BITS(instr, 25, 27) - -#define ARM_INSTR_CLASS_BRANCH 0x5 -/*@}*/ - -/** - * @name ARM instruction opcode - see Figure A3-1 ARM DDI 0100I. Does not - * include extension bits 4-7. - * @{ - */ -#define ARM_INSTR_OPCODE(instr) MVP_EXTRACT_FIELD(instr, 20, 8) - -#define ARM_INSTR_OPCODE_EQ(instr1, instr2) \ - (ARM_INSTR_OPCODE(instr1) == ARM_INSTR_OPCODE(instr2)) -/*@}*/ - -/** - * @brief Extract the offset in a branch instruction - i.e., the least - * significant 24 bits sign extended. - */ -#define ARM_INSTR_BRANCH_TARGET(inst) (((int32)(inst) << 8) >> 6) - -/** - * @brief Check if a potential branch target is outside the encodable distance. - */ -#define ARM_INSTR_BRANCH_TARGET_OVERFLOWS(v) ((v) + (1 << 25) >= (1<< 26)) - -/** - * @brief Modify branch instruction encoding 'ins' with 'offset' as the - * new target. - */ -#define ARM_INSTR_BRANCH_UPDATE_OFFSET(ins, offset) \ - (((ins) & MVP_MASK(24, 8)) | (((offset) >> 2) & MVP_MASK(0, 24))) - -/** - * @brief B instruction encoding - see A8.6.16 ARM DDI 0406A. - */ -#define ARM_INSTR_BR_ENC(cond, offset) \ - (((cond) << 28) | ARM_OP_BR_A1 | MVP_BITS(((uint32)offset) >> 2, 0, 23)) - -/** - * @brief BX instruction encoding - */ -#define ARM_INSTR_BX_ENC(cond, rm) \ - (((cond) << 28) | ARM_OP_BX_A1 | (rm)) - -/** - * @brief LDR +literal instruction encoding - see ARM8.6.59 DDI 0506A. - */ -#define ARM_INSTR_LDR_LIT_ADD_ENC(cond, reg, offset) \ - (((cond) << 28) | ARM_OP_LDR_LIT_A1 | (1 << 23) | ((reg) << 12) | (offset)) - -/** - * @brief Generate encoding of the instruction mov rd, rn. - */ -#define ARM_INSTR_MOV_A1_ENC(cond, rd, rn) \ - ((((cond) << 28) | ARM_OP_MOV_A1 | ((rd) << 12) | (rn))) - -/** - * @name Encoding/decoding of MOVT/W instructions. - * @{ - */ -#define ARM_INSTR_MOVTW_IMMED(instr) \ - (MVP_BITS(instr, 0, 11) | (MVP_BITS(instr, 16, 19) << 12)) - -#define ARM_INSTR_MOVW_A2_ENC(cond,rd,immed) \ - (((cond) << 28) | ARM_OP_MOVW_A2 | (MVP_BITS(immed, 12, 15) << 16) | \ - ((rd) << 12) | MVP_BITS(immed, 0, 11)) - -#define ARM_INSTR_MOVT_A1_ENC(cond,rd,immed) \ - (((cond) << 28) | ARM_OP_MOVT_A1 | \ - (MVP_BITS(((immed) >> 16), 12, 15) << 16) | \ - ((rd) << 12) | MVP_BITS(((immed) >> 16), 0, 11)) -/*@}*/ - -/** - * @brief BKPT instruction encoding - see A4.1.7 ARM DDI 0100I. - */ -#define ARM_INSTR_BKPT_ENC(immed) \ - (0xe1200070 | \ - MVP_EXTRACT_FIELD(immed, 0, 4) | \ - (MVP_EXTRACT_FIELD(immed, 4, 12) << 8)) - -/** - * @name VE instruction encodings - see section 13 ARM PRD03-GENC-008353 10.0. - * @{ - */ -#define ARM_INSTR_HVC_A1_ENC(immed) \ - ((ARM_INSTR_COND_AL << 28) | ARM_OP_HVC_A1 | \ - MVP_EXTRACT_FIELD(immed, 0, 4) | \ - (MVP_EXTRACT_FIELD(immed, 4, 12) << 8)) - -#define ARM_INSTR_ERET_A1_ENC(cond) \ - (((cond) << 28) | ARM_OP_ERET_A1) - -/* - * R=0 - */ -#define ARM_REG_R8_USR 0 -#define ARM_REG_R9_USR 1 -#define ARM_REG_R10_USR 2 -#define ARM_REG_R11_USR 3 -#define ARM_REG_R12_USR 4 -#define ARM_REG_SP_USR 5 -#define ARM_REG_LR_USR 6 -#define ARM_REG_R8_FIQ 8 -#define ARM_REG_R9_FIQ 9 -#define ARM_REG_R10_FIQ 10 -#define ARM_REG_FP_FIQ 11 -#define ARM_REG_IP_FIQ 12 -#define ARM_REG_SP_FIQ 13 -#define ARM_REG_LR_FIQ 14 -#define ARM_REG_LR_IRQ 16 -#define ARM_REG_SP_IRQ 17 -#define ARM_REG_LR_SVC 18 -#define ARM_REG_SP_SVC 19 -#define ARM_REG_LR_ABT 20 -#define ARM_REG_SP_ABT 21 -#define ARM_REG_LR_UND 22 -#define ARM_REG_SP_UND 23 -#define ARM_REG_LR_MON 28 -#define ARM_REG_SP_MON 29 -#define ARM_REG_ELR_HYP 30 -#define ARM_REG_SP_HYP 31 - -/* - * R=1 - */ -#define R_EXTEND(x) ((1 << 5) | (x)) -#define ARM_REG_SPSR_FIQ R_EXTEND(ARM_REG_LR_FIQ) -#define ARM_REG_SPSR_IRQ R_EXTEND(ARM_REG_LR_IRQ) -#define ARM_REG_SPSR_SVC R_EXTEND(ARM_REG_LR_SVC) -#define ARM_REG_SPSR_ABT R_EXTEND(ARM_REG_LR_ABT) -#define ARM_REG_SPSR_UND R_EXTEND(ARM_REG_LR_UND) -#define ARM_REG_SPSR_MON R_EXTEND(ARM_REG_LR_MON) -#define ARM_REG_SPSR_HYP R_EXTEND(ARM_REG_ELR_HYP) - -#define ARM_INSTR_MSR_EXT_T1_ENC(rm,rn) \ - (ARM_OP_MSR_EXT_T1 | (MVP_BIT(rm, 5) << 4) | \ - (MVP_BIT(rm, 4) << 20) | (MVP_EXTRACT_FIELD(rm, 0, 4) << 24) | ((rn) << 0)) - -#define ARM_INSTR_MSR_EXT_A1_ENC(cond,rm,rn) \ - (((cond) << 28) | ARM_OP_MSR_EXT_A1 | (MVP_BIT(rm, 5) << 22) | \ - (MVP_BIT(rm, 4) << 8) | (MVP_EXTRACT_FIELD(rm, 0, 4) << 16) | ((rn) << 0)) - -#define ARM_INSTR_MRS_EXT_A1_ENC(cond,rd,rm) \ - (((cond) << 28) | ARM_OP_MRS_EXT_A1 | (MVP_BIT(rm, 5) << 22) | \ - (MVP_BIT(rm, 4) << 8) | (MVP_EXTRACT_FIELD(rm, 0, 4) << 16) | ((rd) << 12)) -/*@}*/ - -/** - * @name ARM MCR/MRC/MCRR instruction decoding. - * @{ - */ -#define ARM_INSTR_COPROC_CR_LEN 4 -#define ARM_INSTR_COPROC_CR_MAX (1 << ARM_INSTR_COPROC_CR_LEN) -#define ARM_INSTR_COPROC_OPCODE_LEN 3 -#define ARM_INSTR_COPROC_OPCODE_MAX (1 << ARM_INSTR_COPROC_OPCODE_LEN) - -#define ARM_INSTR_COPROC_CRM(instr) MVP_EXTRACT_FIELD(instr, 0, 4) -#define ARM_INSTR_COPROC_CRN(instr) MVP_EXTRACT_FIELD(instr, 16, 4) -#define ARM_INSTR_COPROC_OPCODE1(instr) MVP_EXTRACT_FIELD(instr, 21, 3) -#define ARM_INSTR_COPROC_OPCODE2(instr) MVP_EXTRACT_FIELD(instr, 5, 3) -#define ARM_INSTR_COPROC_OPCODE(instr) MVP_EXTRACT_FIELD(instr, 4, 4) -#define ARM_INSTR_COPROC_CPNUM(instr) MVP_EXTRACT_FIELD(instr, 8, 4) -/*@}*/ - -/** - * @name ARM VMRS/VMSR instruction decoding -- See VMRS (B6.1.14) - * and VMSR (B6.1.15) in ARM DDI 0406B. - * @{ - */ -#define ARM_INSTR_IS_VMRS(instr) ((MVP_EXTRACT_FIELD(instr, 0, 12) == 0xa10) && \ - (ARM_INSTR_OPCODE(instr) == 0xef)) - -#define ARM_INSTR_IS_VMSR(instr) ((MVP_EXTRACT_FIELD(instr, 0, 12) == 0xa10) && \ - (ARM_INSTR_OPCODE(instr) == 0xee)) - -#define ARM_INSTR_VMRS_SPECREG(instr) MVP_EXTRACT_FIELD(instr, 16, 4) -#define ARM_INSTR_VMRS_RT(instr) MVP_EXTRACT_FIELD(instr, 12, 4) - -#define ARM_INSTR_VMSR_SPECREG(instr) MVP_EXTRACT_FIELD(instr, 16, 4) -#define ARM_INSTR_VMSR_RT(instr) MVP_EXTRACT_FIELD(instr, 12, 4) -/*@}*/ - -/** - * @name ARM SWP{B} instruction checking. - * @{ - */ -#define ARM_INSTR_IS_SWP(instr) ((instr & 0x0fb00ff0) == 0x01000090) -/*@}*/ - -#endif /// _INSTR_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/lowmemkiller_variant.sh b/arch/arm/mvp/mvpkm/lowmemkiller_variant.sh deleted file mode 100644 index 7721e57..0000000 --- a/arch/arm/mvp/mvpkm/lowmemkiller_variant.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash -# -# Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support -# -# Copyright (C) 2010-2012 VMware, 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. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; see the file COPYING. If not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# @brief Script providing the variant of the low memory killer implementation -# to assist in mvpkm's export of the other_file calculation. - -if [ -z "$1" ] -then - echo "Usage: $0 " - exit 1 -fi - -# We look at the relevant section of the lowmem_shrink function here. This -# pattern is sufficient to distinguish between the known variants without -# introducing too many false positives for new variants. I.e. we can spot the -# lines that matter for the other_file calculation. In some cases the -# lowmemorykiller uses only the other_file calculation instead of max(free, -# file) - in the cases we've seen this is OK with the balloon policy, since the -# free term isn't really significant when we get into low memory states anyway. - -tmp_file="lmk_md5sum_$RANDOM" - -cat $1 | tr -d '\ \t\n\r' > $tmp_file -sed -i -e 's/.*\(intother_file.*other_file<\).*/;\1/' \ - -e 's/[;][^;]*other_file[^;]*/#<#&#>#/g' \ - -e 's/#>#[^#]*//g' $tmp_file - -# Detect MD5SUM executable -HOST_OS=`uname -s` -if [ "$HOST_OS" = "Darwin" ] -then - MD5SUM_EXEC=md5 -else - MD5SUM_EXEC=md5sum -fi - -MD5=`${MD5SUM_EXEC} $tmp_file | cut -f1 -d\ ` - -rm $tmp_file - -case $MD5 in -4af66fafb5e4cbd7b4092e29e071f152|\ -a0f18472eb53e52b38d6f85d4ec66842|\ -590b89af56f57146edffceba60845ad8|\ -fddbb73a58e82ba1966fd862a561c2bd) - #/* - # * This is the same as the non-exported global_reclaimable_pages() when there - # * is no swap. - # */ - #other_file = global_page_state(NR_ACTIVE_FILE) + - # global_page_state(NR_INACTIVE_FILE); - V=1 -;; -943372c447dd868845d71781292eae17|\ -14d0cc4189c1f4fd7818c3393cc8c311) - # other_file = global_page_state(NR_FILE_PAGES); - V=2 -;; -59f3bb678a855acfea2365b7a904bc5b|\ -df96cbb1784869ac7d017dd343e4e8f2) - # other_file = global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM); - V=3 -;; -ed03b69361c2881ed1a031c9b9a24d8a|\ -8639aca416d3014d68548d6cb538405b) - # other_file = global_page_state(NR_FREE_PAGES) + global_page_state(NR_FILE_PAGES); - # (other_free not used, but max(other_free, other_file) = other_file in this - # case. - V=4 -;; -*) - V=0 -;; -esac - -echo "$MD5 $V" diff --git a/arch/arm/mvp/mvpkm/lpae_defs.h b/arch/arm/mvp/mvpkm/lpae_defs.h deleted file mode 100644 index 7729268..0000000 --- a/arch/arm/mvp/mvpkm/lpae_defs.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Large physical address extension definitions. - * - * See ARM PRD03-GENC-008469 11.0. - */ -#ifndef _LPAE_DEFS_H_ -#define _LPAE_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define ARM_LPAE_PT_ORDER 12 - -#define ARM_LPAE_PT_SIZE (1 << ARM_LPAE_PT_ORDER) -#define ARM_LPAE_ENTRY_ORDER 3 -#define ARM_LPAE_PT_ENTRIES_ORDER (ARM_LPAE_PT_ORDER - ARM_LPAE_ENTRY_ORDER) -#define ARM_LPAE_PT_ENTRIES (1 << ARM_LPAE_PT_ENTRIES_ORDER) - -#define ARM_LPAE_L1D_BLOCK_ORDER 30 -#define ARM_LPAE_L2D_BLOCK_ORDER 21 -#define ARM_LPAE_L3D_BLOCK_ORDER 12 - -#define ARM_LPAE_L1D_BLOCK_BITS (40 - ARM_LPAE_L1D_BLOCK_ORDER) -#define ARM_LPAE_L2D_BLOCK_BITS (40 - ARM_LPAE_L2D_BLOCK_ORDER) -#define ARM_LPAE_L3D_BLOCK_BITS (40 - ARM_LPAE_L3D_BLOCK_ORDER) - -/* - * Currently supporting up to 16GB PA spaces. - */ -#define ARM_LPAE_L1PT_INDX(addr) \ - MVP_EXTRACT_FIELD64(addr, ARM_LPAE_L1D_BLOCK_ORDER, 4) -#define ARM_LPAE_L2PT_INDX(addr) \ - MVP_EXTRACT_FIELD64(addr, ARM_LPAE_L2D_BLOCK_ORDER, ARM_LPAE_PT_ENTRIES_ORDER) -#define ARM_LPAE_L3PT_INDX(addr) \ - MVP_EXTRACT_FIELD64(addr, ARM_LPAE_L3D_BLOCK_ORDER, ARM_LPAE_PT_ENTRIES_ORDER) - -#define ARM_LPAE_L1D_BLOCK_BASE_ADDR(base) ((base) << ARM_LPAE_L1D_BLOCK_ORDER) -#define ARM_LPAE_L1D_BLOCK_ADDR_BASE(addr) ((addr) >> ARM_LPAE_L1D_BLOCK_ORDER) -#define ARM_LPAE_L2D_BLOCK_BASE_ADDR(base) ((base) << ARM_LPAE_L2D_BLOCK_ORDER) -#define ARM_LPAE_L2D_BLOCK_ADDR_BASE(addr) ((addr) >> ARM_LPAE_L2D_BLOCK_ORDER) -#define ARM_LPAE_L3D_BLOCK_BASE_ADDR(base) ((base) << ARM_LPAE_L3D_BLOCK_ORDER) -#define ARM_LPAE_L3D_BLOCK_ADDR_BASE(addr) ((addr) >> ARM_LPAE_L3D_BLOCK_ORDER) - -#define ARM_LPAE_TABLE_BASE_ADDR(base) ((base) << ARM_LPAE_PT_ORDER) -#define ARM_LPAE_TABLE_ADDR_BASE(addr) ((addr) >> ARM_LPAE_PT_ORDER) - -#define ARM_LPAE_TYPE_INVALID 0 -#define ARM_LPAE_TYPE_TABLE 3 -#define ARM_LPAE_L1D_TYPE_BLOCK 1 -#define ARM_LPAE_L2D_TYPE_BLOCK 1 -#define ARM_LPAE_L3D_TYPE_BLOCK 3 - -/** - * @name Second stage permission model. - * - * @{ - */ -#define ARM_LPAE_S2_PERM_NONE 0 -#define ARM_LPAE_S2_PERM_RO 1 -#define ARM_LPAE_S2_PERM_WO 2 -#define ARM_LPAE_S2_PERM_RW 3 -/*@}*/ - - -#endif /// ifndef _LPAE_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/lpae_types.h b/arch/arm/mvp/mvpkm/lpae_types.h deleted file mode 100644 index 292f0d9..0000000 --- a/arch/arm/mvp/mvpkm/lpae_types.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Large physical address extension types. - * - * See ARM PRD03-GENC-008469 11.0. - */ -#ifndef _LPAE_TYPES_H_ -#define _LPAE_TYPES_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "lpae_defs.h" - -/** - * @name ARM LPAE page table descriptors. See p7-8 ARM PRD03-GENC-008469 11.0. - * @{ - */ - -#define LOWER_PAGE_ATTRIBUTES_STAGE1 \ - uint64 attrIndx : 3; \ - uint64 ns : 1; \ - uint64 ap : 2; \ - uint64 sh : 2; \ - uint64 af : 1; \ - uint64 ng : 1; - -#define LOWER_PAGE_ATTRIBUTES_STAGE2 \ - uint64 memAttr : 4; \ - uint64 hap : 2; \ - uint64 sh : 2; \ - uint64 af : 1; \ - uint64 sbzL : 1; - -#define UPPER_PAGE_ATTRIBUTES_STAGE1 \ - uint64 contig : 1; \ - uint64 pxn : 1; \ - uint64 xn : 1; \ - uint64 sw : 4; \ - uint64 ignU : 5; - -#define UPPER_PAGE_ATTRIBUTES_STAGE2 \ - uint64 contig : 1; \ - uint64 sbzU : 1; \ - uint64 xn : 1; \ - uint64 sw : 4; \ - uint64 ignU : 5; - - -#define ARM_LPAE_DESC_TYPE(lvl,blen,sbzpad) \ - typedef union { \ - uint64 u; \ - \ - struct { \ - uint64 type : 2;\ - uint64 ign : 62; \ - } x; \ - \ - struct { \ - uint64 type : 2;\ - LOWER_PAGE_ATTRIBUTES_STAGE1 \ - sbzpad \ - uint64 base : blen; \ - uint64 sbz : 12; \ - UPPER_PAGE_ATTRIBUTES_STAGE1 \ - } blockS1; \ - \ - struct { \ - uint64 type : 2;\ - LOWER_PAGE_ATTRIBUTES_STAGE2 \ - sbzpad \ - uint64 base : blen; \ - uint64 sbz : 12; \ - UPPER_PAGE_ATTRIBUTES_STAGE2 \ - } blockS2; \ - \ - struct { \ - uint64 type : 2;\ - uint64 ign0 : 10; \ - uint64 base : 28; \ - uint64 sbz : 12; \ - uint64 ign1 : 7; \ - uint64 pxn : 1; \ - uint64 xn : 1; \ - uint64 ap : 2; \ - uint64 ns : 1; \ - } table; \ - \ - } ARM_LPAE_L##lvl##D; - - -ARM_LPAE_DESC_TYPE(1, ARM_LPAE_L1D_BLOCK_BITS, uint64 sbzP : 18;) -ARM_LPAE_DESC_TYPE(2, ARM_LPAE_L2D_BLOCK_BITS, uint64 sbzP : 9;) -ARM_LPAE_DESC_TYPE(3, ARM_LPAE_L3D_BLOCK_BITS, ) - -/*@}*/ - -#endif /// ifndef _LPAE_TYPES_H_ diff --git a/arch/arm/mvp/mvpkm/mksck.h b/arch/arm/mvp/mvpkm/mksck.h deleted file mode 100644 index aac00f7..0000000 --- a/arch/arm/mvp/mvpkm/mksck.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#ifndef _MKSCK_H -#define _MKSCK_H - -/** - * @file - * - * @brief The monitor-kernel socket interface definitions. - * - * The monitor kernel socket interface was created for (what the name - * says) communications between the monitor and host processes. On the - * monitor side a special API is introduced, see mksck_vmm.h. On the - * host side the API is the standard Berkeley socket interface. Host - * process to host process or monitor to monitor communication is not - * supported. - * - * A generic address consists of two 16 bit fields: the vm id and the - * port id. Both hosts (vmx) and monitors (vmm) get their vm id - * automatically. The host vm id is assigned at the time the host - * process opens the mvpkm file descriptor, while the monitor vm id is - * assigned when the vmx.c:SetupWorldSwitchPage() calls - * Mvpkm_SetupIds(). As a vmx may create multiple monitors to service - * an MP guest, a vmx vm id may be associated with multiple monitor vm - * ids. A monitor id, however, has a single associated vmx host id, - * the id of its canonical vmx. - * - * Sockets on the host get their addresses either by explicit user - * call (the bind command) or implicitly by (issuing a send command - * first). At an explicit bind the user may omit one or both fields by - * providing MKSCK_VMID_UNDEF/MKSCK_PORT_UNDEF respectively. An - * implicit bind behaves as if both fields were omitted in an explicit - * bind. The default value of the vmid field is the vmid computed from - * the thread group id while that of a port is a new number. It is not - * invalid to bind a host process socket with a vm id different from - * the vmid computed from the tgid. - * - * Sockets of the monitor are automatically assigned a vmid, that of their - * monitor, at the time of their creation. The port id can be assigned by the - * user or left to the implementation to assign an unused one (by specifying - * MKSCK_PORT_UNDEF at @ref Mksck_Open). - * - * Host unconnected sockets may receive from any monitor sender, may send to any - * monitor socket. A socket can be connected to a peer address, that enables the - * use of the send command. - * - * One of many special predefined port (both host and monitor) is - * MKSCK_PORT_MASTER. It is used for initialization. - * - * Monitor sockets have to send their peer address explicitly (by - * Mksck_SetPeer()) or implicitly by receiving first. After the peer - * is set, monitor sockets may send or receive only to/from their - * peer. - */ - - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "vmid.h" - -/* - * The interface limits the size of transferable packets. - */ -#define MKSCK_XFER_MAX 1024 - -#define MKSCK_ADDR_UNDEF (uint32)0xffffffff - -#define MKSCK_PORT_UNDEF (uint16)0xffff -#define MKSCK_PORT_MASTER (MKSCK_PORT_UNDEF-1) -#define MKSCK_PORT_HOST_FB (MKSCK_PORT_UNDEF-2) -#define MKSCK_PORT_BALLOON (MKSCK_PORT_UNDEF-3) -#define MKSCK_PORT_HOST_HID (MKSCK_PORT_UNDEF-4) -#define MKSCK_PORT_CHECKPOINT (MKSCK_PORT_UNDEF-5) -#define MKSCK_PORT_COMM_EV (MKSCK_PORT_UNDEF-6) -#define MKSCK_PORT_HIGH (MKSCK_PORT_UNDEF-7) - -#define MKSCK_VMID_UNDEF VMID_UNDEF -#define MKSCK_VMID_HIGH (MKSCK_VMID_UNDEF-1) - -#define MKSCK_DETACH 3 - -typedef uint16 Mksck_Port; -typedef VmId Mksck_VmId; - -/** - * @brief Page descriptor for typed messages. Each page describes a region of - * the machine address space with base mpn and size 2^(12 + order) bytes. - */ -typedef struct { - uint32 mpn : 20; ///< Base MPN of region described by page - uint32 order : 12; ///< Region is 2^(12 + order) bytes. -} Mksck_PageDesc; - -/** - * @brief Typed message template macro. Allows us to avoid having two message - * types, one with page descriptor vector (for VMM), one without (for - * VMX). - * - * @param type C type of uninterpreted component of the message (following the - * page descriptor vector). - * @param pages number of page descriptors in vector. - */ -#define MKSCK_DESC_TYPE(type,pages) \ - struct { \ - type umsg; \ - Mksck_PageDesc page[pages]; \ - } - -/** - * @brief The monitor kernel socket interface address format - */ -typedef union { - uint32 addr; ///< the address - struct { /* The address is decomposed to two shorts */ - Mksck_Port port; ///< port unique within a vmid - Mksck_VmId vmId; ///< unique vmid - }; -} Mksck_Address; - -static inline uint32 -Mksck_AddrInit(Mksck_VmId vmId, Mksck_Port port) -{ - Mksck_Address aa; - aa.vmId = vmId; - aa.port = port; - return aa.addr; -} -#endif diff --git a/arch/arm/mvp/mvpkm/mksck_kernel.c b/arch/arm/mvp/mvpkm/mksck_kernel.c deleted file mode 100644 index 6811a68..0000000 --- a/arch/arm/mvp/mvpkm/mksck_kernel.c +++ /dev/null @@ -1,2589 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The monitor/kernel socket interface kernel extension. - */ - -#define __KERNEL_SYSCALLS__ -#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 "mvp.h" -#include "actions.h" -#include "mvpkm_kernel.h" -#include "mksck_kernel.h" -#include "mksck_sockaddr.h" -#include "mutex_kernel.h" - -void NORETURN FatalError(char const *file, - int line, - FECode feCode, - int bugno, - char const *fmt, - ...) -{ - /* Lock around printing the error details so that the messages from multiple - * threads are not interleaved. */ - static DEFINE_MUTEX(fatalErrorMutex); - mutex_lock(&fatalErrorMutex); - - FATALERROR_COMMON(printk, vprintk, file, line, feCode, bugno, fmt); - - dump_stack(); - - /* done printing */ - mutex_unlock(&fatalErrorMutex); - - /* do_exit below exits the current thread but does not crash the kernel. - * Hence the stack dump will actually be readable from other user threads. - */ - do_exit(1); -} - - -/* - * The project uses a new address family: AF_MKSCK. Optimally this address - * family were accepted with the Linux community and a permanent number - * were assigned. This, however, is a dream only, not even the x86 team - * has been able to pull it off. - * - * Instead we ASSUME that DECnet is dead and re-use it's address family number. - * This is what the x86 world is moving too in the latest versions. - */ - -static struct proto mksckProto = { - .name = "AF_MKSCK", - .owner = THIS_MODULE, - .obj_size = sizeof (struct sock), -}; - -static int MksckCreate(struct net *net, - struct socket *sock, - int protocol, - int kern); - -static struct net_proto_family mksckFamilyOps = { - .family = AF_MKSCK, - .owner = THIS_MODULE, - .create = MksckCreate, -}; - -static int MksckFault(struct vm_area_struct *vma, struct vm_fault *vmf); - - -/** - * @brief Linux vma operations for receive windows established via Mksck - * mmap. - */ -static struct vm_operations_struct mksckVMOps = { - .fault = MksckFault -}; - -/* - * List of hosts and guests we know about. - */ -static spinlock_t mksckPageListLock; -static MksckPage *mksckPages[MKSCK_MAX_SHARES]; - -/* - * The following functions form the AF_MKSCK DGRAM operations. - */ -static int MksckRelease(struct socket *sock); -static int MksckBacklogRcv(struct sock *sk, struct sk_buff *skb); -static void MksckSkDestruct(struct sock *sk); -static int MksckBind(struct socket *sock, - struct sockaddr *addr, - int addrLen); -static int MksckBindGeneric(struct sock *sk, - Mksck_Address addr); -static int MksckDgramRecvMsg(struct kiocb *kiocb, - struct socket *sock, - struct msghdr *msg, - size_t len, - int flags); -static int MksckDgramSendMsg(struct kiocb *kiocb, - struct socket *sock, - struct msghdr *msg, - size_t len); -static int MksckGetName(struct socket *sock, - struct sockaddr *addr, - int *addrLen, - int peer); -static unsigned int MksckPoll(struct file *filp, - struct socket *sock, - poll_table *wait); -static int MksckDgramConnect(struct socket *sock, - struct sockaddr *addr, - int addrLen, - int flags); -static int MksckMMap(struct file *file, - struct socket *sock, - struct vm_area_struct *vma); - -static void MksckPageRelease(MksckPage *mksckPage); - -static struct proto_ops mksckDgramOps = { - .family = AF_MKSCK, - .owner = THIS_MODULE, - .release = MksckRelease, - .bind = MksckBind, - .connect = MksckDgramConnect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = MksckGetName, - .poll = MksckPoll, - .ioctl = sock_no_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, /* MksckShutdown, */ - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .sendmsg = MksckDgramSendMsg, - .recvmsg = MksckDgramRecvMsg, - .mmap = MksckMMap, - .sendpage = sock_no_sendpage, -}; - - -/** - * @brief Initialize the MKSCK protocol - * - * @return 0 on success, -errno on failure - */ -int -Mksck_Init(void) -{ - int err; - - spin_lock_init(&mksckPageListLock); - - /* - * Create a slab to allocate socket structs from. - */ - err = proto_register(&mksckProto, 1); - if (err != 0) { - printk(KERN_INFO - "Mksck_Init: Cannot register MKSCK protocol, errno = %d.\n", err); - return err; - } - - /* - * Register the socket family - */ - err = sock_register(&mksckFamilyOps); - if (err < 0) { - printk(KERN_INFO - "Mksck_Init: Could not register address family AF_MKSCK" - " (errno = %d).\n", err); - return err; - } - - return 0; -} - - -/** - * @brief De-register the MKSCK protocol - */ -void -Mksck_Exit(void) -{ - sock_unregister(mksckFamilyOps.family); - proto_unregister(&mksckProto); -} - - -/** - * @brief Create a new MKSCK socket - * - * @param net network namespace (2.6.24 or above) - * @param sock user socket structure - * @param protocol protocol to be used - * @param kern called from kernel mode - * - * @return 0 on success, -errno on failure - */ -static int -MksckCreate(struct net *net, - struct socket *sock, - int protocol, - int kern) -{ - struct sock *sk; - uid_t currentUid = current_euid(); - - if (!(currentUid == 0 || - currentUid == Mvpkm_vmwareUid)) { - printk(KERN_WARNING - "MksckCreate: rejected from process %s tgid=%d, pid=%d euid:%d.\n", - current->comm, - task_tgid_vnr(current), - task_pid_vnr(current), - currentUid); - return -EPERM; - } - - if (!sock) { - return -EINVAL; - } - - if (protocol) { - return -EPROTONOSUPPORT; - } - - switch (sock->type) { - case SOCK_DGRAM: { - sock->ops = &mksckDgramOps; - break; - } - default: { - return -ESOCKTNOSUPPORT; - } - } - - sock->state = SS_UNCONNECTED; - - /* - * Most recently (in 2.6.24), sk_alloc() was changed to expect the - * network namespace, and the option to zero the sock was dropped. - */ - sk = sk_alloc(net, mksckFamilyOps.family, GFP_KERNEL, &mksckProto); - - if (!sk) { - return -ENOMEM; - } - - sock_init_data(sock, sk); - - sk->sk_type = SOCK_DGRAM; - sk->sk_destruct = MksckSkDestruct; - sk->sk_backlog_rcv = MksckBacklogRcv; - - /* - * On socket lock... - * - * A bound socket will have an associated private area, the Mksck - * structure part of MksckPage. That area is pointed to by - * sk->sk_protinfo. In addition, a connected socket will have the - * peer field in its associated area set to point to the associated - * private area of the peer socket. A mechanism is needed to ensure - * that these private areas area not freed while they are being - * accessed within the scope of a function. A simple lock would not - * suffice as the interface functions (like MksckDgramRecvMsg()) - * may block. Hence a reference count mechanism is employed. When - * the mentioned references (sk->sk_protinfo and mksck->peer) to - * the respective private areas are set a refcount is incremented, - * and decremented when the references are deleted. - * - * The refcounts of areas pointed to by sk->sk_protinfo and - * mksck->peer will be decremented under the lock of the socket. - * Hence these private areas cannot disappear as long as the socket - * lock is held. - * - * The interface functions will have one of the following - * structures: - * - * simpleFn(sk) - * { - * lock_sock(sk); - * if ((mksck = sk->sk_protinfo)) { - * - * } - * release_sock(sk); - * } - * - * complexFn(sk) - * { - * lock_sock(sk); - * if ((mksck = sk->sk_protinfo)) { - * IncRefc(mksck); - * } - * release_sock(sk); - * - * if (mksck) { - * - * DecRefc(mksck); - * } - * } - */ - sk->sk_protinfo = NULL; - sock_reset_flag(sk, SOCK_DONE); - - return 0; -} - - -/** - * @brief Delete a MKSCK socket - * - * @param sock user socket structure - * - * @return 0 on success, -errno on failure - */ -static int -MksckRelease(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (sk) { - lock_sock(sk); - sock_orphan(sk); - release_sock(sk); - sock_put(sk); - } - - sock->sk = NULL; - sock->state = SS_FREE; - - return 0; -} - - -static int -MksckBacklogRcv(struct sock *sk, struct sk_buff *skb) -{ - /* - * We should never get these as we never queue an skb. - */ - printk("MksckBacklogRcv: should never get here\n"); - return -EIO; -} - - -/** - * @brief Callback at socket destruction - * - * @param sk pointer to kernel socket structure - */ -static void -MksckSkDestruct(struct sock *sk) -{ - Mksck *mksck; - - lock_sock(sk); - mksck = sk->sk_protinfo; - - if (mksck != NULL) { - sk->sk_protinfo = NULL; - Mksck_CloseCommon(mksck); - } - - if (sk->sk_user_data != NULL) { - sock_kfree_s(sk, sk->sk_user_data, sizeof(int)); - sk->sk_user_data = NULL; - } - - release_sock(sk); -} - - -/** - * @brief Set the local address of a MKSCK socket - * - * @param sk kernel socket structure - * @param addr the new address of the socket - * - * @return 0 on success, -errno on failure - * - * If addr.port is undefined a new random port is assigned. - * If addr.vmId is undefined then the vmId computed from the tgid is used. - * Hence the vmId of a socket does not determine the host all the time. - * - * Assumed that the socket is locked. - * This function is called by explicit set (MksckBind) and implicit (Send). - */ -static int -MksckBindGeneric(struct sock *sk, - Mksck_Address addr) -{ - int err; - Mksck *mksck; - MksckPage *mksckPage; - - if (sk->sk_protinfo != NULL) { - return -EISCONN; - } - - /* - * Locate the page for the given host and increment its reference - * count so it can't get freed off while we are working on it. - */ - if (addr.vmId == MKSCK_VMID_UNDEF) { - mksckPage = MksckPage_GetFromTgidIncRefc(); - } else { - printk(KERN_WARNING "MksckBind: host bind called on vmid 0x%X\n", addr.vmId); - mksckPage = MksckPage_GetFromVmIdIncRefc(addr.vmId); - } - - if (mksckPage == NULL) { - printk(KERN_INFO "MksckBind: no mksckPage for vm 0x%X\n", addr.vmId); - return -ENETUNREACH; - } - addr.vmId = mksckPage->vmId; - - /* - * Before we can find an unused socket port on the page we have to - * lock the page for exclusive access so another thread can't - * allocate the same port. - */ - err = Mutex_Lock(&mksckPage->mutex, MutexModeEX); - if (err < 0) { - goto outDec; - } - - addr.port = MksckPage_GetFreePort(mksckPage, addr.port); - if (addr.port == MKSCK_PORT_UNDEF) { - err = -EINVAL; - goto outUnlockDec; - } - - /* - * At this point we have the mksckPage locked for exclusive access - * and its reference count incremented. Also, addr is completely - * filled in with vmId and port that we want to bind. - * - * Find an available mksck struct on the shared page and initialize - * it. - */ - mksck = MksckPage_AllocSocket(mksckPage, addr); - if (mksck == NULL) { - err = -EMFILE; - goto outUnlockDec; - } - - /* - * Stable, release mutex. Leave mksckPage->refCount incremented so - * mksckPage can't be freed until socket is closed. - */ - Mutex_Unlock(&mksckPage->mutex, MutexModeEX); - - /* - * This is why we start mksck->refCount at 1. When sk_protinfo gets - * cleared, we decrement mksck->refCount. - */ - sk->sk_protinfo = mksck; - - PRINTK(KERN_DEBUG "MksckBind: socket bound to %08X\n", mksck->addr.addr); - - return 0; - -outUnlockDec: - Mutex_Unlock(&mksckPage->mutex, MutexModeEX); -outDec: - MksckPage_DecRefc(mksckPage); - return err; -} - - -/** - * @brief Test if the socket is already bound to a local address and, - * if not, bind it to an unused address. - * - * @param sk kernel socket structure - * @return 0 on success, -errno on failure - * - * Assumed that the socket is locked. - */ -static inline int -MksckTryBind(struct sock *sk) -{ - int err = 0; - - if (!sk->sk_protinfo) { - static const Mksck_Address addr = { .addr = MKSCK_ADDR_UNDEF }; - err = MksckBindGeneric(sk, addr); - } - return err; -} - - - -/** - * @brief Set the address of a MKSCK socket (user call) - * - * @param sock user socket structure - * @param addr the new address of the socket - * @param addrLen length of the address - * - * @return 0 on success, -errno on failure - */ -static int -MksckBind(struct socket *sock, - struct sockaddr *addr, - int addrLen) -{ - int err; - struct sock *sk = sock->sk; - struct sockaddr_mk *addrMk = (struct sockaddr_mk *)addr; - - if (addrLen != sizeof *addrMk) { - return -EINVAL; - } - if (addrMk->mk_family != AF_MKSCK) { - return -EAFNOSUPPORT; - } - - /* - * Obtain the socket lock and call the generic Bind function. - */ - lock_sock(sk); - err = MksckBindGeneric(sk, addrMk->mk_addr); - release_sock(sk); - - return err; -} - -/** - * @brief Lock the peer socket by locating it, incrementing its refc - * @param addr the address of the peer socket - * @param[out] peerMksckR set to the locked peer socket pointer - * upon successful lookup - * @return 0 on success, -errno on failure - */ -static int -LockPeer(Mksck_Address addr, Mksck **peerMksckR) -{ - int err = 0; - MksckPage *peerMksckPage = MksckPage_GetFromVmIdIncRefc(addr.vmId); - Mksck *peerMksck; - - /* - * Find corresponding destination shared page and increment its - * reference count so it can't be freed while we are sending to the - * socket. Make sure that the address is indeed an address of a - * monitor/guest socket. - */ - if (peerMksckPage == NULL) { - printk(KERN_INFO "LockPeer: vmId %x is not in use!\n", addr.vmId); - return -ENETUNREACH; - } - if (!peerMksckPage->isGuest) { - MksckPage_DecRefc(peerMksckPage); - printk(KERN_INFO "LockPeer: vmId %x does not belong to a guest!\n", - addr.vmId); - return -ENETUNREACH; - } - - - err = Mutex_Lock(&peerMksckPage->mutex, MutexModeSH); - if (err < 0) { - MksckPage_DecRefc(peerMksckPage); - return err; - } - - /* - * Find corresponding destination socket on that shared page and - * increment its reference count so it can't be freed while we are - * trying to send to it. - */ - peerMksck = MksckPage_GetFromAddr(peerMksckPage, addr); - - if (peerMksck) { - ATOMIC_ADDV(peerMksck->refCount, 1); - *peerMksckR = peerMksck; - } else { - printk(KERN_INFO "LockPeer: addr %x is not a defined socket!\n", - addr.addr); - err = -ENETUNREACH; - } - - Mutex_Unlock(&peerMksckPage->mutex, MutexModeSH); - MksckPage_DecRefc(peerMksckPage); - return err; -} - -/** - * @brief Set the peer address of a MKSCK socket - * - * @param sock user socket structure - * @param addr the new address of the socket - * @param addrLen length of the address - * @param flags flags - * - * @return 0 on success, -errno on failure - */ -static int -MksckDgramConnect(struct socket *sock, - struct sockaddr *addr, - int addrLen, - int flags) -{ - struct sock *sk = sock->sk; - Mksck *mksck; - struct sockaddr_mk *peerAddrMk = (struct sockaddr_mk *)addr; - int err = 0; - - if (addrLen != sizeof *peerAddrMk) { - printk(KERN_INFO "MksckConnect: wrong address length!\n"); - return -EINVAL; - } - if (peerAddrMk->mk_family != AF_MKSCK) { - printk(KERN_INFO "MksckConnect: wrong address family!\n"); - return -EAFNOSUPPORT; - } - - lock_sock(sk); - - if ((err = MksckTryBind(sk))) { - goto releaseSock; - } - mksck = sk->sk_protinfo; - - /* - * First severe any past peer connections - */ - Mksck_DisconnectPeer(mksck); - sock->state = SS_UNCONNECTED; - - /* - * Then build new connections ... - */ - if (peerAddrMk->mk_addr.addr != MKSCK_ADDR_UNDEF) { - sock->state = SS_CONNECTED; - mksck->peerAddr = peerAddrMk->mk_addr; - err = LockPeer(mksck->peerAddr, &mksck->peer); - PRINTK(KERN_DEBUG "MksckConnect: socket %x is connected to %x!\n", - mksck->addr.addr, mksck->peerAddr.addr); - } - -releaseSock: - release_sock(sk); - - return err; -} - - -/** - * @brief returns the address of a MKSCK socket/peer address - * - * @param sock user socket structure - * @param addr the new address of the socket - * @param addrLen length of the address - * @param peer 1 if the peer address is sought - * - * @return 0 on success, -errno on failure - */ -static int -MksckGetName(struct socket *sock, - struct sockaddr *addr, - int *addrLen, - int peer) -{ - int err; - Mksck *mksck; - struct sock *sk = sock->sk; - - // MAX_SOCK_ADDR is size of *addr, Linux doesn't export it! - // ASSERT_ON_COMPILE(sizeof (struct sockaddr_mk) <= MAX_SOCK_ADDR); - - lock_sock(sk); - mksck = sk->sk_protinfo; - - if (mksck == NULL) { - if (peer) { - err = -ENOTCONN; - } else { - ((struct sockaddr_mk *)addr)->mk_family = AF_MKSCK; - ((struct sockaddr_mk *)addr)->mk_addr.addr = MKSCK_ADDR_UNDEF; - *addrLen = sizeof (struct sockaddr_mk); - err = 0; - } - } else if (!peer) { - ((struct sockaddr_mk *)addr)->mk_family = AF_MKSCK; - ((struct sockaddr_mk *)addr)->mk_addr = mksck->addr; - *addrLen = sizeof (struct sockaddr_mk); - err = 0; - } else if (mksck->peerAddr.addr == MKSCK_ADDR_UNDEF) { - err = -ENOTCONN; - } else { - ((struct sockaddr_mk *)addr)->mk_family = AF_MKSCK; - ((struct sockaddr_mk *)addr)->mk_addr = mksck->peerAddr; - *addrLen = sizeof (struct sockaddr_mk); - err = 0; - } - - release_sock(sk); - - return err; -} - - -/** - * @brief VMX polling a receipted packet from VMM. - * - * @param filp kernel file pointer to poll for - * @param sock user socket structure - * @param wait kernel polling table where to poll if not null - * - * @return poll mask state given from socket state. - */ -static unsigned int MksckPoll(struct file *filp, - struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - unsigned int mask = 0; - Mksck *mksck = NULL; - uint32 read; - int err; - - lock_sock(sk); - if ((err = MksckTryBind(sk))) { - release_sock(sk); - return err; - } - mksck = sk->sk_protinfo; - - /* - * To avoid mksck disappearing right after the release_sock the - * refcount needs to be incremented. For more details read the - * block comment on locking in MksckCreate. - */ - ATOMIC_ADDV(mksck->refCount, 1); - release_sock(sk); - - /* - * Wait to make sure this is the only thread trying to access socket. - */ - if ((err = Mutex_Lock(&mksck->mutex, MutexModeEX)) < 0) { - /* we might get in this situation if we are signaled - (select() may handle this, so leave) */ - PRINTK(KERN_INFO "MksckPoll: try to abort\n"); - return mask; - } - - /* - * See if packet in ring. - */ - read = mksck->read; - if (read != mksck->write) { - mask |= POLLIN | POLLRDNORM; /* readable, socket is unlocked */ - /* Note that if we are implementing support for POLLOUT, we SHOULD - change this Mutex_Unlock by Mutex_UnlPoll, because there is no - obvious knowledge about the sleepy reason that is intended by user */ - Mutex_Unlock(&mksck->mutex, MutexModeEX); - } else { - Mutex_UnlPoll(&mksck->mutex, MutexModeEX, MKSCK_CVAR_FILL, filp, wait); - } - - /* - * Note that locking rules differ a little inside MksckPoll, since we are - * not only given a pointer to the struct socket but also a pointer to a - * struct file. This means that during the whole operation of this function - * and during any pending wait (registered with poll_wait()), the file itself - * is reference counted up, and we should rely on that 'upper' reference - * counting to prevent from tearing the Mksck down. That holds true since one - * never re-bind sockets ! - */ - Mksck_DecRefc(mksck); - return mask; -} - -/** - * @brief Manage a set of Mksck_PageDesc from a message or a stored array. - * - * @param pd set of Mksck_PageDesc - * @param pages Mksck_PageDesc pages count for this management operation - * @param incr ternary used to indicate if we want to reference (+1), or - * dereference (-1), or count (0) 4k pages - * - * @return length of bytes processed. - */ -static size_t -MksckPageDescManage(Mksck_PageDesc *pd, - uint32 pages, - int incr) -{ - size_t payloadLen = 0; - uint32 i; - - for (i = 0; i < pages && pd[i].mpn != INVALID_MPN; ++i) { - uint32 j; - - for (j = 0; j < 1 << pd[i].order; ++j) { - struct page *page; - MPN currMPN = pd[i].mpn + j; - - /* - * The monitor tried to send an invalid MPN, bad. - */ - if (!pfn_valid(currMPN)) { - printk("MksckPageDescManage: Invalid MPN %x\n", currMPN); - } else { - page = pfn_to_page(currMPN); - - if (incr == +1) { - get_page(page); - } - if (incr == -1) { - put_page(page); - } - } - - payloadLen += PAGE_SIZE; - } - } - - return payloadLen; -} - -/** - * @brief Management values to be used as third parameter of MksckPageDescManage - */ -#define MANAGE_INCREMENT +1 -#define MANAGE_DECREMENT -1 -#define MANAGE_COUNT 0 - - -/** - * @brief Map a set of Mksck_PageDesc from a message or a stored array. - * - * @param pd set of Mksck_PageDesc - * @param pages pages count for this mapping - * @param iov vectored user virtual addresses of the recv commands - * @param iovCount size for iov parameter - * @param vma virtual memory area used for the mapping, note that - * this is mandatorily required MksckPageDescMap is used - * on an indirect PageDesc context (i.e whenever iov is - * not computed by the kernel but by ourselves). - * - * Since find_vma() and vm_insert_page() are used, this function must - * be called with current's mmap_sem locked, or inside an MMap operation. - * - * @return length of bytes mapped. - */ -static size_t -MksckPageDescMap(Mksck_PageDesc *pd, - uint32 pages, - struct iovec *iov, - int iovCount, - struct vm_area_struct *vma) -{ - size_t payloadLen = 0; - uint32 i; - - for (i = 0; i < pages && pd[i].mpn != INVALID_MPN; ++i) { - uint32 j; - - for (j = 0; j < 1 << pd[i].order; ++j) { - HUVA huva = 0; - struct page *page; - MPN currMPN = pd[i].mpn + j; - - while (iovCount > 0 && iov->iov_len == 0) { - iovCount--; - iov++; - } - - if (iovCount == 0) { - printk("MksckPageDescMap: Invalid iov length\n"); - goto map_done; - } - - huva = (HUVA)iov->iov_base; - - /* - * iovecs for receiving the typed component of the message should - * have page aligned base and size sufficient for page descriptor's - * mappings. - */ - if (huva & (PAGE_SIZE - 1) || iov->iov_len < PAGE_SIZE) { - printk("MksckPageDescMap: Invalid huva %x or iov_len %d\n", - huva, - iov->iov_len); - goto map_done; - } - - /* - * Might be in a new vma... - */ - if (vma == NULL || huva < vma->vm_start || huva >= vma->vm_end) { - vma = find_vma(current->mm, huva); - - /* - * Couldn't find a matching vma for huva. - */ - if (vma == NULL || - huva < vma->vm_start || - vma->vm_ops != &mksckVMOps) { - printk("MksckPageDescMap: Invalid vma\n"); - goto map_done; - } - } - - /* - * The monitor tried to send an invalid MPN, bad. - */ - if (!pfn_valid(currMPN)) { - printk("MksckPageDescMap: Invalid MPN %x\n", currMPN); - } else { - int rc; - - page = pfn_to_page(currMPN); - - /* - * Map into the receive window. - */ - rc = vm_insert_page(vma, huva, page); - if (rc) { - printk("MksckPageDescMap: Failed to insert %x at %x, error %d\n", - currMPN, - huva, - rc); - goto map_done; - } - - ASSERT(iov->iov_len >= PAGE_SIZE); - iov->iov_base += PAGE_SIZE; - iov->iov_len -= PAGE_SIZE; - } - - payloadLen += PAGE_SIZE; - } - } - -map_done: - return payloadLen; -} - - -/** - * @brief Check if the provided MsgHdr has still room for a receive operation. - * - * @param msg user buffer - * @return 1 if MsgHdr has IO space room in order to receive a mapping, 0 otherwise. - */ -static int -MsgHdrHasAvailableRoom(struct msghdr *msg) -{ - struct iovec *vec = msg->msg_iov; - uint32 count = msg->msg_iovlen; - - while (count > 0 && vec->iov_len == 0) { - count--; - vec++; - } - - return (count != 0); -} - - -/** - * Whenever a typed message is received from the monitor, we may choose to store - * all the page descriptor content in a linked state of descriptors, through the - * following information context - */ -typedef struct MksckPageDescInfo { - struct MksckPageDescInfo *next; - uint32 flags; - uint32 pages; - uint32 mapCounts; - Mksck_PageDesc descs[0]; -} MksckPageDescInfo; - -static void MksckPageDescSkDestruct(struct sock *sk); -static int MksckPageDescMMap(struct file *file, - struct socket *sock, - struct vm_area_struct *vma); -static int MksckPageDescIoctl(struct socket *sock, - unsigned int cmd, - unsigned long arg); - -/** - * @brief Delete a page descriptor container socket - * - * @param sock user socket structure - * @return 0 on success, -errno on failure - */ -static int -MksckPageDescRelease(struct socket *sock) -{ - /* This is generic socket release */ - struct sock *sk = sock->sk; - - if (sk) { - lock_sock(sk); - sock_orphan(sk); - release_sock(sk); - sock_put(sk); - } - - sock->sk = NULL; - sock->state = SS_FREE; - - return 0; -} - - -/** - * Whenever a typed message is received from the monitor, we may choose to store - * all the page descriptor content for a future mapping. One shall put a context - * usable by host userland, that means trough a file descriptor, and as a secure - * implementation we choose to define a strict set of operations that are used - * only for that purpose. This set of operation is reduced to leaving the - * default "PageDesc(s) accumulating" mode (inside ioctl), mapping the context, - * and generic socket destruction. - */ -static struct proto_ops mksckPageDescOps = { - .family = AF_MKSCK, - .owner = THIS_MODULE, - .release = MksckPageDescRelease, - .bind = sock_no_bind, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = sock_no_poll, - .ioctl = MksckPageDescIoctl, - .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 = MksckPageDescMMap, - .sendpage = sock_no_sendpage, -}; - - -/** - * @brief Create or accumulate to a PageDesc context, backed as a descriptor. - * - * @param sock user socket structure - * @param msg user buffer to receive the file descriptor as ancillary data - * @param pd source descriptor part of a message - * @param pages pages count for this mapping - * - * @return error if negative, 0 otherwise - * - */ -static int -MksckPageDescToFd(struct socket *sock, - struct msghdr *msg, - Mksck_PageDesc *pd, - uint32 pages) -{ - int retval; - int newfd; - struct socket *newsock; - struct sock *newsk; - struct sock *sk = sock->sk; - MksckPageDescInfo **pmpdi, *mpdi; - lock_sock(sk); - - /* - * Relation between any mk socket and the PageDesc context is as follow: - * - * From the mk socket to the PageDesc context: - * - sk->sk_user_data is a WEAK LINK, containing only a file descriptor - * numerical value such that accumulating is keyed on it. - * - * From the PageDesc context to the mk socket: - * - sk->sk_protinfo contains a MksckPageDescInfo struct. - * - sk->sk_user_data is a pointer REF-COUNTED sock_hold() LINK, also it is - * rarely dereferenced but usually used to check that the - * right socket pair is used. Full dereferencing is used - * only to break the described links. - */ - if (sk->sk_user_data) { - MksckPageDescInfo *mpdi2; - - /* continue any previous on-going mapping, i.e accumulate */ - newfd = *((int *)sk->sk_user_data); - newsock = sockfd_lookup(newfd, &retval); // promote the weak link - if (!newsock) { - retval = -EINVAL; - goto endProcessingReleaseSock; - } - - newsk = newsock->sk; - lock_sock(newsk); - sockfd_put(newsock); - - if (((struct sock *)newsk->sk_user_data) != sk) { - /* One way of going into this situation would be for userland to dup - the file descriptor just received, close the original number, and - open a new mk socket in the very same spot. The userland code have - a lot of way of interacting with the kernel without this driver - code to be notified. */ - retval = -EINVAL; - release_sock(newsk); - goto endProcessingReleaseSock; - } - - mpdi = sock_kmalloc(newsk, sizeof(MksckPageDescInfo) + - pages*sizeof(Mksck_PageDesc), GFP_KERNEL); - if (IS_ERR(mpdi)) { - retval = PTR_ERR(mpdi); - release_sock(newsk); - goto endProcessingReleaseSock; - } - - /* There is no mandatory needs for us to notify userland from - the progress in "appending" to the file descriptor, but it - would feel strange if the userland would have no mean to - tell if the received message was just not thrown away. So, in - order to be consistent one fill the ancillary message while - "creating" and "appending to" file descriptors. */ - retval = put_cmsg(msg, SOL_DECNET, 0, sizeof(int), &newfd); - if (retval < 0) { - goto endProcessingKFreeReleaseSock; - } - - release_sock(sk); - - mpdi2 = (MksckPageDescInfo *)newsk->sk_protinfo; - while (mpdi2->next) { - mpdi2 = mpdi2->next; - } - pmpdi = &(mpdi2->next); - - } else { - /* Create a new socket, new context and a new file descriptor. */ - retval = sock_create(sk->sk_family, sock->type, 0, &newsock); - if (retval < 0) { - goto endProcessingReleaseSock; - } - - newsk = newsock->sk; - lock_sock(newsk); - newsk->sk_destruct = &MksckPageDescSkDestruct; - newsk->sk_user_data = sk; - sock_hold(sk); // keeps a reference to parent mk socket - newsock->ops = &mksckPageDescOps; - - mpdi = sock_kmalloc(newsk, sizeof(MksckPageDescInfo) + - pages*sizeof(Mksck_PageDesc), GFP_KERNEL); - if (IS_ERR(mpdi)) { - retval = PTR_ERR(mpdi); - goto endProcessingFreeNewSock; - } - - sk->sk_user_data = sock_kmalloc(sk, sizeof(int), GFP_KERNEL); - if (IS_ERR(sk->sk_user_data)) { - retval = PTR_ERR(sk->sk_user_data); - sk->sk_user_data = NULL; - goto endProcessingKFreeAndNewSock; - } - - /* mapping to a file descriptor may fail if a thread is closing - in parallel of sock_map_fd/sock_alloc_fd, or kernel memory is full */ - newfd = sock_map_fd(newsock, O_CLOEXEC); - if (newfd < 0) { - retval = newfd; - sock_kfree_s(sk, sk->sk_user_data, sizeof(int)); - sk->sk_user_data = NULL; - goto endProcessingKFreeAndNewSock; - } - - /* notify userland from a new file descriptor, alike AF_UNIX ancillary */ - retval = put_cmsg(msg, SOL_DECNET, 0, sizeof(int), &newfd); - if (retval < 0) { - sock_kfree_s(sk, sk->sk_user_data, sizeof(int)); - sk->sk_user_data = NULL; - sock_kfree_s(newsk, mpdi, sizeof(MksckPageDescInfo) + - mpdi->pages*sizeof(Mksck_PageDesc)); - release_sock(newsk); - sockfd_put(newsock); - sock_release(newsock); - put_unused_fd(newfd); - goto endProcessingReleaseSock; - } - - *(int*)sk->sk_user_data = newfd; - release_sock(sk); - pmpdi = (MksckPageDescInfo **)(&(newsk->sk_protinfo)); - } - - mpdi->next = NULL; - mpdi->flags = 0; - mpdi->mapCounts = 0; - mpdi->pages = pages; - memcpy(mpdi->descs, pd, pages*sizeof(Mksck_PageDesc)); - - *pmpdi = mpdi; // link - release_sock(newsk); - - /* increment all reference counters for the pages */ - MksckPageDescManage(pd, pages, MANAGE_INCREMENT); - return 0; - -endProcessingKFreeAndNewSock: - sock_kfree_s(newsk, mpdi, sizeof(MksckPageDescInfo) + - mpdi->pages*sizeof(Mksck_PageDesc)); -endProcessingFreeNewSock: - release_sock(newsk); - sock_release(newsock); - release_sock(sk); - return retval; - -endProcessingKFreeReleaseSock: - sock_kfree_s(newsk, mpdi, sizeof(MksckPageDescInfo) + - mpdi->pages*sizeof(Mksck_PageDesc)); - release_sock(newsk); -endProcessingReleaseSock: - release_sock(sk); - return retval; -} - -/** - * @brief Callback at socket destruction - * - * @param sk pointer to kernel socket structure - */ -static void -MksckPageDescSkDestruct(struct sock *sk) -{ - struct sock *mkSk = NULL; - MksckPageDescInfo *mpdi; - lock_sock(sk); - mpdi = sk->sk_protinfo; - while (mpdi) { - MksckPageDescInfo *next = mpdi->next; - MksckPageDescManage(mpdi->descs, mpdi->pages, - MANAGE_DECREMENT); - sock_kfree_s(sk, mpdi, sizeof(MksckPageDescInfo) + - mpdi->pages*sizeof(Mksck_PageDesc)); - mpdi = next; - } - if (sk->sk_user_data) { - mkSk = (struct sock *)sk->sk_user_data; - sk->sk_user_data = NULL; - } - sk->sk_protinfo = NULL; - release_sock(sk); - /* clean the monki socket that we are holding */ - if (mkSk) { - lock_sock(mkSk); - sock_kfree_s(mkSk, mkSk->sk_user_data, sizeof(int)); - mkSk->sk_user_data = NULL; - release_sock(mkSk); - sock_put(mkSk); // revert of sock_hold() - } -} - -/** - * @brief The mmap operation of the PageDesc context file descriptor. - * - * The mmap command is used to mmap any detached (i.e. no more accumulating) - * PageDesc context, full of the content from its parent communication mk - * socket. Mapping may be done a specified number of times, so that the - * PageDesc context could become useless (as a security restriction). - * - * Also note that mapping from an offset different from zero is considered - * as a userland invalid operation. - * - * @param file user file structure - * @param sock user socket structure - * @param vma virtual memory area structure - * - * @return error code, 0 on success - */ -static int -MksckPageDescMMap(struct file *file, - struct socket *sock, - struct vm_area_struct *vma) -{ - struct sock *sk = sock->sk; - MksckPageDescInfo *mpdi; - struct iovec iov; - unsigned long vm_flags; - int freed = 0; - - iov.iov_base = (void*)vma->vm_start; - iov.iov_len = vma->vm_end - vma->vm_start; - - lock_sock(sk); - mpdi = sk->sk_protinfo; - - // vma->vm_pgoff is checked, since offsetting the map is not supported - if (!mpdi || sk->sk_user_data || vma->vm_pgoff) { - release_sock(sk); - printk(KERN_INFO "MMAP failed for virt %lx size %lx\n", - vma->vm_start, vma->vm_end - vma->vm_start); - return -EINVAL; - } - - vm_flags = mpdi->flags; - if ((vma->vm_flags & ~vm_flags) & (VM_READ|VM_WRITE)) { - release_sock(sk); - return -EACCES; - } - - while (mpdi) { - MksckPageDescInfo *next = mpdi->next; - MksckPageDescMap(mpdi->descs, mpdi->pages, &iov, 1, vma); - if (mpdi->mapCounts && !--mpdi->mapCounts) { - MksckPageDescManage(mpdi->descs, mpdi->pages, - MANAGE_DECREMENT); - sock_kfree_s(sk, mpdi, sizeof(MksckPageDescInfo) + - mpdi->pages*sizeof(Mksck_PageDesc)); - freed = 1; - } - mpdi = next; - } - - if (freed) { - sk->sk_protinfo = NULL; - } - vma->vm_ops = &mksckVMOps; - release_sock(sk); - return 0; -} - -/** - * @brief The ioctl operation of the PageDesc context file descriptor. - * - * The ioctl MKSCK_DETACH command is used to detach the PageDesc context - * from its parent communication mk socket. Once done, the context - * is able to remap the transferred PageDesc(s) of typed messages accumulated - * into the context. - * - * @param sock user socket structure - * @param cmd select which cmd function needs to be performed - * @param arg argument for command - * - * @return error code, 0 on success - */ -static int -MksckPageDescIoctl(struct socket *sock, - unsigned int cmd, - unsigned long arg) -{ - struct sock *monkiSk = NULL; - struct sock *sk = sock->sk; - MksckPageDescInfo *mpdi; - int retval = 0; - - switch (cmd) { - /** - * ioctl MKSCK_DETACH (in and out): - * Detach, compute size and define allowed protection access rights - * - * [in]: unsigned long flags, similar to prot argument of mmap() - * unsigned long number of available further mappings - * with 0 meaning unlimited number of mappings - * [out]: unsigned long size of the available mappable area - */ - case MKSCK_DETACH: { - unsigned long ul[2]; - lock_sock(sk); - mpdi = sk->sk_protinfo; - // read unsigned long argument that contains the mmap alike flags - if (copy_from_user(ul, (void *)arg, sizeof ul)) { - retval = -EFAULT; - // check that the file descriptor has a parent and some context there - } else if (!mpdi || !sk->sk_user_data) { - retval = -EINVAL; - } else { - /* compute mapping protection bits from argument and size of the - * mapping, that is also given back to userland as unsigned long. - */ - uint32 flags = calc_vm_prot_bits(ul[0]); - ul[0] = 0; - while (mpdi) { - MksckPageDescInfo *next = mpdi->next; - ul[0] += MksckPageDescManage(mpdi->descs, mpdi->pages, - MANAGE_COUNT); - mpdi->mapCounts = ul[1]; - mpdi = next; - } - if (copy_to_user((void *)arg, ul, sizeof(ul[0]))) { - retval = -EFAULT; - } else { - mpdi = sk->sk_protinfo; - mpdi->flags = flags; - monkiSk = (struct sock *)sk->sk_user_data; - sk->sk_user_data = NULL; - } - } - release_sock(sk); - // clean the monki socket that we are holding - if ((sk = monkiSk)) { - lock_sock(sk); - sock_kfree_s(sk, sk->sk_user_data, sizeof(int)); - sk->sk_user_data = NULL; - release_sock(sk); - sock_put(sk); - } - break; - } - default: { - retval = -EINVAL; - break; - } - } - return retval; -} - - -/** - * @brief VMX receiving a packet from VMM. - * - * @param kiocb kernel io control block (unused) - * @param sock user socket structure - * @param msg user buffer to receive the packet - * @param len size of the user buffer - * @param flags flags - * - * @return -errno on failure, else length of untyped portion + total number - * of bytes mapped for typed portion. - */ -static int -MksckDgramRecvMsg(struct kiocb *kiocb, - struct socket *sock, - struct msghdr *msg, - size_t len, - int flags) -{ - int err = 0; - struct sock *sk = sock->sk; - Mksck *mksck; - Mksck_Datagram *dg; - struct sockaddr_mk *fromAddr; - uint32 read; - struct iovec *iov; - size_t payloadLen, untypedLen; - uint32 iovCount; - - if (flags & MSG_OOB || flags & MSG_ERRQUEUE) { - return -EOPNOTSUPP; - } - - if ((msg->msg_name != NULL) && (msg->msg_namelen < sizeof *fromAddr)) { - return -EINVAL; - } - - lock_sock(sk); - if ((err = MksckTryBind(sk))) { - release_sock(sk); - return err; - } - mksck = sk->sk_protinfo; - - /* - * To avoid mksck disappearing right after the release_sock the - * refcount needs to be incremented. For more details read the - * block comment on locking in MksckCreate. - */ - ATOMIC_ADDV(mksck->refCount, 1); - release_sock(sk); - - /* - * Get pointer to next packet in ring to be dequeued. - */ - while (1) { - - /* - * Wait to make sure this is the only thread trying to access socket. - */ - if ((err = Mutex_Lock(&mksck->mutex, MutexModeEX)) < 0) { - goto decRefc; - } - - /* - * See if packet in ring. - */ - read = mksck->read; - if (read != mksck->write) { - break; - } - - /* - * Nothing there, if user wants us not to block then just return EAGAIN. - */ - if (flags & MSG_DONTWAIT) { - Mutex_Unlock(&mksck->mutex, MutexModeEX); - err = -EAGAIN; - goto decRefc; - } - - /* - * Nothing there, unlock socket and wait for data. - */ - mksck->foundEmpty ++; - err = Mutex_UnlSleep(&mksck->mutex, MutexModeEX, MKSCK_CVAR_FILL); - if (err < 0) { - PRINTK(KERN_INFO "MksckDgramRecvMsg: aborted\n"); - goto decRefc; - } - } - - /* - * Point to packet in ring. - */ - dg = (void *)&mksck->buff[read]; - - /* - * Provide the address of the sender. - */ - if (msg->msg_name != NULL) { - fromAddr = (void *)msg->msg_name; - fromAddr->mk_addr = dg->fromAddr; - fromAddr->mk_family = AF_MKSCK; - msg->msg_namelen = sizeof *fromAddr; - } else { - msg->msg_namelen = 0; - } - - /* - * Copy data from ring buffer to caller's buffer and remove packet from - * ring buffer. - */ - iov = msg->msg_iov; - iovCount = msg->msg_iovlen; - payloadLen = untypedLen = - dg->len - dg->pages * sizeof(Mksck_PageDesc) - dg->pad; - - /* - * Handle the untyped portion of the message. - */ - if (untypedLen <= len) { - err = memcpy_toiovec(iov, - dg->data, - untypedLen); - if (err < 0) { - printk("MksckDgramRecvMsg: Failed to memcpy_to_iovec untyped message component " - "(buf len %d datagram len %d (untyped %d))\n", - len, - dg->len, - untypedLen); - } - } else { - err = -EINVAL; - } - - /* - * Map in the typed descriptor. - */ - if (err >= 0 && dg->pages > 0) { - Mksck_PageDesc *pd = (Mksck_PageDesc *)(dg->data + untypedLen + dg->pad); - - /* - * There are 3 ways of receiving typed messages from the monitor. - * - The typed message is mapped directly into a VMA. To indicate this the - * userland sets msg_controllen == 0. - * - The typed message is mapped directly into a VMA and a file descriptor - * created for further mappings on the host (in same userland address - * space or an alternate userland address space). In this case - * msg_controllen should be set to sizeof(fd). - * - The typed message is not mapped directly into a VMA, but a file - * descriptor is created for later mapping on the host. In this case - * msg_controllen should be set to sizeof(fd) and the supplied iovec - * shall not specify a receive window. - * - * The conjuncts below decide on which of these 3 cases we've encountered. - */ - - if ((msg->msg_controllen <= 0) || - ((err = MksckPageDescToFd(sock, msg, pd, dg->pages)) != 0) || - (MsgHdrHasAvailableRoom(msg) != 0)) { - - down_write(¤t->mm->mmap_sem); // lock for a change of mapping - payloadLen += MksckPageDescMap(pd, dg->pages, iov, iovCount, NULL); - up_write(¤t->mm->mmap_sem); - } - } - - /* - * Now that packet is removed, it is safe to unlock socket so another thread - * can do a recv(). We also want to wake someone waiting for room to insert - * a new packet. - */ - if ((err >= 0) && Mksck_IncReadIndex(mksck, read, dg)) { - Mutex_UnlWake(&mksck->mutex, MutexModeEX, MKSCK_CVAR_ROOM, true); - } else { - Mutex_Unlock(&mksck->mutex, MutexModeEX); - } - - /* - * If memcpy error, return error status. - * Otherwise, return number of bytes copied. - */ - if (err >= 0) { - err = payloadLen; - } - -decRefc: - Mksck_DecRefc(mksck); - return err; -} - - -/** - * @brief VMX sending a packet to VMM. - * - * @param kiocb kernel io control block - * @param sock user socket structure - * @param msg packet to be transmitted - * @param len length of the packet - * - * @return length of the sent msg on success, -errno on failure - */ -static int -MksckDgramSendMsg(struct kiocb *kiocb, - struct socket *sock, - struct msghdr *msg, - size_t len) -{ - int err = 0; - struct sock *sk = sock->sk; - Mksck *peerMksck; - Mksck_Datagram *dg; - uint32 needed; - uint32 write; - Mksck_Address fromAddr; - - if (msg->msg_flags & MSG_OOB) { - return -EOPNOTSUPP; - } - - if (len > MKSCK_XFER_MAX) { - return -EMSGSIZE; - } - - /* - * In the next locked section peerMksck pointer needs to be set and - * its refcount needs to be incremented. - */ - lock_sock(sk); - do { - Mksck *mksck; - Mksck_Address peerAddr = - { .addr = (msg->msg_name ? - ((struct sockaddr_mk *)msg->msg_name)->mk_addr.addr : - MKSCK_ADDR_UNDEF) }; - - if ((err = MksckTryBind(sk))) { - break; - } - mksck = sk->sk_protinfo; - fromAddr = mksck->addr; - - /* - * If the socket is connected, use that address (no sendto for - * connected sockets). Otherwise, use the provided address if any. - */ - if ((peerMksck = mksck->peer)) { - if (peerAddr.addr != MKSCK_ADDR_UNDEF && - peerAddr.addr != mksck->peerAddr.addr) { - err = -EISCONN; - break; - } - /* - * To avoid mksckPeer disappearing right after the - * release_sock the refcount needs to be incremented. For - * more details read the block comment on locking in - * MksckCreate. - */ - ATOMIC_ADDV(peerMksck->refCount, 1); - } else if (peerAddr.addr == MKSCK_ADDR_UNDEF) { - err = -ENOTCONN; - } else { - /* - * LockPeer also increments the refc on the peer. - */ - err = LockPeer(peerAddr, &peerMksck); - } - } while(0); - release_sock(sk); - - if (err) { - return err; - } - - /* - * Get pointer to sufficient empty space in ring buffer. - */ - needed = MKSCK_DGSIZE(len); - while (1) { - /* - * Wait to make sure this is the only thread trying to write to ring. - */ - if ((err = Mutex_Lock(&peerMksck->mutex, MutexModeEX)) < 0) { - goto decRefc; - } - - /* - * Check if socket can receive data. - */ - if (peerMksck->shutDown & MKSCK_SHUT_RD) { - err = -ENOTCONN; - goto unlockDecRefc; - } - - /* - * See if there is room for the packet. - */ - write = Mksck_FindSendRoom(peerMksck, needed); - if (write != MKSCK_FINDSENDROOM_FULL) { - break; - } - - /* - * No room, unlock socket and maybe wait for room. - */ - if (msg->msg_flags & MSG_DONTWAIT) { - err = -EAGAIN; - goto unlockDecRefc; - } - - peerMksck->foundFull ++; - err = Mutex_UnlSleep(&peerMksck->mutex, - MutexModeEX, - MKSCK_CVAR_ROOM); - if (err < 0) { - PRINTK(KERN_INFO "MksckDgramSendMsg: aborted\n"); - goto decRefc; - } - } - - /* - * Point to room in ring and fill in message. - */ - dg = (void *)&peerMksck->buff[write]; - - dg->fromAddr = fromAddr; - dg->len = len; - - if ((err = memcpy_fromiovec(dg->data, msg->msg_iov, len)) != 0) { - goto unlockDecRefc; - } - - /* - * Increment past message. - */ - Mksck_IncWriteIndex(peerMksck, write, needed); - - /* - * Unlock socket and wake someone trying to receive, ie, we filled - * in a message. - */ - Mutex_UnlWake(&peerMksck->mutex, MutexModeEX, MKSCK_CVAR_FILL, false); - - /* - * Maybe guest is in a general 'wait for interrupt' wait or - * grinding away executing guest instructions. - * - * If it has a receive callback armed for the socket and is - * waiting a message, just wake it up. Else send an IPI to the CPU - * running the guest so it will interrupt whatever it is doing and - * read the message. - * - * Holding the mksckPage->mutex prevents mksckPage->vmHKVA from - * clearing on us. - */ - if (peerMksck->rcvCBEntryMVA != 0) { - MksckPage *peerMksckPage = Mksck_ToSharedPage(peerMksck); - - if ((err = Mutex_Lock(&peerMksckPage->mutex, MutexModeSH)) == 0) { - uint32 sockIdx = peerMksck->index; - MvpkmVM *vm = (MvpkmVM *) peerMksckPage->vmHKVA; - - /* - * The destruction of vm and wsp is blocked by the - * mksckPage->mutex. - */ - if (vm) { - WorldSwitchPage *wsp = vm->wsp; - - ASSERT(sockIdx < 8 * sizeof peerMksckPage->wakeVMMRecv); - ATOMIC_ORV(peerMksckPage->wakeVMMRecv, 1U << sockIdx); - - if (wsp) { - Mvpkm_WakeGuest(vm, ACTION_MKSCK); - } - } - Mutex_Unlock(&peerMksckPage->mutex, MutexModeSH); - } - } - - /* - * If all are happy tell the caller the number of transferred bytes. - */ - if (!err) { - err = len; - } - - /* - * Now that we are done with target socket, allow it to be freed. - */ -decRefc: - Mksck_DecRefc(peerMksck); - return err; - -unlockDecRefc: - Mutex_Unlock(&peerMksck->mutex, MutexModeEX); - goto decRefc; -} - - -/** - * @brief Page fault handler for receive windows. Since the host process - * should not be faulting in this region and only be accessing - * memory that has been established via a typed message transfer, - * we always signal the fault back to the process. - */ -static int -MksckFault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - return VM_FAULT_SIGBUS; -} - -/** - * @brief Establish a region in the host process suitable for use as a - * receive window. - * - * @param file file reference (ignored). - * @param sock user socket structure. - * @param vma Linux virtual memory area defining the region. - * - * @return 0 on success, otherwise error code. - */ -static int -MksckMMap(struct file *file, struct socket *sock, struct vm_area_struct *vma) -{ - /* - * All the hard work is done in MksckDgramRecvMsg. Here we simply mark the - * vma as belonging to Mksck. - */ - vma->vm_ops = &mksckVMOps; - - return 0; -} - -/** - * @brief This gets called after returning from the monitor. - * Since the monitor doesn't directly wake VMX threads when it sends - * something to VMX (for efficiency), this routine checks for the - * omitted wakes and does them. - * @param mksckPage some shared page that the monitor writes packets to, ie - * an host shared page - */ -void -Mksck_WakeBlockedSockets(MksckPage *mksckPage) -{ - Mksck *mksck; - uint32 i, wakeHostRecv; - - wakeHostRecv = mksckPage->wakeHostRecv; - if (wakeHostRecv != 0) { - mksckPage->wakeHostRecv = 0; - for (i = 0; wakeHostRecv != 0; i ++) { - if (wakeHostRecv & 1) { - mksck = &mksckPage->sockets[i]; - Mutex_CondSig(&mksck->mutex, MKSCK_CVAR_FILL, true); - } - wakeHostRecv >>= 1; - } - } -} - -/** - * @brief allocate and initialize a shared page. - * @return pointer to shared page.
- * NULL on error - */ -MksckPage * -MksckPageAlloc(void) -{ - uint32 jj; - /* - * Ask for pages in the virtual kernel space. There is no - * requirement to be physically contiguous. - */ - MksckPage *mksckPage = vmalloc(MKSCKPAGE_SIZE); - - if (mksckPage) { - - /* - * Initialize its contents. Start refCount at 1 and decrement it - * when the worldswitch or VM page gets freed. - */ - memset(mksckPage, 0, MKSCKPAGE_SIZE); - ATOMIC_SETV(mksckPage->refCount, 1); - mksckPage->portStore = MKSCK_PORT_HIGH; - - Mutex_Init(&mksckPage->mutex); - for (jj = 0; jjsockets[jj].mutex); - } - } - - return mksckPage; -} - -/** - * @brief Release the allocated pages. - * @param mksckPage the address of the mksckPage to be released - */ -static void -MksckPageRelease(MksckPage *mksckPage) -{ - int ii; - - for (ii = 0; iisockets[ii].mutex); - } - Mutex_Destroy(&mksckPage->mutex); - - vfree(mksckPage); -} - -/** - * @brief Using the tgid locate the vmid of this process. - * Assumed that mksckPageListLock is held - * @return the vmId if page is already allocated, - * the first vacant vmid if not yet allocated.
- * MKSCK_PORT_UNDEF if no slot is vacant - */ -static inline Mksck_VmId -GetHostVmId(void) -{ - uint32 jj; - Mksck_VmId vmId, vmIdFirstVacant = MKSCK_VMID_UNDEF; - MksckPage *mksckPage; - uint32 tgid = task_tgid_vnr(current); - /* - * Assign an unique vmId to the shared page. Start the search from - * the vmId that is the result of hashing tgid to 15 bits. As a - * used page with a given vmId can occupy only a given slot in the - * mksckPages array, it is enough to search through the - * MKSCK_MAX_SHARES slots for a vacancy. - */ - for (jj = 0, vmId = MKSCK_TGID2VMID(tgid); - jj < MKSCK_MAX_SHARES; - jj++, vmId++) { - if (vmId > MKSCK_VMID_HIGH) { - vmId = 0; - } - mksckPage = mksckPages[MKSCK_VMID2IDX(vmId)]; - - if (mksckPage) { - if (mksckPage->tgid == tgid && - !mksckPage->isGuest) { - return mksckPage->vmId; - } - - } else if (vmIdFirstVacant == MKSCK_VMID_UNDEF) { - vmIdFirstVacant = vmId; - } - } - return vmIdFirstVacant; -} - - -/** - * @brief Locate the first empty slot - * Assumed that mksckPageListLock is held - * @return the first vacant vmid.
- * MKSCK_PORT_UNDEF if no slot is vacant - */ -static inline Mksck_VmId -GetNewGuestVmId(void) -{ - Mksck_VmId vmId; - - for (vmId = 0; vmId < MKSCK_MAX_SHARES; vmId++) { - if (!mksckPages[MKSCK_VMID2IDX(vmId)]) { - return vmId; - } - } - return MKSCK_VMID_UNDEF; -} - - -/** - * @brief Find shared page for a given idx. The page referred to be the - * idx should exist and be locked by the caller. - * @param idx index of the page in the array - * @return pointer to shared page - */ -MksckPage * -MksckPage_GetFromIdx(uint32 idx) -{ - MksckPage *mksckPage = mksckPages[idx]; - ASSERT(mksckPage); - ASSERT(idxrefCount)); - return mksckPage; -} - -/** - * @brief find shared page for a given vmId - * The vmid should exist and be locked by the caller. - * @param vmId vmId to look for, either an host vmId or a guest vmId - * @return pointer to shared page - */ -MksckPage * -MksckPage_GetFromVmId(Mksck_VmId vmId) -{ - MksckPage *mksckPage = mksckPages[MKSCK_VMID2IDX(vmId)]; - ASSERT(mksckPage); - ASSERT(mksckPage->vmId == vmId); - ASSERT(ATOMIC_GETO(mksckPage->refCount)); - return mksckPage; -} - - -/** - * @brief find shared page for a given vmId - * @param vmId vmId to look for, either an host vmId or a guest vmId - * @return NULL: no such shared page exists
- * else: pointer to shared page. - * Call Mksck_DecRefc() when done with pointer - */ -MksckPage * -MksckPage_GetFromVmIdIncRefc(Mksck_VmId vmId) -{ - MksckPage *mksckPage; - - spin_lock(&mksckPageListLock); - mksckPage = mksckPages[MKSCK_VMID2IDX(vmId)]; - - if (!mksckPage || (mksckPage->vmId != vmId)) { - printk(KERN_INFO "MksckPage_GetFromVmIdIncRefc: vmId %04X not found\n", - vmId); - mksckPage = NULL; - } else { - ATOMIC_ADDV(mksckPage->refCount, 1); - } - spin_unlock(&mksckPageListLock); - return mksckPage; -} - - -/** - * @brief find or allocate shared page using tgid - * @return NULL: no such shared page exists
- * else: pointer to shared page. - * Call Mksck_DecRefc() when done with pointer - */ -MksckPage * -MksckPage_GetFromTgidIncRefc(void) -{ - MksckPage *mksckPage; - Mksck_VmId vmId; - - while (1) { - spin_lock(&mksckPageListLock); - vmId = GetHostVmId(); - - if (vmId == MKSCK_VMID_UNDEF) { - /* - * No vmId has been allocated yet and there is no free slot. - */ - spin_unlock(&mksckPageListLock); - return NULL; - } - - mksckPage = mksckPages[MKSCK_VMID2IDX(vmId)]; - if (mksckPage != NULL) { - /* - * There is a vmid already allocated, increment the refc on it. - */ - ATOMIC_ADDV(mksckPage->refCount, 1); - spin_unlock(&mksckPageListLock); - return mksckPage; - } - - /* - * Have to release spinlock to allocate a new page. - */ - spin_unlock(&mksckPageListLock); - mksckPage = MksckPageAlloc(); - if (mksckPage == NULL) { - return NULL; - } - - /* - * Re-lock and make sure no one else allocated while unlocked. - * If someone else did allocate, free ours off and use theirs. - */ - spin_lock(&mksckPageListLock); - vmId = GetHostVmId(); - if ((vmId != MKSCK_VMID_UNDEF) && - (mksckPages[MKSCK_VMID2IDX(vmId)] == NULL)) { - break; - } - spin_unlock(&mksckPageListLock); - MksckPageRelease(mksckPage); - } - - /* - * This is a successful new allocation. insert it into the table - * and initialize the fields. - */ - mksckPages[MKSCK_VMID2IDX(vmId)] = mksckPage; - mksckPage->vmId = vmId; - mksckPage->isGuest = false; - mksckPage->vmHKVA = 0; - mksckPage->tgid = task_tgid_vnr(current); - printk(KERN_DEBUG "New host mksck page is allocated: idx %x, vmId %x, tgid %d\n", - MKSCK_VMID2IDX(vmId), vmId, mksckPage->tgid); - - spin_unlock(&mksckPageListLock); - return mksckPage; -} - -/** - * @brief Initialize the VMX provided wsp. Allocate communication page. - * @param vm which virtual machine we're running - * @return 0 if all OK, error value otherwise - */ -int -Mksck_WspInitialize(MvpkmVM *vm) -{ - WorldSwitchPage *wsp = vm->wsp; - int err; - Mksck_VmId vmId; - MksckPage *mksckPage; - - if (wsp->guestId) { - err = -EBUSY; - } else if (!(mksckPage = MksckPageAlloc())) { - err = -ENOMEM; - } else { - spin_lock(&mksckPageListLock); - - if ((vmId = GetNewGuestVmId()) == MKSCK_VMID_UNDEF) { - - err = -EMFILE; - MksckPageRelease(mksckPage); - - printk(KERN_INFO "Mksck_WspInitialize: Cannot allocate vmId\n"); - - } else { - /* - * Now that the mksckPage is all initialized, let others see it. - */ - mksckPages[MKSCK_VMID2IDX(vmId)] = mksckPage; - mksckPage->vmId = vmId; - mksckPage->isGuest = true; - mksckPage->vmHKVA = (HKVA)vm; - /* mksckPage->tgid is undefined when isGuest is true */ - - wsp->guestId = vmId; - - printk(KERN_DEBUG "New guest mksck page is allocated: idx %x, vmId %x\n", - MKSCK_VMID2IDX(vmId), vmId); - - err = 0; - } - - /* - * All stable, ie, mksckPages[] written, ok to unlock now. - */ - spin_unlock(&mksckPageListLock); - } - - return err; -} - -/** - * @brief Release the wsp. Clean up after the monitor. Free the - * associated communication page. - * @param wsp which worldswitch page (VCPU) - */ -void -Mksck_WspRelease(WorldSwitchPage *wsp) -{ - int ii; - int err; - MksckPage *mksckPage = MksckPage_GetFromVmId(wsp->guestId); - - /* - * The worldswitch page for a particular VCPU is about to be freed - * off, so we know the monitor will never execute again. But the - * monitor most likely left some sockets open. Those may have - * outbound connections to host sockets that we must close. - * - * Loop through all possibly open sockets. - */ - uint32 isOpened = wsp->isOpened; - Mksck *mksck = mksckPage->sockets; - while (isOpened) { - if (isOpened & 1) { - ASSERT(ATOMIC_GETO(mksck->refCount) != 0); - /* - * The socket may be connected to a peer (host) socket, so we - * have to decrement that target socket's reference - * count. Unfortunately, Mksck_DisconnectPeer(mksck) cannot - * be called as mksck->peer is an mva not an hkva. Translate - * the address first. - */ - if (mksck->peer) { - MksckPage *mksckPagePeer = MksckPage_GetFromVmId(mksck->peerAddr.vmId); - ASSERT(mksckPagePeer); - mksck->peer = MksckPage_GetFromAddr(mksckPagePeer, mksck->peerAddr); - ASSERT(mksck->peer); - /* mksck->peer is now a hkva */ - } - - Mksck_CloseCommon(mksck); - } - isOpened >>= 1; - mksck++; - } - - /* - * A host socket may be in the process of sending to the guest. It - * will attempt to wake up the guest using mksckPage->vmHKVA and - * mksckPage->vmHKVA->wsp. To assure that the vm and wsp structures - * are not disappearing from under the sending thread we lock the - * page here. - */ - err = Mutex_Lock(&mksckPage->mutex, MutexModeEX); - ASSERT(!err); - mksckPage->vmHKVA = 0; - Mutex_Unlock(&mksckPage->mutex, MutexModeEX); - /* - * Decrement refcount set by MksckPageAlloc() call in - * Mksck_WspInitialize(). - */ - MksckPage_DecRefc(mksckPage); - - /* - * Decrement refcount set by VMM:Mksck_Init() referring to the local - * variable guestMksckPage. - */ - if (wsp->guestPageMapped) { - wsp->guestPageMapped = false; - MksckPage_DecRefc(mksckPage); - } - - /* - * Another task is to decrement the reference count on the mksck - * pages the monitor accessed. Those pages are listed in the - * wsp->isPageMapped list. They were locked by the monitor - * calling WSCALL_GET_PAGE_FROM_VMID - */ - for (ii = 0; ii < MKSCK_MAX_SHARES; ii++) { - if (wsp->isPageMapped[ii]) { - MksckPage *mksckPageOther = MksckPage_GetFromIdx(ii); - - wsp->isPageMapped[ii] = false; - MksckPage_DecRefc(mksckPageOther); - } - } -} - -/** - * @brief disconnect from peer by decrementing - * peer socket's reference count and clearing the pointer. - * @param mksck local socket to check for connection - */ -void -Mksck_DisconnectPeer(Mksck *mksck) -{ - Mksck *peerMksck = mksck->peer; - if (peerMksck != NULL) { - mksck->peer = NULL; - mksck->peerAddr.addr = MKSCK_ADDR_UNDEF; - Mksck_DecRefc(peerMksck); - } -} - - -/** - * @brief decrement shared page reference count, free page if it goes zero. - * also do a dmb first to make sure all activity on the struct is - * finished before decrementing the ref count. - * @param mksckPage shared page - */ -void -MksckPage_DecRefc(MksckPage *mksckPage) -{ - uint32 oldRefc; - - DMB(); - do { - while ((oldRefc = ATOMIC_GETO(mksckPage->refCount)) == 1) { - - /* - * Find corresponding entry in list of known shared pages and - * clear it so we can't open any new sockets on this shared - * page, thus preventing its refCount from being incremented. - */ - spin_lock(&mksckPageListLock); - if (ATOMIC_SETIF(mksckPage->refCount, 0, 1)) { - uint32 ii = MKSCK_VMID2IDX(mksckPage->vmId); - ASSERT(ii < MKSCK_MAX_SHARES); - ASSERT(mksckPages[ii] == mksckPage); - mksckPages[ii] = NULL; - spin_unlock(&mksckPageListLock); - printk(KERN_DEBUG "%s mksck page is released: idx %x, vmId %x, tgid %d\n", - mksckPage->isGuest?"Guest":"Host", - ii, mksckPage->vmId, mksckPage->tgid); - MksckPageRelease(mksckPage); - return; - } - spin_unlock(&mksckPageListLock); - } - ASSERT(oldRefc != 0); - } while (!ATOMIC_SETIF(mksckPage->refCount, oldRefc - 1, oldRefc)); -} - -/** - * @brief Lookup if the provided mpn belongs to one of the Mksck pages. Map if found. - * @return 0 if all OK, error value otherwise - */ -int -MksckPage_LookupAndInsertPage(struct vm_area_struct *vma, - unsigned long address, - MPN mpn) -{ - int ii, jj; - MksckPage **mksckPagePtr = mksckPages; - - spin_lock(&mksckPageListLock); - for (jj = MKSCK_MAX_SHARES; jj--; mksckPagePtr++) { - if (*mksckPagePtr) { - for (ii = 0; ii < MKSCKPAGE_TOTAL; ii++) { - if (vmalloc_to_pfn((void*)(((HKVA)*mksckPagePtr) + ii*PAGE_SIZE)) == mpn && - vm_insert_page(vma, address, pfn_to_page(mpn)) == 0) { - spin_unlock(&mksckPageListLock); - return 0; - } - } - } - } - spin_unlock(&mksckPageListLock); - return -1; -} - - -/** - * @brief Print information on the allocated shared pages - * - * This function reports (among many other things) on the use of locks - * on the mksck page (page lock and individual socket locks). To avoid - * the Hiesenberg effect it avoids using locks unless there is a - * danger of dereferencing freed memory. In particular, holding - * mksckPageListLock ensures that the mksck page is not freed while it - * is read. But under very rare conditions this function may report - * inconsistent or garbage data. - */ -static int -MksckPageInfoShow(struct seq_file *m, void *private) -{ - int ii, jj; - uint32 isPageMapped = 0; - int err; - MvpkmVM *vm; - - /* - * Lock is needed to atomize the test and dereference of - * mksckPages[ii] - */ - spin_lock(&mksckPageListLock); - for (ii = 0; ii < MKSCK_MAX_SHARES; ii++) { - MksckPage *mksckPage = mksckPages[ii]; - if (mksckPage != NULL && mksckPage->isGuest) { - /* - * After the refcount is incremented mksckPage will not be - * freed and it can continued to be dereferenced after the - * unlock of mksckPageListLock. - */ - ATOMIC_ADDV(mksckPage->refCount, 1); - spin_unlock(&mksckPageListLock); - - /* - * To dereference mksckPage->vmHKVA, we need to have the page - * lock. - */ - err = Mutex_Lock(&mksckPage->mutex, MutexModeEX); - vm = (MvpkmVM *) mksckPage->vmHKVA; - - if (err == 0 && vm && vm->wsp) { - for (jj = 0; jj < MKSCK_MAX_SHARES; jj++) { - if (vm->wsp->isPageMapped[jj]) isPageMapped |= 1<mutex, MutexModeEX); - /* - * Decrement the page refcount and relock the - * mksckPageListLock for the next for loop. - */ - MksckPage_DecRefc(mksckPage); - spin_lock(&mksckPageListLock); - break; - } - } - - /* mksckPageListLock is still locked, mksckPages[ii] can be dereferenced */ - for (ii = 0; ii < MKSCK_MAX_SHARES; ii++) { - MksckPage *mksckPage = mksckPages[ii]; - if (mksckPage != NULL) { - uint32 lState = ATOMIC_GETO(mksckPage->mutex.state); - uint32 isOpened = 0; /* guest has an implicit ref */ - - seq_printf(m, "MksckPage[%02d]: { vmId = %4x(%c), refC = %2d%s", - ii, mksckPage->vmId, - mksckPage->isGuest?'G':'H', - ATOMIC_GETO(mksckPage->refCount), - (isPageMapped&(1<mutex.line, mksckPage->mutex.lineUnl); - } - - - if (!mksckPage->isGuest) { - struct task_struct *target; - seq_printf(m, ", tgid = %d", mksckPage->tgid); - - rcu_read_lock(); - - target = pid_task(find_vpid(mksckPage->tgid), PIDTYPE_PID); - seq_printf(m, "(%s)", target ? target->comm : "no such process"); - - rcu_read_unlock(); - } else { - ATOMIC_ADDV(mksckPage->refCount, 1); - spin_unlock(&mksckPageListLock); - - err = Mutex_Lock(&mksckPage->mutex, MutexModeEX); - vm = (MvpkmVM *) mksckPage->vmHKVA; - - if (err == 0 && vm && vm->wsp) { - isOpened = vm->wsp->isOpened; - } - Mutex_Unlock(&mksckPage->mutex, MutexModeEX); - MksckPage_DecRefc(mksckPage); - spin_lock(&mksckPageListLock); - /* - * As the mksckPageListLock was unlocked, nothing - * prevented the MksckPage_DecRefc from actually freeing - * the page. Lets verify that the page is still there. - */ - if (mksckPage != mksckPages[ii]) { - seq_printf(m, " released }\n"); - continue; - } - } - seq_printf(m, ", sockets[] = {"); - - for (jj = 0; jj < mksckPage->numAllocSocks; jj++, isOpened >>= 1) { - Mksck *mksck = mksckPage->sockets + jj; - - if (ATOMIC_GETO(mksck->refCount)) { - uint32 blocked; - lState = ATOMIC_GETO(mksck->mutex.state); - seq_printf(m, "\n { addr = %8x, refC = %2d%s%s%s", - mksck->addr.addr, - ATOMIC_GETO(mksck->refCount), - (isOpened & 1 ? "*" : ""), - (mksck->shutDown & MKSCK_SHUT_RD ? " SHUTD_RD":""), - (mksck->shutDown & MKSCK_SHUT_WR ? " SHUTD_WR":"")); - - if (mksck->peer) { - seq_printf(m, ", peerAddr = %8x", - mksck->peerAddr.addr); - } - - if (lState) { - seq_printf(m, ", lock=%x locked by line %d, unlocked by %d", - lState, mksck->mutex.line, mksck->mutex.lineUnl); - } - - if ((blocked = ATOMIC_GETO(mksck->mutex.blocked))) { - seq_printf(m, ", blocked=%d", blocked); - } - - seq_printf(m, " }"); - } - } - seq_printf(m, " } }\n"); - } - } - spin_unlock(&mksckPageListLock); - - return 0; -} - - -static int -MksckPageInfoOpen(struct inode *inode, struct file *file) -{ - return single_open(file, MksckPageInfoShow, inode->i_private); -} - -static const struct file_operations mksckPageInfoFops = { - .open = MksckPageInfoOpen, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *mksckPageDentry = NULL; - -void -MksckPageInfo_Init(void) -{ - mksckPageDentry = debugfs_create_file("mksckPage", - S_IROTH, - NULL, - NULL, - &mksckPageInfoFops); -} - -void -MksckPageInfo_Exit(void) -{ - if (mksckPageDentry) { - debugfs_remove(mksckPageDentry); - } -} diff --git a/arch/arm/mvp/mvpkm/mksck_kernel.h b/arch/arm/mvp/mvpkm/mksck_kernel.h deleted file mode 100644 index 233b780..0000000 --- a/arch/arm/mvp/mvpkm/mksck_kernel.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The monitor-kernel socket interface kernel-only definitions. - */ - -#ifndef _MKSCK_KERNEL_H -#define _MKSCK_KERNEL_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mksck_shared.h" - -/* - * prototypes - */ -int Mksck_Init(void); -void Mksck_Exit(void); -void Mksck_WakeBlockedSockets(MksckPage *mksckPage); -MksckPage *MksckPage_GetFromTgidIncRefc(void); -MksckPage *MksckPage_GetFromVmIdIncRefc(Mksck_VmId vmId); -MksckPage *MksckPage_GetFromIdx(uint32 idx); -void MksckPageInfo_Init(void); -void MksckPageInfo_Exit(void); -int Mksck_WspInitialize(MvpkmVM *vm); -void Mksck_WspRelease(WorldSwitchPage *wsp); -int MksckPage_LookupAndInsertPage(struct vm_area_struct *vma, - unsigned long address, - MPN mpn); - -/* - * Mksck open request must come from this uid. - */ -extern uid_t Mvpkm_vmwareUid; - -#define MKSCK_DEVEL 0 - -#if MKSCK_DEVEL -#define PRINTK printk -#else -#define PRINTK if (0) printk -#endif - -#define HOST_CPUID_UNDEF (~0) - -#endif diff --git a/arch/arm/mvp/mvpkm/mksck_shared.c b/arch/arm/mvp/mvpkm/mksck_shared.c deleted file mode 100644 index 68c38fc6..0000000 --- a/arch/arm/mvp/mvpkm/mksck_shared.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#include "mvp.h" -#include "mksck_shared.h" - -/** - * @file - * - * @brief The mksck shared area functions used by the monitor and the - * kernel extension. - * - */ - -/** - * @brief try to locate a socket using an address. - * @param mksckPage which shared page to look on. - * ASSUMED: locked for shared access - * @param addr address to check - * @return pointer to mksck page with addr. - * NULL if not found - */ -Mksck * -MksckPage_GetFromAddr(MksckPage *mksckPage, Mksck_Address addr) -{ - Mksck *mksck = mksckPage->sockets; - uint32 ii; - - ASSERT(addr.vmId == mksckPage->vmId); - - for (ii = mksckPage->numAllocSocks; ii--; mksck++) { - if ((ATOMIC_GETO(mksck->refCount) != 0) && - (mksck->addr.addr == addr.addr)) { - return mksck; - } - } - return NULL; -} - -/** - * @brief Close a monitor socket. - * - * @param mksck pointer to the socket control block - */ -void -Mksck_CloseCommon(Mksck *mksck) -{ - /* - * If a peer was connected, release the peer. - */ - Mksck_DisconnectPeer(mksck); - - /* - * Signal senders that this socket won't be read anymore. - */ - while (Mutex_Lock(&mksck->mutex, MutexModeEX) < 0); - mksck->shutDown = MKSCK_SHUT_WR | MKSCK_SHUT_RD; - Mutex_UnlWake(&mksck->mutex, MutexModeEX, MKSCK_CVAR_ROOM, true); - - /* - * Decrement reference count because it was set to 1 when opened. It could - * still be non-zero after this if some other thread is currently sending to - * this socket. - */ - Mksck_DecRefc(mksck); -} - - -/** - * @brief decrement socket reference count, free if it goes zero. Also do a - * dmb first to make sure all activity on the struct is finished before - * decrementing the ref count. - * @param mksck socket - */ -void -Mksck_DecRefc(Mksck *mksck) -{ - uint32 oldRefc; - - DMB(); - do { - while ((oldRefc = ATOMIC_GETO(mksck->refCount)) == 1) { - - MksckPage *mksckPage = Mksck_ToSharedPage(mksck); - - /* - * Socket refcount is going zero on a socket that locks mksckPage in. - * Lock shared page exclusive to make sure no one is trying to look - * for this socket, thus preventing socket's refcount from being - * incremented non-zero once we decrement it to zero. - */ - - /* - * Lock failed probably because of an interrupt. Keep trying - * to lock until we succeed. - */ - while (Mutex_Lock(&mksckPage->mutex, MutexModeEX) < 0); - - /* - * No one is doing any lookups, so set refcount zero. - */ - if (ATOMIC_SETIF(mksck->refCount, 0, 1)) { -#if 0 - /** - * @knownjira{MVP-1349} - * The standard Log is not yet implemented in the kernel space. - */ - KNOWN_BUG(MVP-1349); - PRINTK(KERN_INFO "Mksck_DecRefc: %08X shutDown %u, foundEmpty %u," - " foundFull %u, blocked %u\n", - mksck->addr.addr, mksck->shutDown, - mksck->foundEmpty, mksck->foundFull, - ATOMIC_GETO(mksck->mutex.blocked)); -#endif - - /* - * Sockets can't have connected peers by the time their - * refc hits 0. The owner should have cleaned that up by - * now. - */ - ASSERT(mksck->peer == 0); - - /* - * Successfully set to zero, release mutex and decrement - * shared page ref count as it was incremented when the - * socket was opened. This may free the shared page. - */ - Mutex_Unlock(&mksckPage->mutex, MutexModeEX); - MksckPage_DecRefc(mksckPage); - return; - } - - /* - * Someone incremented refcount just before we locked the mutex, so - * try it all again. - */ - Mutex_Unlock(&mksckPage->mutex, MutexModeEX); - } - - /* - * Not going zero or doesn't lock mksckPage, simple decrement. - */ - ASSERT(oldRefc != 0); - } while (!ATOMIC_SETIF(mksck->refCount, oldRefc - 1, oldRefc)); -} - - -/** - * @brief Find an unused port. - * @param mksckPage which shared page to look in. - * Locked for exclusive access - * @param port if not MKSCK_PORT_UNDEF test only this port - * @return port allocated or MKSCK_PORT_UNDEF if none was found - */ -Mksck_Port -MksckPage_GetFreePort(MksckPage *mksckPage, Mksck_Port port) -{ - Mksck_Address addr = { .addr = Mksck_AddrInit(mksckPage->vmId, port) }; - uint32 ii; - - if (port == MKSCK_PORT_UNDEF) { - for (ii = 0; iiportStore--; - if (!addr.port) { - - /* - * Wrapped around, reset portStore - */ - mksckPage->portStore = MKSCK_PORT_HIGH; - } - - if (!MksckPage_GetFromAddr(mksckPage, addr)) { - return addr.port; - } - } - - } else if (!MksckPage_GetFromAddr(mksckPage, addr)) { - return addr.port; - } - - return MKSCK_PORT_UNDEF; -} - -/** - * @brief Find an unused slot in the sockets[] array and allocate it. - * @param mksckPage which shared page to look in. - * Locked for exclusive access - * @param addr what local address to assign to the socket - * @return NULL: no slots available
- * else: pointer to allocated socket - */ -Mksck * -MksckPage_AllocSocket(MksckPage *mksckPage, Mksck_Address addr) -{ - Mksck *mksck; - uint32 i; - - for (i = 0; (offsetof(MksckPage, sockets[i+1]) <= MKSCKPAGE_SIZE) && - (i < 8 * sizeof mksckPage->wakeHostRecv) && - (i < 8 * sizeof mksckPage->wakeVMMRecv); i ++) { - mksck = &mksckPage->sockets[i]; - if (ATOMIC_GETO(mksck->refCount) == 0) { - ATOMIC_SETV(mksck->refCount, 1); - mksck->addr = addr; - mksck->peerAddr.addr = MKSCK_ADDR_UNDEF; - mksck->peer = NULL; - mksck->index = i; - mksck->write = 0; - mksck->read = 0; - mksck->shutDown = 0; - mksck->foundEmpty = 0; - mksck->foundFull = 0; - ATOMIC_SETV(mksck->mutex.blocked, 0); - mksck->rcvCBEntryMVA = 0; - mksck->rcvCBParamMVA = 0; - - if (mksckPage->numAllocSocks < ++ i) { - mksckPage->numAllocSocks = i; - } - - return mksck; - } - } - return NULL; -} - - -/** - * @brief increment read index over the packet just read - * @param mksck socket packet was read from. - * Locked for exclusive access - * @param read current value of mksck->read - * @param dg datagram at current mksck->read - * @return with mksck->read updated to next packet
- * false: buffer not empty
- * true: buffer now empty - */ -_Bool -Mksck_IncReadIndex(Mksck *mksck, uint32 read, Mksck_Datagram *dg) -{ - ASSERT(read == mksck->read); - ASSERT((void *)dg == (void *)&mksck->buff[read]); - - read += MKSCK_DGSIZE(dg->len); - if ((read > mksck->write) && (read >= mksck->wrap)) { - ASSERT(read == mksck->wrap); - read = 0; - } - mksck->read = read; - - return read == mksck->write; -} - - -/** - * @brief find index in buffer that has enough room for a packet - * @param mksck socket message is being sent to. - * Locked for exclusive access - * @param needed room needed, including dg header and rounded up - * @return MKSCK_FINDSENDROOM_FULL: not enough room available
- * else: index in mksck->buff for packet - */ -uint32 -Mksck_FindSendRoom(Mksck *mksck, uint32 needed) -{ - uint32 read, write; - - /* - * We must leave at least one byte unused so receiver can distinguish full - * from empty. - */ - read = mksck->read; - write = mksck->write; - if (write == read) { - if (needed < MKSCK_BUFSIZE) { - mksck->read = 0; - mksck->write = 0; - return 0; - } - } else if (write < read) { - if (write + needed < read) { - return write; - } - } else { - if (write + needed < MKSCK_BUFSIZE) { - return write; - } - if ((write + needed == MKSCK_BUFSIZE) && (read > 0)) { - return write; - } - if (needed < read) { - mksck->wrap = write; - mksck->write = 0; - return 0; - } - } - - return MKSCK_FINDSENDROOM_FULL; -} - - -/** - * @brief increment read index over the packet just written - * @param mksck socket packet was written to. - * Locked for exclusive access - * @param write as returned by @ref Mksck_FindSendRoom - * @param needed as passed to @ref Mksck_FindSendRoom - * @return with mksck->write updated to next packet - */ -void -Mksck_IncWriteIndex(Mksck *mksck, uint32 write, uint32 needed) -{ - ASSERT(write == mksck->write); - write += needed; - if (write >= MKSCK_BUFSIZE) { - ASSERT(write == MKSCK_BUFSIZE); - mksck->wrap = MKSCK_BUFSIZE; - write = 0; - } - ASSERT(write != mksck->read); - mksck->write = write; -} diff --git a/arch/arm/mvp/mvpkm/mksck_shared.h b/arch/arm/mvp/mvpkm/mksck_shared.h deleted file mode 100644 index 2677ec1..0000000 --- a/arch/arm/mvp/mvpkm/mksck_shared.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The monitor-kernel socket interface shared area definitions. - */ - -#ifndef _MKSCK_SHARED_H -#define _MKSCK_SHARED_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/* - * Allocated MksckPages are stored in an array of size - * MKSCK_MAX_SHARES. The vmid and the slot index of a shared page is - * not unrelated: vmid = idx%MKSCK_MAX_SHARES. - */ -#define MKSCK_MAX_SHARES_LOG2 4 // 16: one per VM + one per VCPU -#define MKSCK_MAX_SHARES (1U << MKSCK_MAX_SHARES_LOG2) -#define MKSCK_VMID2IDX(idx) ((idx)%MKSCK_MAX_SHARES) -#define MKSCK_TGID2VMID(tgid) (((((tgid)<<1)^((tgid)>>15))&0xfffe)|1) -/* - * The size of a shared page determines how many sockets can be open - * concurrently. - */ -#define MKSCKPAGE_TOTAL 8 // number of shared pages -#define MKSCKPAGE_SIZE (PAGE_SIZE * MKSCKPAGE_TOTAL) -#define MKSCK_SOCKETS_PER_PAGE ((MKSCKPAGE_SIZE-offsetof(MksckPage, sockets[0])) / \ - sizeof(Mksck)) - -/* - * Individual datagrams are aligned on a MKSCK_ALIGNMENT byte boundary - * in the data receive area of a socket. - */ -#define MKSCK_ALIGNMENT 8 // data packet alignment -#define MKSCK_ALIGN(x) MVP_ALIGN(x, MKSCK_ALIGNMENT) -#define MKSCK_DGSIZE(len) offsetof(Mksck_Datagram, data[MKSCK_ALIGN(len)]) -#define MKSCK_BUFSIZE MKSCK_DGSIZE(MKSCK_XFER_MAX + 1) - -/* - * Conditional variables for sleeping on. - */ -#define MKSCK_CVAR_ROOM 0 // senders waiting for room for message -#define MKSCK_CVAR_FILL 1 // receivers waiting for a message to fetch - -#define MKSCK_FINDSENDROOM_FULL 0xFFFFFFFFU - -/* - * Shutdown bits - */ -#define MKSCK_SHUT_WR (1 << 0) // socket can't send data anymore -#define MKSCK_SHUT_RD (1 << 1) // socket can't receive data anymore - -typedef struct Mksck Mksck; -typedef struct Mksck_Datagram Mksck_Datagram; -typedef struct MksckPage MksckPage; - -#include "atomic.h" -#include "mksck.h" -#include "mmu_defs.h" -#include "mutex.h" -#include "arm_inline.h" - -/** - * @brief Monitor-kernel socket datagram structure - */ -struct Mksck_Datagram { - Mksck_Address fromAddr; ///< source address - uint32 len : 16; ///< length of the data - uint32 pad : 3; ///< padding between untyped message and mpn - ///< array. - uint32 pages : 13; ///< number of pages in mpn array - uint8 data[1] ///< start of the data - __attribute__((aligned(MKSCK_ALIGNMENT))); -}; - -/** - * @brief one particular socket's shared page data. - */ -struct Mksck { - AtmUInt32 refCount; ///< when zero, struct is free - ///< ... increment only with mksckPage->mutex - ///< ... decrement at any time - Mksck_Address addr; ///< this socket's address if open - ///< ... MKSCK_ADDR_UNDEF if closed - ///< ... open only with mksckPage->mutex - Mksck_Address peerAddr; ///< peer's address if connected - ///< ... MKSCK_ADDR_UNDEF if not - struct Mksck *peer; ///< connected peer's ptr or NULL if not - ///< ... ptr is MVA for monitor sockets and - ///< ... HKVA for sockets of host processes - ///< ... holds ref count on target socket - uint32 index; ///< index of this socket in page - - ///< empty ring indicated by read == write - ///< ring never completely fills, always at - ///< least room for one more byte so we can tell - ///< empty from full - - uint32 write; ///< index within buff to insert next data - ///< ... always < MKSCK_BUFSIZE - uint32 read; ///< index within buff to remove next data - ///< ... always < MKSCK_BUFSIZE - uint32 wrap; ///< current wrapping point - ///< ... valid only whenever write < read - uint32 shutDown; ///< MKSCK_SHUT_RD, MKSCK_SHUT_WR bitfield - uint32 foundEmpty; ///< number of times a receive has blocked - uint32 foundFull; ///< number of times a send has blocked - Mutex mutex; ///< locks the ring buffer - MVA rcvCBEntryMVA; ///< monitor's receive callback entrypoint - MVA rcvCBParamMVA; ///< monitor's receive callback parameter - uint8 buff[MKSCK_BUFSIZE] ///< data going TO this socket - __attribute__((aligned(MKSCK_ALIGNMENT))); -}; - - -/** - * @brief the shared page of an address domain (vmId) - */ -struct MksckPage { - _Bool isGuest; ///< the page belongs to a monitor/guest - uint32 tgid; ///< thread group id if isGuest=true - ///< undefined otherwise - volatile HKVA vmHKVA; ///< host side local data structure for vm - AtmUInt32 refCount; ///< page cannot be freed unless this is zero - ///< ... increment only with mksckPageListLock - ///< ... decrement at any time - ///< ... initialized to 1 for wsp->mksckPage* pointers - uint32 wakeHostRecv; ///< bitmask of sockets[] to be woken for receive - ///< ... access from VCPU thread only - AtmUInt32 wakeVMMRecv; ///< likewise for monitor receive callbacks - Mutex mutex; ///< locks list of open sockets - Mksck_VmId vmId; ///< hostId or guestId these sockets are for - Mksck_Port portStore; ///< used to assign ephemeral port numbers - uint32 numAllocSocks; ///< number of elements in sockets[] array - Mksck sockets[1]; ///< array of sockets (to fill MKSCKPAGE_SIZE) -}; - -MksckPage *MksckPage_GetFromVmId(Mksck_VmId vmId); -Mksck_Port MksckPage_GetFreePort(MksckPage *mksckPage, Mksck_Port port); -Mksck *MksckPage_GetFromAddr(MksckPage *mksckPage, Mksck_Address addr); -Mksck *MksckPage_AllocSocket(MksckPage *mksckPage, Mksck_Address addr); -void MksckPage_DecRefc(MksckPage *mksckPage); - -void Mksck_DecRefc(Mksck *mksck); -void Mksck_CloseCommon(Mksck *mksck); -_Bool Mksck_IncReadIndex(Mksck *mksck, uint32 read, Mksck_Datagram *dg); -uint32 Mksck_FindSendRoom(Mksck *mksck, uint32 needed); -void Mksck_IncWriteIndex(Mksck *mksck, uint32 write, uint32 needed); -void Mksck_DisconnectPeer(Mksck *mksck); - - -/** - * @brief determine which shared page a given socket is on - * Note that this process does not rely on any directory. - * @param mksck pointer to socket - * @return pointer to shared page - */ -static inline MksckPage * -Mksck_ToSharedPage(Mksck *mksck) -{ - return (MksckPage*)((char*)(mksck - mksck->index) - - offsetof(MksckPage, sockets)); -} -#endif diff --git a/arch/arm/mvp/mvpkm/mksck_sockaddr.h b/arch/arm/mvp/mvpkm/mksck_sockaddr.h deleted file mode 100644 index e99d1f5..0000000 --- a/arch/arm/mvp/mvpkm/mksck_sockaddr.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Host user space definitions for mksck sockets. - */ - -#ifndef _MKSCK_SOCKADDR_H_ -#define _MKSCK_SOCKADDR_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mksck.h" - -/* no one ever uses DECnet anymore? */ -#define AF_MKSCK AF_DECnet -#define PF_MKSCK PF_DECnet - -/* Address structure used by the host user socket interface. */ -struct sockaddr_mk { - sa_family_t mk_family; - Mksck_Address mk_addr; -}; - -#endif diff --git a/arch/arm/mvp/mvpkm/mmu_defs.h b/arch/arm/mvp/mvpkm/mmu_defs.h deleted file mode 100644 index 340b91b..0000000 --- a/arch/arm/mvp/mvpkm/mmu_defs.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MMU-related definitions. - */ - -#ifndef _MMU_DEFS_H_ -#define _MMU_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @name ARM address space identifier. - * @{ - */ -#define ARM_ASID_BITS 8 -#define ARM_ASID_NUM (1 << ARM_ASID_BITS) -#define ARM_ASID_MASK (ARM_ASID_NUM - 1) -/*@}*/ - -/** - * @name ARM level 1 and 2 page table sizes. - * @{ - */ -#define ARM_L1PT_ORDER 14 -#define ARM_L2PT_FINE_ORDER 12 -#define ARM_L2PT_COARSE_ORDER 10 - -#define ARM_L1D_SECTION_ORDER 20 -#define ARM_L1D_SUPERSECTION_ORDER 24 - -#define ARM_L2D_SMALL_ORDER 12 -#define ARM_L2D_LARGE_ORDER 16 - -#define ARM_L1PT_SIZE (1 << ARM_L1PT_ORDER) -#define ARM_L2PT_FINE_SIZE (1 << ARM_L2PT_FINE_ORDER) -#define ARM_L2PT_COARSE_SIZE (1 << ARM_L2PT_COARSE_ORDER) - -#define ARM_L1D_SECTION_SIZE (1 << ARM_L1D_SECTION_ORDER) -#define ARM_L1D_SUPERSECTION_SIZE (1 << ARM_L1D_SUPERSECTION_ORDER) - -#define ARM_L2D_SMALL_SIZE (1 << ARM_L2D_SMALL_ORDER) -#define ARM_L2D_LARGE_SIZE (1 << ARM_L2D_LARGE_ORDER) - -#define ARM_L2PT_COARSE_PER_PAGE (PAGE_SIZE / ARM_L2PT_COARSE_SIZE) - -#define ARM_L1PT_ENTRIES (ARM_L1PT_SIZE / sizeof(ARM_L1D)) -#define ARM_L2PT_FINE_ENTRIES (ARM_L2PT_FINE_SIZE / sizeof(ARM_L2D)) -#define ARM_L2PT_COARSE_ENTRIES (ARM_L2PT_COARSE_SIZE / sizeof(ARM_L2D)) -/*@}*/ - -/** - * @brief Level 1 descriptor type field values. - * @{ - */ -#define ARM_L1D_TYPE_INVALID 0 -#define ARM_L1D_TYPE_COARSE 1 -#define ARM_L1D_TYPE_SECTION 2 -#define ARM_L1D_TYPE_SUPERSECTION 2 -/*@}*/ - -/** - * @name Decomposition of virtual addresses for page table indexing. - * @{ - */ -#define ARM_L1PT_INDX(addr) MVP_EXTRACT_FIELD((addr), 20, 12) -#define ARM_L2PT_COARSE_INDX(addr) MVP_EXTRACT_FIELD((addr), 12, 8) -/*@}*/ - -/** - * @name Mapping from the VA/PA/MA of a LxD entry to its table index. - * @{ - */ -#define ARM_L1D_PTR_INDX(l1dp) MVP_BITS((uint32)(l1dp), 2, ARM_L1PT_ORDER - 1) -#define ARM_L2D_PTR_INDX(l2dp) MVP_BITS((uint32)(l2dp), 2, ARM_L2PT_COARSE_ORDER - 1) -/*@}*/ - -/** - * @name L1D base index <-> MA. - * @{ - */ -#define ARM_L1D_BASE_ADDR(base) ((base) << ARM_L1PT_ORDER) -#define ARM_L1D_ADDR_BASE(addr) ((addr) >> ARM_L1PT_ORDER) -/*@}*/ - -/** - * @brief Which 1 MB section of a 16 MB supersection does the given addr lie in? - */ -#define ARM_SUPER_SECTION_INDEX(addr) MVP_EXTRACT_FIELD((addr), 20, 4) - -/** - * @name L1D entry base <-> either MA or MA of a second-level table. - * @{ - */ -#define ARM_L1D_SUPERSECTION_BASE_ADDR(base) ((base) << ARM_L1D_SUPERSECTION_ORDER) -#define ARM_L1D_SUPERSECTION_ADDR_BASE(addr) ((addr) >> ARM_L1D_SUPERSECTION_ORDER) -#define ARM_L1D_SECTION_BASE_ADDR(base) ((base) << ARM_L1D_SECTION_ORDER) -#define ARM_L1D_SECTION_ADDR_BASE(addr) ((addr) >> ARM_L1D_SECTION_ORDER) -#define ARM_L1D_COARSE_BASE_ADDR(base) ((base) << ARM_L2PT_COARSE_ORDER) -#define ARM_L1D_COARSE_ADDR_BASE(addr) ((addr) >> ARM_L2PT_COARSE_ORDER) -#define ARM_L1D_FINE_BASE_ADDR(base) ((base) << ARM_L2PT_FINE_ORDER) -#define ARM_L1D_FINE_ADDR_BASE(addr) ((addr) >> ARM_L2PT_FINE_ORDER) -/*@}*/ - -/* - * The number of L1 page directory pages the service the entire - * virtual space - */ -#define ARM_L1PT_PAGES (1<<(ARM_L1PT_ORDER - PAGE_ORDER)) - - -/** - * @name Level 2 descriptor type field values. - * @{ - */ -#define ARM_L2D_TYPE_INVALID 0 -#define ARM_L2D_TYPE_LARGE 0 -#define ARM_L2D_TYPE_SMALL 1 -#define ARM_L2D_XTYPE_LARGE 1 -#define ARM_L2D_XTYPE_SMALL 2 -#define ARM_L2D_XTYPE_SMALL_NX 3 -/*@}*/ - -/** - * @name Small/Large L2D (in coarse table) base <-> MA conversion. - * @{ - */ -#define ARM_L2D_LARGE_BASE_ADDR(base) ((base) << ARM_L2D_LARGE_ORDER) -#define ARM_L2D_LARGE_ADDR_BASE(addr) ((addr) >> ARM_L2D_LARGE_ORDER) -#define ARM_L2D_SMALL_BASE_ADDR(base) ((base) << ARM_L2D_SMALL_ORDER) -#define ARM_L2D_SMALL_ADDR_BASE(addr) ((addr) >> ARM_L2D_SMALL_ORDER) - -#define ARM_L2D_SMALL_PAGE_NUMBER(addr) ARM_L2D_SMALL_ADDR_BASE(addr) -#define ARM_L2D_SMALL_PAGE_OFFSET(addr) ((addr) & (PAGE_SIZE - 1)) -/* @}*/ - -/** - * @brief ARM page table descriptor access permissions for the AP field. - * @{ - */ -#define ARM_PERM_NONE 0 -#define ARM_PERM_PRIV_RW 1 -#define ARM_PERM_USER_RO 2 -#define ARM_PERM_USER_RW 3 -/*@}*/ - -/** - * @name Simplified access permission model introduced in ARMv7. - * - * AP[0] is an access flag, AP[2:1] are one of the following. - * - * @{ - */ -#define ARM_SIMPLE_PERM_KERN_RW 0 -#define ARM_SIMPLE_PERM_USER_RW 1 -#define ARM_SIMPLE_PERM_KERN_RO 2 -#define ARM_SIMPLE_PERM_USER_RO 3 - -#define ARM_SIMPLE_PERM_AP_KERN 1 -#define ARM_SIMPLE_PERM_AP_USER 3 - -#define ARM_SIMPLE_PERM_APX_RW 0 -#define ARM_SIMPLE_PERM_APX_RO 1 - -#define ARM_SIMPLE_PERM_AP(x) ((MVP_BIT(x, 0) << 1) | 1) -#define ARM_SIMPLE_PERM_APX(x) MVP_BIT(x, 1) -/*@}*/ - -/** - * @name ARM domains. - * @{ - */ -#define ARM_DOMAINS 16 - -#define ARM_DOMAIN_NOACCESS 0 -#define ARM_DOMAIN_CLIENT 1 -#define ARM_DOMAIN_RESERVED 2 -#define ARM_DOMAIN_MANAGER 3 -/*@}*/ - -#define ARM_DOMAIN_INDEX(dacr,dom) MVP_EXTRACT_FIELD((dacr), 2*(dom), 2) -#define ARM_DOMAIN_ACCESS(dom,access) ((access) << (2*(dom))) - -/* - * Cache-related definitions. - */ -#define ARM_CACHE_LEVELS_MAX 8 -#define ARM_CACHE_LINE_SIZE_MAX 2048 - -#endif /// _MMU_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/mmu_types.h b/arch/arm/mvp/mvpkm/mmu_types.h deleted file mode 100644 index da8a6fa..0000000 --- a/arch/arm/mvp/mvpkm/mmu_types.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MMU-related types. - */ - -#ifndef _MMU_TYPES_H_ -#define _MMU_TYPES_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mmu_defs.h" - -/** - * @brief ARM level 1 page table descriptor. See B3-8 ARM DDI 0406B. - */ -typedef union { - uint32 u; - - struct { - uint32 type : 2; - uint32 xx : 30; - } x; - - struct { - uint32 type : 2; - uint32 sbz1 : 1; - uint32 ns : 1; - uint32 sbz2 : 1; - uint32 domain : 4; - uint32 imp : 1; - uint32 base : 22; - } coarse; - - struct { - uint32 type : 2; - uint32 cb : 2; - uint32 xn : 1; - uint32 domain : 4; - uint32 imp : 1; - uint32 ap : 2; - uint32 tex : 3; - uint32 apx : 1; - uint32 s : 1; - uint32 ng : 1; - uint32 sbz : 1; - uint32 ns : 1; - uint32 base : 12; - } section; - - struct { - uint32 type : 2; - uint32 cb : 2; - uint32 xn : 1; - uint32 xbase2 : 4; - uint32 imp : 1; - uint32 ap : 2; - uint32 tex : 3; - uint32 apx : 1; - uint32 s : 1; - uint32 ng : 1; - uint32 sbo : 1; - uint32 ns : 1; - uint32 xbase1 : 4; - uint32 base : 8; - } supersection; -} ARM_L1D; - -/** - * @brief ARM level 2 page table descriptor. See B3-10 ARM DDI 0406B. - */ -typedef union { - uint32 u; - - struct { - uint32 type : 2; - uint32 cb : 2; - uint32 xx : 28; - } x; - - struct { - uint32 type : 2; - uint32 cb : 2; - uint32 ap : 2; - uint32 sbz : 3; - uint32 apx : 1; - uint32 s : 1; - uint32 ng : 1; - uint32 tex : 3; - uint32 xn : 1; - uint32 base : 16; - } large; - - struct { - uint32 xn : 1; - uint32 type : 1; - uint32 cb : 2; - uint32 ap : 2; - uint32 tex : 3; - uint32 apx : 1; - uint32 s : 1; - uint32 ng : 1; - uint32 base : 20; - } small; -} ARM_L2D; - -/** - * @brief Get the simplified access permissions from a small L2 descriptor. - * - * @param l2D value of L2 descriptor. - * - * @return Simplified access permissions. - */ -static inline uint8 -ARM_L2DSimpleAP(ARM_L2D l2D) -{ - ASSERT(l2D.small.type == ARM_L2D_TYPE_SMALL); - return (l2D.small.apx << 1) | (l2D.small.ap >> 1); -} - -/** - * @brief Permissions for a page - intermediate format. - */ -typedef struct { - uint8 ap : 2; - uint8 apx : 1; - uint8 xn : 1; -} ARM_AccessPerms; - -/** - * @brief ARM domain (0-15). - */ -typedef uint8 ARM_Domain; - -/** - * @brief ARM Domain Access Control Register, see B4.9.4 ARM DDI 0100I. - */ -typedef uint32 ARM_DACR; - -/** - * @brief ARM address space identifier. - * 8-bits with an "invalid ASID" value - * representation. - */ -typedef uint32 ARM_ASID; - -#define ARM_INVALID_ASID ((uint32)(-1)) - -/** - * @brief Page shareability property. - * - * LPAE encoding, see p8 ARM PRD03-GENC-008469 11.0. - */ -typedef enum { - ARM_SHARE_ATTR_NONE, - ARM_SHARE_ATTR_RESERVED, - ARM_SHARE_ATTR_OUTER, - ARM_SHARE_ATTR_INNER, -} PACKED ARM_ShareAttr; - -/** - * @brief Page cacheability property (TEX Remap disabled). - * - * ARM C/B bits, see B4.4.1 ARM DDI 0100I. - */ -typedef enum { - ARM_CB_UNBUFFERED = 0, - ARM_CB_UNCACHED = 1, - ARM_CB_WRITETHROUGH = 2, - ARM_CB_WRITEBACK = 3 -} PACKED ARM_CB; - -/** - * @brief Normal page cacheability property (TEX Remap enabled). - * - * NMRR encoding, see B3-146 ARM DDI 0406B. - */ -typedef enum { - ARM_CACHE_ATTR_NORMAL_NONE, - ARM_CACHE_ATTR_NORMAL_WB_WALLOC, - ARM_CACHE_ATTR_NORMAL_WT, - ARM_CACHE_ATTR_NORMAL_WB -} PACKED ARM_CacheAttrNormal; - -/** - * @brief Normal page memory attributes. - * - * Captures the general case of distinct inner/outer cacheability/shareability. - * See A3-30 ARM DDI 0406B for a discussion of shareability domains and - * cacheability attributes. - */ -typedef struct { - ARM_ShareAttr share; - ARM_CacheAttrNormal innerCache; - ARM_CacheAttrNormal outerCache; -} ARM_MemAttrNormal; - -#endif /// _MMU_TYPES_H_ diff --git a/arch/arm/mvp/mvpkm/montimer_kernel.c b/arch/arm/mvp/mvpkm/montimer_kernel.c deleted file mode 100644 index e2f8ef8..0000000 --- a/arch/arm/mvp/mvpkm/montimer_kernel.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MVP host kernel implementation of monitor timers - * - * The monitor sends requests that are simply a 64-bit absolute time that it - * wants a reply. If it changes its mind, it simply sends a different 64-bit - * absolute time. It is tolerant of us replying too soon, so if we miss the - * update to a later time, it doesn't matter, the monitor will re-send the - * request for the later time. The only time we should miss an update to a - * sooner time is when we are about to send the reply to the old time anyway, - * in which case the monitor sees a reply as quickly as we can generate them, - * so no harm there either. - */ - -#include -#include - -#include "mvp.h" -#include "mvp_timer.h" -#include "actions.h" -#include "mvpkm_kernel.h" - -/** - * @brief Linux timer callback - * @param timer The linux timer raised - * @return Status to not restart the timer - */ -static enum hrtimer_restart -MonitorTimerCB(struct hrtimer *timer) -{ - MvpkmVM *vm = container_of(timer, MvpkmVM, monTimer.timer); - Mvpkm_WakeGuest(vm, ACTION_TIMER); - return HRTIMER_NORESTART; -} - -/** - * @brief Initialize vm associated timer - * @param vm which virtual machine we're running - */ -void -MonitorTimer_Setup(MvpkmVM *vm) -{ - MonTimer *monTimer = &vm->monTimer; - monTimer->vm = vm; - - hrtimer_init(&monTimer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - monTimer->timer.function = MonitorTimerCB; -} - -/** - * @brief New timer request from monitor - * @param monTimer Monitor timer - * @param when64 Timer target value - */ -void -MonitorTimer_Request(MonTimer *monTimer, uint64 when64) -{ - if (when64) { - ktime_t kt; - - /* - * Simple conversion, assuming RATE64 is 1e+9 - */ - kt = ns_to_ktime(when64); - ASSERT_ON_COMPILE(MVP_TIMER_RATE64 == 1000000000); - - /* - * Start the timer. If it was already active, it will remove - * the previous expiration time. Linux handles correctly timer - * with deadline in the past, and forces a safety minimal delta - * for closer timer deadlines. - */ - hrtimer_start(&monTimer->timer, kt, HRTIMER_MODE_ABS); - } else { - /* - * Cancel a pending request. If there is none, this will do nothing. - * If it's too late, monitor tolerance will forgive us. - */ - hrtimer_cancel(&monTimer->timer); - } -} diff --git a/arch/arm/mvp/mvpkm/montimer_kernel.h b/arch/arm/mvp/mvpkm/montimer_kernel.h deleted file mode 100644 index 6817a83..0000000 --- a/arch/arm/mvp/mvpkm/montimer_kernel.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The monitor-kernel socket interface kernel-only definitions. - */ - -#ifndef _MONITOR_TIMER_KERNEL_H -#define _MONITOR_TIMER_KERNEL_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include - -/** - * @brief Monitor Timer structure - */ -typedef struct { - struct MvpkmVM *vm; ///< Associated vm - struct hrtimer timer; ///< Linux timer -} MonTimer; - -void MonitorTimer_Setup(struct MvpkmVM *vm); -void MonitorTimer_Request(MonTimer *monTimer, uint64 when64); - -#endif diff --git a/arch/arm/mvp/mvpkm/monva_common.h b/arch/arm/mvp/mvpkm/monva_common.h deleted file mode 100644 index de3dd1a..0000000 --- a/arch/arm/mvp/mvpkm/monva_common.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Constant definitions that describing the monitor memory layout - * (common to both LPV and VE monitors). - * - */ - -#ifndef _MONVA_COMMON_H_ -#define _MONVA_COMMON_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mmu_defs.h" -#include "mmu_types.h" - -/* - * The monitor occupies a hole in the guest virtual address space. - * The following macros define that hole. - */ - -#define MONITOR_VA_START ((MVA)0xE8000000) -#define MONITOR_VA_LEN 0x03000000 - -/* - * Worldswitch page gets mapped right after the stack guard. - */ -#define MONITOR_VA_WORLDSWITCH \ - ((MVA)(MONITOR_VA_START + 3 * PAGE_SIZE)) - -#define MONITOR_VA_WORLDSWITCH_CODE \ - (MONITOR_VA_WORLDSWITCH + PAGE_SIZE) - -#define MONITOR_VA_UART \ - (MONITOR_VA_WORLDSWITCH_CODE + PAGE_SIZE) - -/** - * @brief Type of physmem region mapping that we want the VMX to know about. - * Helps to identify Guest page allocations. - */ -typedef enum { - MEMREGION_MAINMEM = 1, - MEMREGION_MODULE = 2, - MEMREGION_WSP = 3, - MEMREGION_MONITOR_MISC = 4, - MEMREGION_DEFAULT = 0 -} PACKED PhysMem_RegionType; - -typedef struct MonVA { /* Note that this struct is VE only */ - MA l2BaseMA; ///< MA of monitor L2 page table page - MVA excVec; ///< Monitor exception vector virtual address -} MonVA; - -/** - * @brief Monitor VA mapping type, device or memory. - * - * These values are used to index HMAIR0 in the VE monitor - do not change - * without making the required update to HMAIR0. - */ -typedef enum { - MVA_MEMORY = 0, - MVA_DEVICE = 1 -} MVAType; - -/** - * @name Monitor types, used in VMX, Mvpkm and monitors. - * - * This is not a C enumeration, as we may want to use the values in CPP macros. - * - * @{ - */ -#define MONITOR_TYPE_LPV 0 -#define MONITOR_TYPE_VE 1 -#define MONITOR_TYPE_UNKNOWN 0xf - -typedef uint32 MonitorType; -/*@}*/ - -#endif diff --git a/arch/arm/mvp/mvpkm/mutex.h b/arch/arm/mvp/mvpkm/mutex.h deleted file mode 100644 index 30de97d..0000000 --- a/arch/arm/mvp/mvpkm/mutex.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Common mutex definitions. - */ - -#ifndef _MUTEX_H -#define _MUTEX_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define MUTEX_CVAR_MAX 2 ///< maximum number of condition variables supported - ///< on a given mutex - -typedef enum MutexMode MutexMode; -typedef struct HKWaitQ HKWaitQ; -typedef struct Mutex Mutex; - -/** - * @brief modes for locking - */ -enum MutexMode { - MutexModeSH = 1, ///< minimum value that can be saved in low - ///< 16 bits of 'state', ie, it won't allow - ///< any other EXs in there without overflowing. - ///< it also will block if there are already - ///< 0xFFFF other shared accesses, but it should - ///< be of little consequence. - - MutexModeEX = 0xFFFF ///< maximum value that can be saved in low - ///< 16 bits of 'state', ie, it won't allow - ///< any other EXs or SHs in there without - ///< overflowing, thus causing a block. -}; - -#include "atomic.h" - -typedef union Mutex_State { - uint32 state; ///< for atomic setting/reading - struct { - uint16 mode; ///< the sum of mode values of MutexMode - uint16 blck; ///< The number of threads blocked - }; -} Mutex_State; - -/** - * @brief shareable mutex struct. - */ -struct Mutex { - HKVA mtxHKVA; ///< mutex's host kernel virtual address - AtmUInt32 state; ///< low 16 bits: # of granted shared accessors - ///< or FFFF if granted exclusive - ///< high 16 bits: # of blocked threads - AtmUInt32 waiters; ///< number of threads on all condWaitQs - ///< ... increment only with mutex locked EX - ///< ... decrement any time - AtmUInt32 blocked; ///< number times blocked (stats only) - HKVA lockWaitQ; ///< threads blocked for mutex to be unlocked - HKVA cvarWaitQs[MUTEX_CVAR_MAX]; ///< condition variables - /* - * Padding to keep binary compatibility @see{MVP-1876} - * These padding bytes can be used for debugging. - */ - int line; - int lineUnl; - uint32 pad3; - uint32 pad4; - uint32 pad5; - uint32 pad6; -}; - -#define Mutex_Lock(a, b) Mutex_LockLine(a, b, __FILE__, __LINE__) -#define Mutex_Unlock(a, b) Mutex_UnlockLine(a, b, __LINE__) -#define Mutex_UnlSleep(a, b, c) Mutex_UnlSleepLine(a, b, c, __FILE__, __LINE__) -#define Mutex_UnlSleepTest(a, b, c, d, e) Mutex_UnlSleepTestLine(a, b, c, d, e, __FILE__, __LINE__) -int Mutex_LockLine(Mutex *mutex, MutexMode mode, const char *file, int line); -void Mutex_UnlockLine(Mutex *mutex, MutexMode mode, int line); -int Mutex_UnlSleepLine(Mutex *mutex, MutexMode mode, uint32 cvi, const char *file, int line); -int Mutex_UnlSleepTestLine(Mutex *mutex, MutexMode mode, uint32 cvi, AtmUInt32 *test, uint32 mask, const char *file, int line); -void Mutex_UnlWake(Mutex *mutex, MutexMode mode, uint32 cvi, _Bool all); - -#endif diff --git a/arch/arm/mvp/mvpkm/mutex_kernel.c b/arch/arm/mvp/mvpkm/mutex_kernel.c deleted file mode 100644 index 7b76bfcf..0000000 --- a/arch/arm/mvp/mvpkm/mutex_kernel.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The host kernel mutex functions. These mutexes can be located in - * shared address space with the monitor. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "mvp.h" - -#include "arm_inline.h" -#include "coproc_defs.h" -#include "mutex_kernel.h" - -#define POLL_IN_PROGRESS_FLAG (1<<(30-MUTEX_CVAR_MAX)) - -#define INITWAITQ(waitQ) do { \ - init_waitqueue_head((wait_queue_head_t *)(waitQ)); \ -} while (0) - -#define WAKEUPALL(waitQ) do { \ - wake_up_all((wait_queue_head_t *)(waitQ)); \ -} while (0) - -#define WAKEUPONE(waitQ) do { \ - wake_up((wait_queue_head_t *)(waitQ)); \ -} while (0) - -/** - * @brief initialize mutex - * @param[in,out] mutex mutex to initialize - */ -void -Mutex_Init(Mutex *mutex) -{ - wait_queue_head_t *wq; - int i; - - wq = kcalloc(MUTEX_CVAR_MAX + 1, sizeof(wait_queue_head_t), 0); - FATAL_IF(wq == NULL); - - memset(mutex, 0, sizeof *mutex); - mutex->mtxHKVA = (HKVA)mutex; - mutex->lockWaitQ = (HKVA)&wq[0]; - INITWAITQ(mutex->lockWaitQ); - for (i = 0; i < MUTEX_CVAR_MAX; i ++) { - mutex->cvarWaitQs[i] = (HKVA)&wq[i + 1]; - INITWAITQ(mutex->cvarWaitQs[i]); - } -} - -/** - * @brief Check if it is ok to sleep - * @param file the file of the caller code - * @param line the line number of the caller code - */ -static void -MutexCheckSleep(const char *file, int line) -{ -#ifdef MVP_DEVEL - static unsigned long prev_jiffy; /* ratelimiting: 1/s */ - -#ifdef CONFIG_PREEMPT - if (preemptible() && !irqs_disabled()) { - return; - } -#else - if (!irqs_disabled()) { - return; - } -#endif - if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) { - return; - } - prev_jiffy = jiffies; - printk(KERN_ERR - "BUG: sleeping function called from invalid context at %s:%d\n", - file, line); - printk(KERN_ERR - "irqs_disabled(): %d, preemtible(): %d, pid: %d, name: %s\n", - irqs_disabled(), - preemptible(), - current->pid, current->comm); - dump_stack(); -#endif -} - -/** - * @brief destroy mutex - * @param[in,out] mutex mutex to destroy - */ -void -Mutex_Destroy(Mutex *mutex) -{ - kfree((void*)mutex->lockWaitQ); -} - -/** - * @brief Lock the mutex. Also does a data barrier after locking so the - * locking is complete before any shared data is accessed. - * @param[in,out] mutex which mutex to lock - * @param mode mutex lock mode - * @param file the file of the caller code - * @param line the line number of the code that called this function - * @return rc = 0: mutex now locked by caller
- * < 0: interrupted - */ -int -Mutex_LockLine(Mutex *mutex, MutexMode mode, const char *file, int line) -{ - Mutex_State newState, oldState; - - MutexCheckSleep(file, line); - - /* - * If uncontended, just set new lock state and return success status. - * If contended, mark state saying there is a waiting thread to wake. - */ - do { -lock_start: - /* - * Get current state and calculate what new state would be. - * New state adds 1 for shared and 0xFFFF for exclusive. - * If the 16 bit field overflows, there is contention. - */ - oldState.state = ATOMIC_GETO(mutex->state); - newState.mode = oldState.mode + mode; - newState.blck = oldState.blck; - - /* - * So we are saying there is no contention if new state - * indicates no overflow. - * - * On fairness: The test here allows a new-comer thread to grab - * the lock even if there is a blocked thread. For example 2 - * threads repeatedly obtaining shared access can starve a third - * wishing to obtain an exclusive lock. Currently this is only a - * hypothetical situation as mksck use exclusive lock only and - * the code never has more than 2 threads using the same mutex. - */ - if ((uint32)newState.mode >= (uint32)mode) { - if (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)) { - goto lock_start; - } - DMB(); - mutex->line = line; - mutex->lineUnl = -1; - return 0; - } - - /* - * There is contention, so increment the number of blocking threads. - */ - newState.mode = oldState.mode; - newState.blck = oldState.blck + 1; - } while (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)); - - /* - * Statistics... - */ - ATOMIC_ADDV(mutex->blocked, 1); - - /* - * Mutex is contended, state has been updated to say there is a blocking - * thread. - * - * So now we block till someone wakes us up. - */ - do { - DEFINE_WAIT(waiter); - - /* - * This will make sure we catch any wakes done after we check the lock - * state again. - */ - prepare_to_wait((wait_queue_head_t *)mutex->lockWaitQ, - &waiter, - TASK_INTERRUPTIBLE); - - /* - * Now that we will catch wakes, check the lock state again. If now - * uncontended, mark it locked, abandon the wait and return success. - */ - -set_new_state: - /* - * Same as the original check for contention above, except that we - * must decrement the number of waiting threads by one - * if we are successful in locking the mutex. - */ - oldState.state = ATOMIC_GETO(mutex->state); - newState.mode = oldState.mode + mode; - newState.blck = oldState.blck - 1; - ASSERT(oldState.blck); - - if ((uint32)newState.mode >= (uint32)mode) { - if (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)) { - goto set_new_state; - } - /* - * Mutex is no longer contended and we were able to lock it. - */ - finish_wait((wait_queue_head_t *)mutex->lockWaitQ, &waiter); - DMB(); - mutex->line = line; - mutex->lineUnl = -1; - return 0; - } - - /* - * Wait for a wake that happens any time after prepare_to_wait() - * returned. - */ - WARN(!schedule_timeout(10*HZ), "Mutex_Lock: soft lockup - stuck for 10s!\n"); - finish_wait((wait_queue_head_t *)mutex->lockWaitQ, &waiter); - } while (!signal_pending(current)); - - /* - * We aren't waiting anymore, so decrement the number of waiting threads. - */ - do { - oldState.state = ATOMIC_GETO(mutex->state); - newState.mode = oldState.mode; - newState.blck = oldState.blck - 1; - - ASSERT(oldState.blck); - - } while (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)); - - return -ERESTARTSYS; -} - - -/** - * @brief Unlock the mutex. Also does a data barrier before unlocking so any - * modifications made before the lock gets released will be completed - * before the lock is released. - * @param mutex as passed to Mutex_Lock() - * @param mode as passed to Mutex_Lock() - * @param line the line number of the code that called this function - */ -void -Mutex_UnlockLine(Mutex *mutex, MutexMode mode, int line) -{ - Mutex_State newState, oldState; - - DMB(); - do { - oldState.state = ATOMIC_GETO(mutex->state); - newState.mode = oldState.mode - mode; - newState.blck = oldState.blck; - mutex->lineUnl = line; - - ASSERT(oldState.mode >= mode); - } while (!ATOMIC_SETIF(mutex->state, newState.state, oldState.state)); - - /* - * If another thread was blocked, then wake it up. - */ - if (oldState.blck) { - if (mode == MutexModeSH) { - WAKEUPONE(mutex->lockWaitQ); - } else { - WAKEUPALL(mutex->lockWaitQ); - } - } -} - - -/** - * @brief Unlock the mutex and sleep. Also does a data barrier before - * unlocking so any modifications made before the lock gets released - * will be completed before the lock is released. - * @param mutex as passed to Mutex_Lock() - * @param mode as passed to Mutex_Lock() - * @param cvi which condition variable to sleep on - * @param file the file of the caller code - * @param line the line number of the caller code - * @return rc = 0: successfully waited
- * < 0: error waiting - */ -int -Mutex_UnlSleepLine(Mutex *mutex, MutexMode mode, uint32 cvi, const char *file, int line) -{ - return Mutex_UnlSleepTestLine(mutex, mode, cvi, NULL, 0, file, line); -} - -/** - * @brief Unlock the mutex and sleep. Also does a data barrier before - * unlocking so any modifications made before the lock gets released - * will be completed before the lock is released. - * @param mutex as passed to Mutex_Lock() - * @param mode as passed to Mutex_Lock() - * @param cvi which condition variable to sleep on - * @param test sleep only if null or pointed atomic value mismatches mask - * @param mask bitfield to check test against before sleeping - * @param file the file of the caller code - * @param line the line number of the caller code - * @return rc = 0: successfully waited
- * < 0: error waiting - */ -int -Mutex_UnlSleepTestLine(Mutex *mutex, MutexMode mode, uint32 cvi, AtmUInt32 *test, uint32 mask, const char *file, int line) -{ - DEFINE_WAIT(waiter); - - MutexCheckSleep(file, line); - - ASSERT(cvi < MUTEX_CVAR_MAX); - - /* - * Tell anyone who might try to wake us that they need to actually call - * WAKEUP***(). - */ - ATOMIC_ADDV(mutex->waiters, 1); - - /* - * Be sure to catch any wake that comes along just after we unlock the mutex - * but before we call schedule(). - */ - prepare_to_wait_exclusive((wait_queue_head_t *)mutex->cvarWaitQs[cvi], - &waiter, - TASK_INTERRUPTIBLE); - - /* - * Release the mutex, someone can wake us up now. - * They will see mutex->waiters non-zero so will actually do the wake. - */ - Mutex_Unlock(mutex, mode); - - /* - * Wait to be woken or interrupted. - */ - if (test == NULL || (ATOMIC_GETO(*test) & mask) == 0) { - schedule(); - } - finish_wait((wait_queue_head_t *)mutex->cvarWaitQs[cvi], &waiter); - - /* - * Done waiting, don't need a wake any more. - */ - ATOMIC_SUBV(mutex->waiters, 1); - - /* - * If interrupted, return error status. - */ - if (signal_pending(current)) { - return -ERESTARTSYS; - } - - /* - * Wait completed, return success status. - */ - return 0; -} - - -/** - * @brief Unlock the mutex and prepare to sleep on a kernel polling table - * given as anonymous parameters for poll_wait - * @param mutex as passed to Mutex_Lock() - * @param mode as passed to Mutex_Lock() - * @param cvi which condition variable to sleep on - * @param filp which file to poll_wait upon - * @param wait which poll_table to poll_wait upon - */ -void -Mutex_UnlPoll(Mutex *mutex, MutexMode mode, uint32 cvi, void *filp, void *wait) -{ - ASSERT(cvi < MUTEX_CVAR_MAX); - - /* poll_wait is done with mutex locked to prevent any wake that comes and - * defer them just after we unlock the mutex but before kernel polling - * tables are used - * Note that the kernel is probably avoiding an exclusive wait in that case - * and also increments the usage for the file given in filp - */ - poll_wait(filp, (wait_queue_head_t *)mutex->cvarWaitQs[cvi], wait); - - /* - * Tell anyone who might try to wake us that they need to actually call - * WAKEUP***(). This is done in putting ourselves in a "noisy" mode since - * there is no guaranty that we would really sleep, or if we would be - * wakening the sleeping thread with that socket or condition. This is - * done using a POLL_IN_PROGRESS_FLAG, but unfortunately it has to be - * a per-cvi flag, in case we would poll independently on different cvi - */ - DMB(); - ATOMIC_ORO(mutex->waiters, (POLL_IN_PROGRESS_FLAG << cvi)); - - /* - * Release the mutex, someone can wake us up now. - * They will see mutex->waiters non-zero so will actually do the wake. - */ - Mutex_Unlock(mutex, mode); -} - - -/** - * @brief Unlock the semaphore and wake sleeping threads. Also does a data - * barrier before unlocking so any modifications made before the lock - * gets released will be completed before the lock is released. - * @param mutex as passed to Mutex_Lock() - * @param mode as passed to Mutex_Lock() - * @param cvi which condition variable to signal - * @param all false: wake a single thread
- * true: wake all threads - */ -void -Mutex_UnlWake(Mutex *mutex, MutexMode mode, uint32 cvi, _Bool all) -{ - Mutex_Unlock(mutex, mode); - Mutex_CondSig(mutex, cvi, all); -} - - -/** - * @brief Signal condition variable, ie, wake up anyone waiting. - * @param mutex mutex that holds the condition variable - * @param cvi which condition variable to signal - * @param all false: wake a single thread
- * true: wake all threads - */ -void -Mutex_CondSig(Mutex *mutex, uint32 cvi, _Bool all) -{ - uint32 waiters; - - ASSERT(cvi < MUTEX_CVAR_MAX); - - waiters = ATOMIC_GETO(mutex->waiters); - if (waiters != 0) { - /* Cleanup the effects of Mutex_UnlPoll() but only when it is SMP safe, - * considering that atomic and wakeup operations should also do memory - * barriers accordingly. This is mandatory otherwise rare SMP races are - * even possible, since Mutex_CondSig is called with the associated mutex - * unlocked, and that does not prevent from select() to run parallel ! - */ - if ((waiters >= POLL_IN_PROGRESS_FLAG) && - !waitqueue_active((wait_queue_head_t *)mutex->cvarWaitQs[cvi])) { - ATOMIC_ANDO(mutex->waiters, ~(POLL_IN_PROGRESS_FLAG << cvi)); - } - DMB(); - - if (all) { - WAKEUPALL(mutex->cvarWaitQs[cvi]); - } else { - WAKEUPONE(mutex->cvarWaitQs[cvi]); - } - } -} diff --git a/arch/arm/mvp/mvpkm/mutex_kernel.h b/arch/arm/mvp/mvpkm/mutex_kernel.h deleted file mode 100644 index 4bdf0e1..0000000 --- a/arch/arm/mvp/mvpkm/mutex_kernel.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The host kernel mutex definitions. - */ - -#ifndef _MUTEX_KERNEL_H -#define _MUTEX_KERNEL_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mutex.h" - -void Mutex_Init(Mutex *mutex); -void Mutex_Destroy(Mutex *mutex); -void Mutex_CondSig(Mutex *mutex, uint32 cvi, _Bool all); -void Mutex_UnlPoll(Mutex *mutex, MutexMode mode, uint32 cvi, void *filp, void *wait); - -#endif diff --git a/arch/arm/mvp/mvpkm/mvp.h b/arch/arm/mvp/mvpkm/mvp.h deleted file mode 100644 index e21b8a0..0000000 --- a/arch/arm/mvp/mvpkm/mvp.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief top-level include for all basic includes. - * This file should not define anything of its own. - */ - -#ifndef _MVP_H -#define _MVP_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "mvp_compiler.h" -#include "utils.h" -#include "mvp_assert.h" -#include "mvp_types.h" -#include "platdefx.h" - -#endif diff --git a/arch/arm/mvp/mvpkm/mvp_assert.h b/arch/arm/mvp/mvpkm/mvp_assert.h deleted file mode 100644 index 9ee6fc0..0000000 --- a/arch/arm/mvp/mvpkm/mvp_assert.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief ASSERT() and related macros. - */ - -#ifndef _MVP_ASSERT_H -#define _MVP_ASSERT_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define ASSERT(_x) ASSERT_BUG((_x),0) - -#ifndef NDEBUG -#define ASSERT_BUG(_x,_tkt) do { \ - if (UNLIKELY(!(_x))) { \ - FatalError(__FILE__, __LINE__, FECodeAssert, _tkt, NULL); \ - } \ -} while (0) - -#define ASSERTF(_x, ...) do { \ - if (UNLIKELY(!(_x))) { \ - FatalError(__FILE__, \ - __LINE__, \ - FECodeAssert, \ - 0, \ - __VA_ARGS__); \ - } \ -} while (0) -#else - -#define ASSERT_BUG(_x,_tkt) (void)sizeof((int)(_x)) -#define ASSERTF(_x, ...) ASSERT_BUG(_x, 0) - -#endif - -/* - * Compile-time assertions. - * - * ASSERT_ON_COMPILE does not use the common - * switch (0) { case 0: case (e): ; } trick because some compilers (e.g. MSVC) - * generate code for it. - * - * The implementation uses both enum and typedef because the typedef alone is - * insufficient; gcc allows arrays to be declared with non-constant expressions - * (even in typedefs, where it makes no sense). - */ -#ifdef __COVERITY__ -#define ASSERT_ON_COMPILE(e) ASSERT(e) -#else -#define ASSERT_ON_COMPILE(e) \ - do { \ - enum { AssertOnCompileMisused = ((e) ? 1 : -1) }; \ - typedef char AssertOnCompileFailed[AssertOnCompileMisused]; \ - } while (0) -#endif - -/* - * To put an ASSERT_ON_COMPILE() outside a function, wrap it - * in MY_ASSERTS(). The first parameter must be unique in - * each .c file where it appears. For example, - * - * MY_ASSERTS(FS3_INT, - * ASSERT_ON_COMPILE(sizeof(FS3_DiskLock) == 128); - * ASSERT_ON_COMPILE(sizeof(FS3_DiskLockReserved) == DISK_BLOCK_SIZE); - * ASSERT_ON_COMPILE(sizeof(FS3_DiskBlock) == DISK_BLOCK_SIZE); - * ASSERT_ON_COMPILE(sizeof(Hardware_DMIUUID) == 16); - * ) - * - * Caution: ASSERT() within MY_ASSERTS() is silently ignored. - * The same goes for anything else not evaluated at compile time. - */ - -#define MY_ASSERTS(name, assertions) \ - static inline void name(void) { \ - assertions \ - } - -#define KNOWN_BUG(_tkt) - -#define NOT_IMPLEMENTED() NOT_IMPLEMENTED_JIRA(0) -#define NOT_IMPLEMENTED_JIRA(_tkt,...) FatalError(__FILE__, __LINE__, FECodeNI, _tkt, NULL) - -#define NOT_IMPLEMENTED_IF(_x) NOT_IMPLEMENTED_IF_JIRA((_x),0) -#define NOT_IMPLEMENTED_IF_JIRA(_x,_tkt,...) do { if (UNLIKELY(_x)) NOT_IMPLEMENTED_JIRA(_tkt); } while (0) -/* - * All sites tagged with this are @knownjira{MVP-1855}. - */ -#define NOT_IMPLEMENTEDF(...) FatalError(__FILE__, __LINE__, FECodeNI, 0, __VA_ARGS__) - -#define NOT_REACHED() FatalError(__FILE__, __LINE__, FECodeNR, 0, NULL) - -#include "fatalerror.h" -#include "nottested.h" - -#endif diff --git a/arch/arm/mvp/mvpkm/mvp_balloon.h b/arch/arm/mvp/mvpkm/mvp_balloon.h deleted file mode 100644 index 9df5669..0000000 --- a/arch/arm/mvp/mvpkm/mvp_balloon.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Common guest/host balloon state machine. - */ -#ifndef _MVP_BALLOON_H -#define _MVP_BALLOON_H - -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_MODULE -#include "include_check.h" - -/** - * @brief Balloon watchdog timeout (in seconds). - * - * If we don't hear back from the guest balloon driver in this amount of time, - * we terminate the guest. - * - * This can sound arbitrary long but we need to deal with checkpointing. The - * watchdog goal is only to not let not-responding VM running for ages. - */ -#define BALLOON_WATCHDOG_TIMEOUT_SECS 90 - -/** - * @brief MVP_BALLOON_GET_DELTA return. - */ -typedef union { - struct { - int32 delta : 21; ///< Number/direction balloon adjustment in pages. - }; - uint32 u; -} Balloon_GetDeltaRet; - -/** - * @name Guest settings for lowmemorykiller oom_adj and minfree thresholds, as reflected in - * the guest's /sys/module/lowmemorykiller/parameters/{minfree,adj}. - * - * @{ - */ - -/** - * @brief Android oom_adj levels for the various thresholds. - */ -typedef enum { - BALLOON_ANDROID_GUEST_OOM_ADJ_FOREGROUND_APP = 0, - BALLOON_ANDROID_GUEST_OOM_ADJ_VISIBLE_APP = 1, - BALLOON_ANDROID_GUEST_OOM_ADJ_SECONDARY_SERVER = 2, - BALLOON_ANDROID_GUEST_OOM_ADJ_BACKUP_APP = 2, - BALLOON_ANDROID_GUEST_OOM_ADJ_HOME_APP = 4, - BALLOON_ANDROID_GUEST_OOM_ADJ_HIDDEN_APP_MIN = 7, - BALLOON_ANDROID_GUEST_OOM_ADJ_CONTENT_PROVIDER = 14, - BALLOON_ANDROID_GUEST_OOM_ADJ_EMPTY_APP = 15 -} Balloon_AndroidGuestOOMAdj; - -/** - * @brief Android low memory killer thresholds (in pages). - */ -typedef enum { - BALLOON_ANDROID_GUEST_MIN_FREE_FOREGROUND_APP_PAGES = 1536, - BALLOON_ANDROID_GUEST_MIN_FREE_VISIBLE_APP_PAGES = 2048, - BALLOON_ANDROID_GUEST_MIN_FREE_SECONDARY_SERVER_PAGES = 4096, - BALLOON_ANDROID_GUEST_MIN_FREE_BACKUP_APP_PAGES = 4096, - BALLOON_ANDROID_GUEST_MIN_FREE_HOME_APP_PAGES = 4096, - BALLOON_ANDROID_GUEST_MIN_FREE_HIDDEN_APP_PAGES = 5120, - BALLOON_ANDROID_GUEST_MIN_FREE_CONTENT_PROVIDER_PAGES = 5632, - BALLOON_ANDROID_GUEST_MIN_FREE_EMPTY_APP_MEM_PAGES = 6144 -} Balloon_AndroidGuestMinFreePages; - -/* @} */ -/** - * @brief Calculate distance to the point at which Android will terminate - * processes. - * - * In the balloon policy we strive to maintain the low memory killer minfree - * value (e.g. max(freePages, filePages)) above the threshold for terminating - * empty apps (as per the Android low memory killer's logic). Here we measure - * the number of pages we have buffering us from this point. - * - * We chose the empty app threshold instead instead of the home app threshold, - * the threshold we ultimately want to avoid crossing for two reasons: - * - We want to avoid any error introduced by the use of max(free, file) when - * between the two thresholds from interfering with the errorBackground term - * in the balloon policy. If we instead measure the distance to the home app - * threshold, we can get into the situation that even when both sides have - * balanced background pages and the same low memory distance, different - * free/file ratios in the two worlds introduces a further bias. - * - It's helpful in avoiding extreme situations where the balloon won't be able - * to adapt quickly to leave a buffer. With empty app minfree as the target, - * when background pages drops to zero, and both worlds are below the empty - * app minfree target, the balloon will stop adjusting, leaving each world to - * fend for itself. At this point, the worlds have a maximum of 8192 pages - * (using the above logic) until they start killing services and foreground - * apps, which seems like a reasonable buffer to have in place. Another way of - * putting it is that at this point, we are unsure that rebalancing the - * balloon won't harm the side it balances against by eating into its buffer. - * - * We assume that normally filePages only decreases as a result of freePages - * being close to zero, when vmscan reclaiming kicks in. Based on this, - * there are two cases when computing the distance. - * - * - filePages >= emptyAppPages: - * freePages + filePages - emptyAppPages - * - filePages < emptyAppPages: - * MAX(0, freePages - emptyAppPages) - * - * @param freePages number of free pages. - * @param filePages number of pages in the page cache. - * @param emptyAppPages number of free/file pages at which the - * lowmemorykiller will start killing empty apps. - * - * @return Low memory distance measure (in pages). - */ -static inline uint32 -Balloon_LowMemDistance(uint32 freePages, uint32 filePages, uint32 emptyAppPages) -{ - return filePages >= emptyAppPages ? - freePages + (filePages - emptyAppPages) : - (freePages > emptyAppPages ? freePages - emptyAppPages : 0); -} - -#ifdef __KERNEL__ -/** - * @brief Obtain approximation of # anonymous pages belonging to Android - * background processes. - * - * Used to inform balloon policy. Note that this is a coarse approximation only, - * since we use RSS. More precise accounting is possible but potentially costly - * as it's not available directly in the task struct. - * - * @param hiddenAppOOMAdj minimum oom_adj for hidden apps. - * - * @return sum of empty, content provider and hidden app anon resident pages. - */ -static uint32 -Balloon_AndroidBackgroundPages(uint32 minHiddenAppOOMAdj) -{ - uint32 backgroundPages = 0, nonBackgroundPages = 0; - struct task_struct *t; - - /* - * Traverse the tasklist to replicate the behavior of the Android low memory - * killer. - */ - rcu_read_lock(); - - for_each_process(t) { - int oom_adj = 0; - - task_lock(t); - - if (t->signal == NULL) { - task_unlock(t); - continue; - } else { - oom_adj = t->signal->oom_adj; - } - - if (t->mm != NULL) { -#ifdef BALLOON_DEBUG_PRINT_ANDROID_PAGES - printk("Balloon_AndroidBackgroundPages: %d %d %s\n", - oom_adj, - (int)get_mm_counter(t->mm, MM_ANONPAGES), - t->comm); -#endif - - if (oom_adj >= (int)minHiddenAppOOMAdj) { - /* - * Unlike the Android low memory killer, we only consider anonymous - * memory here, since we already account for file pages in the - * balloon policy using global_page_state(NR_FILE_PAGES). - */ - backgroundPages += get_mm_counter(t->mm, MM_ANONPAGES); - } else { - nonBackgroundPages += get_mm_counter(t->mm, MM_ANONPAGES); - } - } - - task_unlock(t); - } - - rcu_read_unlock(); - -#ifdef BALLOON_DEBUG_PRINT_ANDROID_PAGES - printk("Balloon_AndroidBackgroundPages: non-background pages: %d " - "background pages: %d\n", - nonBackgroundPages, - backgroundPages); -#endif - - return backgroundPages; -} -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/mvp_compiler.h b/arch/arm/mvp/mvpkm/mvp_compiler.h deleted file mode 100644 index 58825a0..0000000 --- a/arch/arm/mvp/mvpkm/mvp_compiler.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Compiler-related definitions and directives. - */ - -#ifndef _MVP_COMPILER_H_ -#define _MVP_COMPILER_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#ifdef __GNUC__ -#include "mvp_compiler_gcc.h" -#else /* __GNUC__ */ -#include "mvp_compiler_other.h" -#endif /* __GNUC__ */ - -/** - * @brief Find last set bit. - * - * @param n unsigned 32-bit integer. - * - * @return 0 if n == 0 otherwise 32 - the number of leading zeroes in n. - */ -#define FLS(n) (32 - CLZ(n)) - -#endif /// ifndef _MVP_COMPILER_H_ diff --git a/arch/arm/mvp/mvpkm/mvp_compiler_gcc.h b/arch/arm/mvp/mvpkm/mvp_compiler_gcc.h deleted file mode 100644 index ab35ebd..0000000 --- a/arch/arm/mvp/mvpkm/mvp_compiler_gcc.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief common definitions for GCC - */ - -#ifndef _MVP_COMPILER_GCC_H -#define _MVP_COMPILER_GCC_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @brief Count leading zeroes. - * - * @param n unsigned 32-bit integer. - * - * @return 32 if n == 0 otherwise 31 - the bit position of the most significant 1 - * in n. - */ -#ifdef __COVERITY__ -static inline int -CLZ(unsigned int n) -{ - unsigned int r = 0; - - while (n) { - r++; - n >>= 1; - } - - return 32 - r; -} -#else -#define CLZ(n) __builtin_clz(n) -#endif - -#define PACKED __attribute__ ((packed)) -#define ALLOC __attribute__ ((malloc, warn_unused_result)) -#define UNUSED __attribute__ ((unused)) -#define PURE __attribute__ ((pure)) -#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) -#define FORMAT(x,y,z) __attribute__ ((format(x,y,z))) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect((x), 0) - -/* - * For debug builds, we want to omit __attribute__((noreturn)) so that gcc will - * keep stack linkages and then we will have useful core dumps. For non-debug - * builds, we don't care about the stack frames and want the little bit of - * optimization that noreturn gives us. - */ -#if defined(__COVERITY__) || !defined(MVP_DEBUG) -#define NORETURN __attribute__((noreturn)) -#else -#define NORETURN -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/mvp_math.h b/arch/arm/mvp/mvpkm/mvp_math.h deleted file mode 100644 index 7017bc8..0000000 --- a/arch/arm/mvp/mvpkm/mvp_math.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Math library. - */ - -#ifndef _MVP_MATH_H_ -#define _MVP_MATH_H_ - -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_HOSTUSER -#include "include_check.h" - -#include "mvp_compiler_gcc.h" - -/** - * @brief Compute floor log2 of a given 32-bit unsigned integer. - * - * @param n 32-bit unsigned integer, n > 0. - * - * @return floor(log2(n)). - */ -#define LOG2(n) \ -( \ - __builtin_constant_p(n) ? ( \ - (n) & (1UL << 31) ? 31 : \ - (n) & (1UL << 30) ? 30 : \ - (n) & (1UL << 29) ? 29 : \ - (n) & (1UL << 28) ? 28 : \ - (n) & (1UL << 27) ? 27 : \ - (n) & (1UL << 26) ? 26 : \ - (n) & (1UL << 25) ? 25 : \ - (n) & (1UL << 24) ? 24 : \ - (n) & (1UL << 23) ? 23 : \ - (n) & (1UL << 22) ? 22 : \ - (n) & (1UL << 21) ? 21 : \ - (n) & (1UL << 20) ? 20 : \ - (n) & (1UL << 19) ? 19 : \ - (n) & (1UL << 18) ? 18 : \ - (n) & (1UL << 17) ? 17 : \ - (n) & (1UL << 16) ? 16 : \ - (n) & (1UL << 15) ? 15 : \ - (n) & (1UL << 14) ? 14 : \ - (n) & (1UL << 13) ? 13 : \ - (n) & (1UL << 12) ? 12 : \ - (n) & (1UL << 11) ? 11 : \ - (n) & (1UL << 10) ? 10 : \ - (n) & (1UL << 9) ? 9 : \ - (n) & (1UL << 8) ? 8 : \ - (n) & (1UL << 7) ? 7 : \ - (n) & (1UL << 6) ? 6 : \ - (n) & (1UL << 5) ? 5 : \ - (n) & (1UL << 4) ? 4 : \ - (n) & (1UL << 3) ? 3 : \ - (n) & (1UL << 2) ? 2 : \ - (n) & (1UL << 1) ? 1 : \ - (n) & (1UL << 0) ? 0 : \ - 0xffffffff \ - ) : (uint32)(CLZ(1) - CLZ(n)) \ -) - -/** - * @brief Multiplicative hash function for 32-bit key and p-bit range. See p229 - * Introduction to Algorithms, Cormen, Leiserson and Rivest, 1996. - * - * @param key 32-bit key. - * @param p range order, <= 32. - * - * @return hash value in range [0..2^p) - */ -static inline uint32 -Math_MultiplicativeHash(uint32 key, uint32 p) -{ - return (key * 2654435769UL) >> (32 - p); -} - -/** - * @brief Compute ceiling log2 of a given 32-bit unsigned integer. - * - * @param n 32-bit unsigned integer, n > 0. - * - * @return ceiling(log2(n)). - */ -static inline uint32 CLOG2(uint32 n) -{ - return LOG2(n) + ((n & -n) != n); -} - - -/** - * @brief djb2 String hashing function by Dan Bernstein, see - * http://www.cse.yorku.ca/~oz/hash.html - * @param str String to hash - * @return 32-bit hash value - */ -static inline -uint32 Math_Djb2Hash(uint8 *str) -{ - uint32 hash = 5381; - int32 c; - - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - - return hash; -} - -#endif // ifndef _MVP_MATH_H_ diff --git a/arch/arm/mvp/mvpkm/mvp_timer.h b/arch/arm/mvp/mvpkm/mvp_timer.h deleted file mode 100644 index 0bd073a..0000000 --- a/arch/arm/mvp/mvpkm/mvp_timer.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief timer definitions - */ - -#ifndef _MVP_TIMER_H -#define _MVP_TIMER_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @brief timer tick rate as returned by MVPTimer_Now64 as a uint64 and used by - * MVPTimer.when64. - * - * For example 1,000,000 means the counter is in microseconds. - * - * Current implementation requires MVP_TIMER_RATE64 <= 1,000,000,000 and that - * it evenly divide 1,000,000,000. Currently 1,000,000,000 to avoid a multiply - * or divide in MVPTimer_Now64. - */ -#define MVP_TIMER_RATE64 1000000000 - -/* - * Extract current UNIX-style time_t date/time from the 64-bit time as returned - * by MVPTimer_Now64(). - */ -#define MVP_TIMER_RATE64_TIME_T(time64) ((time_t)((time64) / MVP_TIMER_RATE64)) - -typedef struct MVPTimer MVPTimer; - -/** - * @brief timer entry struct - */ -struct MVPTimer { - MVPTimer *next; ///< next in timers list - uint64 when64; ///< absolute expiration - void (*entry)(uint64 now64, MVPTimer *timer); ///< callback entrypoint - void *param; ///< callback parameter -}; - -void MVPTimer_InitVMX(void); -uint64 MVPTimer_Now64(void); -void MVPTimer_Start(MVPTimer *timer); -_Bool MVPTimer_Cancel(MVPTimer *timer); - -#endif diff --git a/arch/arm/mvp/mvpkm/mvp_types.h b/arch/arm/mvp/mvpkm/mvp_types.h deleted file mode 100644 index 035efd7..0000000 --- a/arch/arm/mvp/mvpkm/mvp_types.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief basic type definitions. - * These may need to be conditionalized for different compilers/platforms. - */ - -#ifndef _MVPTYPES_H -#define _MVPTYPES_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; -typedef unsigned long long uint64; - -typedef signed char int8; -typedef short int16; -typedef int int32; -typedef long long int64; - -typedef uint32 CVA; // whatever we are compiling the code as -typedef uint32 GVA; // guest virtual addresses -typedef uint32 MVA; // monitor virtual addresses -typedef uint32 HKVA; // host kernel virtual addresses -typedef uint32 HUVA; // host user virtual addresses -typedef uint64 PA; // (guest) physical addresses (40-bit) -typedef uint32 MA; // (host) machine addresses - -typedef uint32 PPN; // PA/PAGE_SIZE -typedef uint32 MPN; // MA/PAGE_SIZE - -typedef uint64 cycle_t; - -/** - * @brief Page segment. - * - * Specifies a segment within a single page. - */ -typedef struct { - uint16 off; - uint16 len; -} PageSeg; - -/* - * GCC's argument checking for printf-like functions - * - * fmtPos is the position of the format string argument, beginning at 1 - * varPos is the position of the variable argument, beginning at 1 - */ - -#if defined(__GNUC__) -# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos))) -#else -# define PRINTF_DECL(fmtPos, varPos) -#endif - -#if defined(__GNUC__) -# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos))) -#else -# define SCANF_DECL(fmtPos, varPos) -#endif - -#endif /* _MVPTYPES_H */ diff --git a/arch/arm/mvp/mvpkm/mvp_version.h b/arch/arm/mvp/mvpkm/mvp_version.h deleted file mode 100644 index 31274dd..0000000 --- a/arch/arm/mvp/mvpkm/mvp_version.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief What version is this? - * - */ - -#ifndef _MVP_VERSION_H_ -#define _MVP_VERSION_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#define INCLUDE_ALLOW_HOSTUSER -#include "include_check.h" -#include "utils.h" - -/* - * MVP Internal Version Numbering - * - * - * There are 4 different usage areas of version information. - * - * Version Name. This is a marketing string that is used to sell the - * product. The update of this string has legal consequences, it - * should be done infrequently. Currently we use "V1.0" like - * terms. Developer builds have E.X.P as Version Name. - * - * Android Version Code. This is an integer associated with - * com.vmware.mvp.apk on Google Play (a.k.a Android Market). If our - * product is multi-apk (that is, we release individual apks for the - * different Android versions) then the Android Version Code must - * satisfy certain constrains. Typically the Android API level is - * the high order 2 digits. - * - * Engineering Version Code. During an update process of one of the - * 3 components on the handset (MVP, VVP, OEK) compatibility needs - * to be verified. The Engineering Version Code is a single number - * associated with each of the 4 components and it serves as a basis - * of this compatibility test. It reflects time, bigger number is - * associated with newer code. - * - * Git Revision. The git hash is a unique identifier of the - * source. If picked up from a log, engineers can go to the code - * depos and check out the exact code used for the build. For MVP, - * VVP, and OEK this is the main/mvp.git, for HMM it is - * main/mdm.git. Note that git hash is not ordered, it cannot be - * used to directly determine precedence. - * - */ - -#define MVP_VERSION_CODE 16800005 -#define MVP_VERSION_CODE_FORMATSTR "%s_%d" -#define MVP_VERSION_CODE_FORMATARGSV(V_) MVP_STRINGIFY(1.1.3), (V_) -#define MVP_VERSION_CODE_FORMATARGS \ - MVP_VERSION_CODE_FORMATARGSV(MVP_VERSION_CODE) - -#define MVP_VERSION_FORMATSTR \ - MVP_VERSION_CODE_FORMATSTR \ - " compiled at %s based on revision %s by user %s." - -#define MVP_VERSION_FORMATARGS \ - MVP_VERSION_CODE_FORMATARGS, \ - __DATE__, \ - MVP_STRINGIFY(5c995a85564cd060562bdbcd1422709e7a326301), \ - MVP_STRINGIFY() - -#define MvpVersion_Map(map_, version_) \ - ({ \ - uint32 ii_; \ - uint32 versionApi_ = 0; \ - for (ii_ = 0; ii_ < NELEM(map_); ii_++) { \ - if (map_[ii_] <= version_) { \ - versionApi_ = map_[ii_]; \ - } \ - } \ - versionApi_; \ - }) - -/* - * MVP.apk must communicate to VVP and OEK on many of its APIs. To - * ensure compatibility, it is mandated that any VVP and OEK version - * younger than the minimums defined below can be serviced on all of - * the various APIs. - * - * During the deprecation process, first a marketing decision is made - * that the limit below can be raised. After the new minimums are - * determined, they must be entered here. Then the various APIs can - * remove code that has been obsoleted before the new minimum versions. - */ -#define VVP_VERSION_CODE_MIN 0x0100020e -#define OEK_VERSION_CODE_MIN 0x01000001 - -#endif /* _MVP_VERSION_H_ */ diff --git a/arch/arm/mvp/mvpkm/mvpkm_comm_ev.c b/arch/arm/mvp/mvpkm/mvpkm_comm_ev.c deleted file mode 100644 index cb0ce26..0000000 --- a/arch/arm/mvp/mvpkm/mvpkm_comm_ev.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief mvpkm kernel hooks for Comm event signaling - */ - -#include -#include "comm_transp_impl.h" - -int (*CommTranspEvProcess)(CommTranspID* id, CommTranspIOEvent event); - - -/** - * @brief Register a processing callback for the host when a signal - * is received from the guest. Supports only a single comm "service" - * on the host. - * @param commProcessFunc function pointer to process a signal - */ - -void -Mvpkm_CommEvRegisterProcessCB(int (*commProcessFunc)(CommTranspID*, - CommTranspIOEvent)) -{ - CommTranspEvProcess = commProcessFunc; -} - -/** - * @brief Unregister the processing callback for the host when a signal - * is received from the guest. - */ - -void -Mvpkm_CommEvUnregisterProcessCB(void) -{ - CommTranspEvProcess = NULL; -} - - -EXPORT_SYMBOL(Mvpkm_CommEvRegisterProcessCB); -EXPORT_SYMBOL(Mvpkm_CommEvUnregisterProcessCB); diff --git a/arch/arm/mvp/mvpkm/mvpkm_comm_ev.h b/arch/arm/mvp/mvpkm/mvpkm_comm_ev.h deleted file mode 100644 index 2e3c960..0000000 --- a/arch/arm/mvp/mvpkm/mvpkm_comm_ev.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief mvpkm kernel hooks for comm event signaling - */ - -#ifndef _MVPKM_COMM_EV_H -#define _MVPKM_COMM_EV_H - -extern int (*CommTranspEvProcess)(CommTranspID* id, CommTranspIOEvent event); - -/** - * @brief Forward any guest signal requests to the commkm module - * @param id transport channel id - * @param event comm event type - */ - -static inline void -Mvpkm_CommEvSignal(CommTranspID *id, CommTranspIOEvent event) -{ - if (CommTranspEvProcess) { - CommTranspEvProcess(id, event); - } -} - -void -Mvpkm_CommEvRegisterProcessCB(int (*commProcessFunc)(CommTranspID*, - CommTranspIOEvent)); -void Mvpkm_CommEvUnregisterProcessCB(void); - - - -#endif diff --git a/arch/arm/mvp/mvpkm/mvpkm_kernel.h b/arch/arm/mvp/mvpkm/mvpkm_kernel.h deleted file mode 100644 index 19ba6ce..0000000 --- a/arch/arm/mvp/mvpkm/mvpkm_kernel.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#ifndef _MVPKM_KERNEL_H -#define _MVPKM_KERNEL_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include -#include -#include - -#ifdef CONFIG_HAS_WAKELOCK -#include -#endif - -#include "atomic.h" -#include "montimer_kernel.h" -#include "worldswitch.h" - -/** - * @file - * - * @brief The kernel level driver. - */ - -struct MvpkmVM { - struct kobject kobj; ///< used to hook into sysfs - struct kset *devicesKSet; ///< kset to list virtual device entries - struct kset *miscKSet; ///< kset to list miscellaneous entries - _Bool haveKObj; ///< used to properly release instance - struct rb_root lockedRoot; ///< locked page RB tree root - struct rw_semaphore lockedSem; ///< linked list rw semaphore - AtmUInt32 usedPages; ///< number of MEMREGION_MAINMEM pages - _Bool isMonitorInited; ///< Has SetupMonitor been called already? - WorldSwitchPage *wsp; ///< worldswitch page - wait_queue_head_t wfiWaitQ; ///< guest VCPU is waiting-for-interrupt - struct rw_semaphore wspSem; /*< prevents entries the WFI - wait Q from disappearing - underneath us in - MvpkmShrink. */ - MonTimer monTimer; /*< monitor timers, there - should be one of these - per VCPU */ - MPN stubPageMPN; /*< stub page to be used for - unmappable pages */ - struct vm_struct *wspHkvaArea; ///< VM area struct for wspHkvaArea - HKVA wspHKVADummyPage;///< Dummy page used for backing wspHkvaArea -#ifdef CONFIG_HAS_WAKELOCK - struct wake_lock wakeLock; ///< guest running wake lock -#endif - struct rw_semaphore monThreadTaskSem;/*< prevents monThreadTask from - disappearing underneath us */ - struct task_struct *monThreadTask; - struct timer_list balloonWDTimer; /// Balloon watchdog timer - _Bool balloonWDEnabled; /// Balloon watchdog enabled? -}; - -typedef struct MvpkmVM MvpkmVM; - -void Mvpkm_WakeGuest(MvpkmVM *vm, int why); -struct kset *Mvpkm_FindVMNamedKSet(int vmID, const char *name); - -#endif diff --git a/arch/arm/mvp/mvpkm/mvpkm_main.c b/arch/arm/mvp/mvpkm/mvpkm_main.c deleted file mode 100644 index d32a4c1..0000000 --- a/arch/arm/mvp/mvpkm/mvpkm_main.c +++ /dev/null @@ -1,2691 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief The kernel level driver. - */ - -#define __KERNEL_SYSCALLS__ -#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 - -#include - -#include -#include -#include -#include -#include - -#include "mvp.h" -#include "mvp_version.h" -#include "mvpkm_types.h" -#include "mvpkm_private.h" -#include "mvpkm_kernel.h" -#include "actions.h" -#include "wscalls.h" -#include "arm_inline.h" -#include "tsc.h" -#include "mksck_kernel.h" -#include "mmu_types.h" -#include "mvp_timer.h" -#include "qp.h" -#include "qp_host_kernel.h" -#include "cpufreq_kernel.h" -#include "mvpkm_comm_ev.h" -#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER -#include "mvp_balloon.h" -#endif - - -/********************************************************************* - * - * Definition of the file operations - * - *********************************************************************/ -static _Bool LockedListAdd(MvpkmVM *vm, - __u32 mpn, - __u32 order, - PhysMem_RegionType forRegion); -static _Bool LockedListDel(MvpkmVM *vm, __u32 mpn); -static void LockedListUnlockAll(MvpkmVM *vm); -static _Bool LockedListLookup(MvpkmVM *vm, __u32 mpn); -static int SetupMonitor(MvpkmVM *vm); -static int RunMonitor(MvpkmVM *vm); -static MPN AllocZeroedFreePages(MvpkmVM *vm, - uint32 order, - _Bool highmem, - PhysMem_RegionType forRegion, - HKVA *hkvaRet); -static HKVA MapWSPHKVA(MvpkmVM *vm, HkvaMapInfo *mapInfo); -static void UnmapWSPHKVA(MvpkmVM *vm); -static int MvpkmWaitForInt(MvpkmVM *vm, _Bool suspend); -static void ReleaseVM(MvpkmVM *vm); - -/* - * Mksck open request must come from this uid. It must be root until - * it is set via an ioctl from mvpd. - */ -uid_t Mvpkm_vmwareUid = 0; -EXPORT_SYMBOL(Mvpkm_vmwareUid); - -/* - * Minimum hidden app oom_adj, provided by mvpd, since we can't get it directly - * from the lowmemorykiller module. - */ -static int minHiddenAppOOMAdj; - -/* - * vCPU cpu affinity to let monitor/guest run on some CPUs only (when possible) - */ -static DECLARE_BITMAP(vcpuAffinity, NR_CPUS); - -/********************************************************************* - * - * Sysfs nodes - * - *********************************************************************/ -/* - * kobject for our sysfs representation, used for global nodes. - */ -static struct kobject *mvpkmKObj; - -/* - * kobject for the balloon exports. - */ -static struct kobject *balloonKObj; - -/** - * @brief sysfs show function for global version attribute. - * - * @param kobj reference to kobj nested in MvpkmVM struct. - * @param attr kobj_attribute reference, not used. - * @param buf PAGE_SIZEd buffer to write to. - * - * @return number of characters printed (not including trailing null character). - */ -static ssize_t -version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, MVP_VERSION_FORMATSTR "\n", MVP_VERSION_FORMATARGS); -} - -static struct kobj_attribute versionAttr = __ATTR_RO(version); - -/** - * @brief sysfs show function for global background_pages attribute. - * - * Used by vmx balloon policy controller to gauge the amount of freeable - * anonymous memory. - * - * @param kobj reference to kobj nested in MvpkmVM struct. - * @param attr kobj_attribute reference, not used. - * @param buf PAGE_SIZEd buffer to write to. - * - * @return number of characters printed (not including trailing null character). - */ -static ssize_t -background_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) -{ -#ifndef CONFIG_ANDROID_LOW_MEMORY_KILLER - return snprintf(buf, PAGE_SIZE, "0\n"); -#else - return snprintf(buf, PAGE_SIZE, "%d\n", Balloon_AndroidBackgroundPages(minHiddenAppOOMAdj)); -#endif -} - -static struct kobj_attribute backgroundAttr = __ATTR_RO(background); - -/** - * @brief sysfs show function to export the other_file calculation in - * lowmemorykiller. - * - * It's helpful, in the balloon controller, to know what the lowmemorykiller - * module is using to know when the system has crossed a minfree threshold. - * Since there exists a number of different other_file calculations in various - * lowmemorykiller patches (@see{MVP-1674}), and the module itself doesn't - * provide a clean export of this figure, we provide it on a case-by-case basis - * for the various supported hosts here. - * - * @param kobj reference to kobj nested in MvpkmVM struct. - * @param attr kobj_attribute reference, not used. - * @param buf PAGE_SIZEd buffer to write to. - * - * @return number of characters printed (not including trailing null character). - */ -static ssize_t -other_file_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) -{ - int32 other_file = 0; - -#ifndef LOWMEMKILLER_VARIANT -#define LOWMEMKILLER_VARIANT 0 -#endif - -#ifndef LOWMEMKILLER_MD5 -#define LOWMEMKILLER_MD5 0 -#endif - -#ifndef LOWMEMKILLER_SHRINK_MD5 -#define LOWMEMKILLER_SHRINK_MD5 0 -#endif - - /* - * The build system hashes the lowmemorykiller section related to the - * other_file calculation in the kernel source for us, here we have to - * provide the code. - */ -#if LOWMEMKILLER_VARIANT == 1 - /* - * This is the same as the non-exported global_reclaimable_pages() when there - * is no swap. - */ - other_file = global_page_state(NR_ACTIVE_FILE) + - global_page_state(NR_INACTIVE_FILE); -#elif LOWMEMKILLER_VARIANT == 2 - other_file = global_page_state(NR_FILE_PAGES); -#elif LOWMEMKILLER_VARIANT == 3 - other_file = global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM); -#elif LOWMEMKILLER_VARIANT == 4 - /* - * Here free/file pages are fungible and max(free, file) isn't used, but we - * can continue to use max(free, file) since max(free, file) = other_file in - * this case. - */ - other_file = global_page_state(NR_FREE_PAGES) + global_page_state(NR_FILE_PAGES); -#elif defined(NONANDROID) - /* - * Non-Android host platforms don't have ballooning enabled. - */ -#else - /* - * If you get this message, you need to run 'make lowmem-info' and inspect - * lowmemorykiller.c. If the "other_file = ..." calculation in lowmem_shrink - * appears above, simply add the "Shrink#" to an existing entry in - * lowmemkiller-variant.sh, pointing to the variant number above. Otherwise, - * provide a new entry above and variant number, with the appropriate - * other_file calculation and update lowmemkiller-variant.sh accordingly. - */ -//#warning "Unknown lowmemorykiller variant in hosted/module/mvpkm_main.c, falling back on default (see other_file_show for the remedy)" - /* - * Fall back on default - this may bias strangely for/against the host, but - * nothing catastrophic should result. - */ - other_file = global_page_state(NR_FILE_PAGES); -#endif - -#define _STRINGIFY(x) #x -#define STRINGIFY(x) _STRINGIFY(x) - return snprintf(buf, - PAGE_SIZE, - "%d %d %s %s\n", - other_file, - LOWMEMKILLER_VARIANT, - STRINGIFY(LOWMEMKILLER_MD5), - STRINGIFY(LOWMEMKILLER_SHRINK_MD5)); -#undef _STRINGIFY -#undef STRINGIFY -} - -static struct kobj_attribute otherFileAttr = __ATTR_RO(other_file); - -/* - * kset for our sysfs representation, used for per-VM nodes. - */ -static struct kset *mvpkmKSet; - -static ssize_t MvpkmAttrShow(struct kobject *kobj, - struct attribute *attr, - char *buf); -static ssize_t MvpkmAttrStore(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t count); - -static void MvpkmKObjRelease(struct kobject *kobj) - __attribute__ ((optimize ("-fomit-frame-pointer"))); - - -/** - * @brief Releases the vm structure containing the kobject. - * - * @param kobj the vm's kobject. - */ - -static void -MvpkmKObjRelease(struct kobject *kobj) -{ - MvpkmVM *vm = container_of(kobj, MvpkmVM, kobj); - - ReleaseVM(vm); - - module_put(THIS_MODULE); -} - - -/** - * @name mvpkm ktype attribute structures for locked_pages. - * - * @{ - */ -static struct sysfs_ops mvpkmSysfsOps = { - .show = MvpkmAttrShow, - .store = MvpkmAttrStore -}; - -static struct attribute mvpkmLockedPagesAttr = { - .name = "locked_pages", - .mode = 0444, -}; - -static struct attribute mvpkmBalloonWatchdogAttr = { - .name = "balloon_watchdog", - .mode = 0666 -}; - -static struct attribute mvpkmMonitorAttr = { - .name = "monitor", - .mode = 0400, -}; - -static struct attribute *mvpkmDefaultAttrs[] = { - &mvpkmLockedPagesAttr, - &mvpkmBalloonWatchdogAttr, - &mvpkmMonitorAttr, - NULL, -}; - -static struct kobj_type mvpkmKType = { - .sysfs_ops = &mvpkmSysfsOps, - .release = MvpkmKObjRelease, - .default_attrs = mvpkmDefaultAttrs, -}; -/*@}*/ - -/* - * As it is not very common for host kernels to have SYS_HYPERVISOR enabled and - * you have to "hack" a Kconfig file to enable it, just include the - * functionality inline if it is not enabled. - */ -#ifndef CONFIG_SYS_HYPERVISOR -struct kobject *hypervisor_kobj; -EXPORT_SYMBOL_GPL(hypervisor_kobj); -#endif - - -/* - * kobject and kset utilities. - */ - -extern struct kobject *kset_find_obj(struct kset *, const char *) - __attribute__((weak)); - - -/** - * @brief Finds a kobject in a kset. The actual implementation is copied from - * kernel source in lib/kobject.c. Although the symbol is extern-declared, - * it is not EXPORT_SYMBOL-ed. We use a weak reference in case the symbol - * might be exported in future kernel versions. - * - * @param kset set to search. - * @param name object name. - * - * @return retained kobject if found, NULL otherwise. - */ - -struct kobject * -kset_find_obj(struct kset *kset, - const char *name) -{ - struct kobject *k; - struct kobject *ret = NULL; - - spin_lock(&kset->list_lock); - list_for_each_entry(k, &kset->list, entry) { - if (kobject_name(k) && !strcmp(kobject_name(k), name)) { - ret = kobject_get(k); - break; - } - } - spin_unlock(&kset->list_lock); - return ret; -} - - -/** - * @brief Finds one of the VM's pre-defined ksets. - * - * @param vmID a VM ID. - * @param name name of one of the VM's pre-defined ksets. - * - * @return retained kset if found, NULL otherwise. - */ - -struct kset * -Mvpkm_FindVMNamedKSet(int vmID, - const char *name) -{ - MvpkmVM *vm; - struct kobject *kobj; - char vmName[32] = {}; /* Large enough to hold externally-formatted int32. */ - struct kset *res = NULL; - - if (!mvpkmKSet) { - return NULL; - } - - snprintf(vmName, sizeof vmName, "%d", vmID); - vmName[sizeof vmName - 1] = '\0'; /* Always null-terminate, no overflow. */ - - kobj = kset_find_obj(mvpkmKSet, vmName); - if (!kobj) { - return NULL; - } - - vm = container_of(kobj, MvpkmVM, kobj); - - if (!strcmp(name, "devices")) { - res = kset_get(vm->devicesKSet); - } else if (!strcmp(name, "misc")) { - res = kset_get(vm->miscKSet); - } - - kobject_put(kobj); - return res; -} - -EXPORT_SYMBOL(Mvpkm_FindVMNamedKSet); - - - -/********************************************************************* - * - * Standard Linux miscellaneous device registration - * - *********************************************************************/ - -MODULE_LICENSE("GPL"); // for kallsyms_lookup_name - -static int MvpkmFault(struct vm_area_struct *vma, struct vm_fault *vmf); - - -/** - * @brief Linux vma operations for /dev/mem-like kernel module mmap. We - * enforce the restriction that only MPNs that have been allocated - * to the opened VM may be mapped and also increment the reference - * count (via vm_insert_page), so that even if the memory is later - * freed by the VM, host process vma's containing the MPN can't - * compromise the system. - * - * However, only trusted host processes (e.g. the vmx) should be allowed - * to use this interface, since you can mmap the monitor's code/data/ - * page tables etc. with it. Untrusted host processes are limited to - * typed messages for sharing memory with the monitor. Unix file system - * access permissions are the intended method of restricting access. - * Unfortunately, today _any_ host process utilizing Mksck requires - * access to mvpkm to setup its Mksck pages and obtain socket info via - * ioctls - we probably should be exporting two devices, one for trusted - * and one for arbitrary host processes to avoid this confusion of - * concerns. - */ -static struct vm_operations_struct mvpkmVMOps = { - .fault = MvpkmFault -}; - -/* - * Generic kernel module file ops. These functions will be registered - * at the time the kernel module is loaded. - */ -static long MvpkmUnlockedIoctl(struct file *filep, - unsigned int cmd, - unsigned long arg); -static int MvpkmOpen(struct inode *inode, struct file *filp); -static int MvpkmRelease(struct inode *inode, struct file *filp); -static int MvpkmMMap(struct file *file, struct vm_area_struct *vma); - -/** - * @brief the file_operation structure contains the callback functions - * that are registered with Linux to handle file operations on - * the mvpkm device. - * - * The structure contains other members that the mvpkm device - * does not use. Those members are auto-initialized to NULL. - * - * WARNING, this structure has changed after Linux kernel 2.6.19: - * readv/writev are changed to aio_read/aio_write (neither is used here). - */ -static const struct file_operations mvpkmFileOps = { - .owner = THIS_MODULE, - .unlocked_ioctl = MvpkmUnlockedIoctl, - .open = MvpkmOpen, - .release = MvpkmRelease, - .mmap = MvpkmMMap -}; - -/** - * @brief The mvpkm device identifying information to be used to register - * the device with the Linux kernel. - */ -static struct miscdevice mvpkmDev = { - .minor = 165, - .name = "mvpkm", - .fops = &mvpkmFileOps -}; - -/** - * Mvpkm is loaded by mvpd and only mvpd will be allowed to open - * it. There is a very simple way to verify that: record the process - * id (thread group id) at the time the module is loaded and test it - * at the time the module is opened. - */ -static struct pid *initTgid; - - -#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER -/** - * @name Slab shrinker for triggering balloon adjustment. - * - * @note shrinker us used as a trigger for guest balloon. - * - * @{ - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) -static int MvpkmShrink(struct shrinker *this, struct shrink_control *sc); -#else -static int MvpkmShrink(struct shrinker *this, int nrToScan, gfp_t gfpMask); -#endif - -static struct shrinker mvpkmShrinker = { - .shrink = MvpkmShrink, - .seeks = DEFAULT_SEEKS -}; -/*@}*/ -#endif - -module_param_array(vcpuAffinity, ulong, NULL, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(vcpuAffinity, "vCPU affinity"); - - -/** - * @brief Initialize the mvpkm device, register it with the Linux kernel. - * - * @return A zero is returned on success and a negative errno code for failure. - * (Same as the return policy of misc_register(9).) - */ - -static int __init -MvpkmInit(void) -{ - int err = 0; - _Bool mksckInited = false; - _Bool cpuFreqInited = false; - - printk(KERN_INFO "Mvpkm: " MVP_VERSION_FORMATSTR "\n", MVP_VERSION_FORMATARGS); - printk(KERN_INFO "Mvpkm: loaded from process %s tgid=%d, pid=%d\n", - current->comm, - task_tgid_vnr(current), - task_pid_vnr(current)); - - if (bitmap_empty(vcpuAffinity, NR_CPUS)) { - bitmap_copy(vcpuAffinity, cpumask_bits(cpu_possible_mask), NR_CPUS); - } - - if ((err = misc_register(&mvpkmDev))) { - return -ENOENT; - } - - if ((err = Mksck_Init())) { - goto error; - } else { - mksckInited = true; - } - - QP_HostInit(); - - CpuFreq_Init(); - cpuFreqInited = true; - - /* - * Reference mvpd (module loader) tgid struct, so that we can avoid - * attacks based on pid number wraparound. - */ - initTgid = get_pid(task_tgid(current)); - -#ifndef CONFIG_SYS_HYPERVISOR - hypervisor_kobj = kobject_create_and_add("hypervisor", NULL); - if (!hypervisor_kobj) { - err = -ENOMEM; - goto error; - } -#endif - - if (!(mvpkmKObj = kobject_create_and_add("mvp", hypervisor_kobj)) || - !(balloonKObj = kobject_create_and_add("lowmem", mvpkmKObj)) || - !(mvpkmKSet = kset_create_and_add("vm", NULL, mvpkmKObj))) { - err = -ENOMEM; - goto error; - } - - if ((err = sysfs_create_file(mvpkmKObj, &versionAttr.attr))) { - goto error; - } - - if ((err = sysfs_create_file(balloonKObj, &backgroundAttr.attr))) { - goto error; - } - - if ((err = sysfs_create_file(balloonKObj, &otherFileAttr.attr))) { - goto error; - } - -#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER - register_shrinker(&mvpkmShrinker); -#endif - - MksckPageInfo_Init(); - - return 0; - -error: - if (mvpkmKSet) { - kset_unregister(mvpkmKSet); - } - - if (balloonKObj) { - kobject_del(balloonKObj); - kobject_put(balloonKObj); - } - - if (mvpkmKObj) { - kobject_del(mvpkmKObj); - kobject_put(mvpkmKObj); - } - -#ifndef CONFIG_SYS_HYPERVISOR - if (hypervisor_kobj) { - kobject_del(hypervisor_kobj); - kobject_put(hypervisor_kobj); - } -#endif - - if (cpuFreqInited) { - CpuFreq_Exit(); - } - - if (mksckInited) { - Mksck_Exit(); - } - - if (initTgid) { - put_pid(initTgid); - } - - misc_deregister(&mvpkmDev); - return err; -} - -/** - * @brief De-register the mvpkm device with the Linux kernel. - */ -void -MvpkmExit(void) -{ - PRINTK(KERN_INFO "MvpkmExit called !\n"); - - MksckPageInfo_Exit(); - -#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER - unregister_shrinker(&mvpkmShrinker); -#endif - - kset_unregister(mvpkmKSet); - kobject_del(balloonKObj); - kobject_put(balloonKObj); - kobject_del(mvpkmKObj); - kobject_put(mvpkmKObj); -#ifndef CONFIG_SYS_HYPERVISOR - kobject_del(hypervisor_kobj); - kobject_put(hypervisor_kobj); -#endif - - CpuFreq_Exit(); - - Mksck_Exit(); - - put_pid(initTgid); - - misc_deregister(&mvpkmDev); -} - -/* - * The standard module registration macros of Linux. - */ -module_init(MvpkmInit); -module_exit(MvpkmExit); - -module_param_named(minHiddenAppOOMAdj, minHiddenAppOOMAdj, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(minHiddenAppOOMAdj, "minimum hidden app oom_adj, as per lowmemorykiller"); - -#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER -/** - * @brief Balloon watchdog timeout callback. - * - * Terminate the VM since it's not responsive. - * - * @param data vm reference representation. - */ -static void -WatchdogCB(unsigned long data) -{ - MvpkmVM *vm = (MvpkmVM *)data; - - printk("Balloon watchdog expired (%d s)!\n", BALLOON_WATCHDOG_TIMEOUT_SECS); - - Mvpkm_WakeGuest(vm, ACTION_ABORT); -} - -/** - * @brief Slab shrinker. - * - * Called by Linux kernel when we're under memory pressure. We treat all locked - * pages as a slab for this purpose, similar to the Android low memory killer. - * - * @param this reference to registered shrinker for callback context. - * @param nrToScan number of entries to scan. If 0 then just return the number - * of present entries. We ignore the value of nrToScan when > 1 - * since the shrinker is a trigger to readjust guest balloons, - * where the actual balloon size is determined in conjunction - * with the guest. - * @param gfpMask ignored. - * - * @return number of locked pages. - */ -static int -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) -MvpkmShrink(struct shrinker *this, struct shrink_control *sc) -#else -MvpkmShrink(struct shrinker *this, int nrToScan, gfp_t gfpMask) -#endif -{ - uint32 locked = 0; - struct kobject *k; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) - int nrToScan = sc->nr_to_scan; -#endif - - spin_lock(&mvpkmKSet->list_lock); - - list_for_each_entry(k, &mvpkmKSet->list, entry) { - MvpkmVM *vm = container_of(k, MvpkmVM, kobj); - - locked += ATOMIC_GETO(vm->usedPages); - - /* - * Try and grab the WSP semaphore - if we fail, we must be VM setup or - * teardown, no point trying to wake the guest. - */ - if (nrToScan > 0 && - down_read_trylock(&vm->wspSem)) { - - if (vm->wsp) { - Mvpkm_WakeGuest(vm, ACTION_BALLOON); - - /* - * Balloon watchdog. - */ - if (vm->balloonWDEnabled) { - struct timer_list *t = &vm->balloonWDTimer; - - if (!timer_pending(t)) { - t->data = (unsigned long)vm; - t->function = WatchdogCB; - t->expires = jiffies + BALLOON_WATCHDOG_TIMEOUT_SECS * HZ; - add_timer(t); - } - } - } - - up_read(&vm->wspSem); - } - } - - spin_unlock(&mvpkmKSet->list_lock); - - return locked; -} -#endif - - -/** - * @brief The open file operation. Initializes the vm specific structure. - */ -int -MvpkmOpen(struct inode *inode, struct file *filp) -{ - MvpkmVM *vm; - - if (initTgid != task_tgid(current)) { - printk(KERN_ERR "%s: MVPKM can be opened only from MVPD (process %d).\n", - __FUNCTION__, pid_vnr(initTgid)); - return -EPERM; - } - printk(KERN_DEBUG "%s: Allocating an MvpkmVM structure from process %s tgid=%d, pid=%d\n", - __FUNCTION__, - current->comm, - task_tgid_vnr(current), - task_pid_vnr(current)); - - vm = kmalloc(sizeof(MvpkmVM), GFP_KERNEL); - if (!vm) { - return -ENOMEM; - } - - memset(vm, 0, sizeof *vm); - - init_timer(&vm->balloonWDTimer); - init_rwsem(&vm->lockedSem); - init_rwsem(&vm->wspSem); - init_rwsem(&vm->monThreadTaskSem); - vm->monThreadTask = NULL; - vm->isMonitorInited = false; - - filp->private_data = vm; - - if (!Mvpkm_vmwareUid) { - Mvpkm_vmwareUid = current_euid(); - } - - return 0; -} - -/** - * @brief Releases a VMs resources - * @param vm vm to release - */ -static void -ReleaseVM(MvpkmVM *vm) -{ - del_timer_sync(&vm->balloonWDTimer); - - down_write(&vm->wspSem); - - if (vm->isMonitorInited) { - MonitorTimer_Request(&vm->monTimer, 0); -#ifdef CONFIG_HAS_WAKELOCK - wake_lock_destroy(&vm->wakeLock); -#endif - Mksck_WspRelease(vm->wsp); - vm->wsp = NULL; - } - - up_write(&vm->wspSem); - - LockedListUnlockAll(vm); - - UnmapWSPHKVA(vm); - - /* - * All sockets potentially connected to sockets of this vm's vmId will fail - * at send now. DGRAM sockets are note required to tear down connection - * explicitly. - */ - - kfree(vm); -} - -/** - * @brief The release file operation. Releases the vm specific - * structure including all the locked pages. - * - * @param inode Unused - * @param filp which VM we're dealing with - * @return 0 - */ -int -MvpkmRelease(struct inode *inode, struct file *filp) -{ - MvpkmVM *vm = filp->private_data; - - /* - * Tear down any queue pairs associated with this VM - */ - if (vm->isMonitorInited) { - ASSERT(vm->wsp); - QP_DetachAll(vm->wsp->guestId); - } - - /* - * Release the VM's ksets. - */ - - kset_unregister(vm->miscKSet); - kset_unregister(vm->devicesKSet); - - if (vm->haveKObj) { - /* - * Release the VM's kobject. - * 'vm' will be kfree-d in its kobject's release function. - */ - - kobject_del(&vm->kobj); - kobject_put(&vm->kobj); - } else { - ReleaseVM(vm); - } - - filp->private_data = NULL; - - printk(KERN_INFO "%s: Released MvpkmVM structure from process %s tgid=%d, pid=%d\n", - __FUNCTION__, - current->comm, - task_tgid_vnr(current), - task_pid_vnr(current)); - - return 0; -} - -/** - * @brief Page fault handler for /dev/mem-like regions (see mvpkmVMOps - * block comment). - */ -static int -MvpkmFault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - unsigned long address = (unsigned long)vmf->virtual_address; - MPN mpn = vmf->pgoff; - MvpkmVM *vm = vma->vm_file->private_data; - - - /* - * Only insert pages belonging to the VM. The check is slow, O(n) in the - * number of MPNs associated with the VM, but it doesn't matter - the mmap - * interface should only be used by trusted processes at initialization - * time and for debugging. - * - * The mpn can be either in the memory reserved the monitor or mvpd - * through the regular mechanisms or it could be a mksck page. - */ - if (!pfn_valid(mpn)) { - printk(KERN_ERR "MvpkmMMap: Failed to insert %x @ %lx, mpn invalid\n", - mpn, - address); - } else if (LockedListLookup(vm, mpn)) { - if (vm_insert_page(vma, address, pfn_to_page(mpn)) == 0) { - return VM_FAULT_NOPAGE; - } - - printk(KERN_ERR "MvpkmMMap: Failed to insert %x @ %lx \n", - mpn, - address); - } else if (MksckPage_LookupAndInsertPage(vma, address, mpn) == 0) { - return VM_FAULT_NOPAGE; - } - - if (vm->stubPageMPN) { - if (vm_insert_page(vma, address, pfn_to_page(vm->stubPageMPN)) == 0) { - printk(KERN_INFO "MvpkmMMap: mapped the stub page at %x @ %lx \n", - mpn, - address); - return VM_FAULT_NOPAGE; - } - - printk(KERN_ERR "MvpkmMMap: Could not insert stub page %x @ %lx \n", - mpn, - address); - - } - - return VM_FAULT_SIGBUS; -} - -/** - * @brief sysfs show function for per-VM locked_pages attribute. - * - * @param kobj reference to kobj nested in MvpkmVM struct. - * @param attr attribute reference. - * @param buf PAGE_SIZEd buffer to write to. - * - * @return number of characters printed (not including trailing null character). - */ -static ssize_t -MvpkmAttrShow(struct kobject *kobj, - struct attribute *attr, - char *buf) -{ - if (attr == &mvpkmLockedPagesAttr) { - MvpkmVM *vm = container_of(kobj, MvpkmVM, kobj); - - return snprintf(buf, PAGE_SIZE, "%d\n", ATOMIC_GETO(vm->usedPages)); - } else if (attr == &mvpkmMonitorAttr) { - MvpkmVM *vm = container_of(kobj, MvpkmVM, kobj); - - return snprintf(buf, - PAGE_SIZE, - "hostActions %x callno %d\n", - ATOMIC_GETO(vm->wsp->hostActions), - WSP_Params(vm->wsp)->callno); - } else { - return -EPERM; - } -} - -/** - * @brief sysfs store function for per-VM locked_pages attribute. - * - * @param kobj reference to kobj nested in MvpkmVM struct. - * @param attr attribute reference. - * @param buf PAGE_SIZEd buffer to write to. - * @param buf input buffer. - * @param count input buffer length. - * - * @return number of bytes consumed or negative error code. - */ -static ssize_t -MvpkmAttrStore(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t count) -{ - if (attr == &mvpkmBalloonWatchdogAttr) { - MvpkmVM *vm = container_of(kobj, MvpkmVM, kobj); - - /* - * Enable balloon watchdog on first write. This includes all ballooning - * capable guest. - */ - vm->balloonWDEnabled = true; - del_timer_sync(&vm->balloonWDTimer); - - return 1; - } else { - return -EPERM; - } -} - -/** - * @brief Map machine address space region into host process. - * - * @param file file reference (ignored). - * @param vma Linux virtual memory area defining the region. - * - * @return 0 on success, otherwise error code. - */ -static int -MvpkmMMap(struct file *file, struct vm_area_struct *vma) -{ - vma->vm_ops = &mvpkmVMOps; - - return 0; -} - -#ifdef CONFIG_ARM_LPAE -/** - * @brief Determine host cacheability/shareability attributes. - * - * Used to ensure monitor/guest shared mappings are consistent with - * those of host user/kernel. - * - * @param[out] attribMAN when setting up the HW monitor this provides the - * attributes in the generic ARM_MemAttrNormal form, - * suitable for configuring the monitor and guest's - * [H]MAIR0 and setting the shareability attributes of - * the LPAE descriptors. - */ -static void -DetermineMemAttrLPAE(ARM_MemAttrNormal *attribMAN) -{ - /* - * We use set_pte_ext to sample what {S,TEX,CB} bits Linux is using for - * normal kernel/user L2D mappings. These bits should be consistent both - * with each other and what we use in the monitor since we share various - * pages with both host processes, the kernel module and monitor, and the - * ARM ARM requires that synonyms have the same cacheability attributes, - * see end of A3.5.{4,7} ARM DDI 0406A. - */ - HKVA hkva = __get_free_pages(GFP_KERNEL, 0); - - ARM_LPAE_L3D *pt = (ARM_LPAE_L3D *)hkva; - ARM_LPAE_L3D *kernL3D = &pt[0], *userL3D = &pt[1]; - uint32 attr, mair0, mair1; - - set_pte_ext((pte_t *)kernL3D, pfn_pte(0, PAGE_KERNEL), 0); - set_pte_ext((pte_t *)userL3D, pfn_pte(0, PAGE_NONE), 0); - - printk(KERN_INFO - "DetermineMemAttr: Kernel L3D AttrIndx=%x SH=%x\n", - kernL3D->blockS1.attrIndx, - kernL3D->blockS1.sh); - - printk(KERN_INFO - "DetermineMemAttr: User L3D AttrIndx=%x SH=%x\n", - userL3D->blockS1.attrIndx, - userL3D->blockS1.sh); - - ASSERT(kernL3D->blockS1.attrIndx == userL3D->blockS1.attrIndx); - ASSERT(kernL3D->blockS1.sh == userL3D->blockS1.sh); - - switch (kernL3D->blockS1.sh) { - case 0: { - attribMAN->share = ARM_SHARE_ATTR_NONE; - break; - } - case 2: { - attribMAN->share = ARM_SHARE_ATTR_OUTER; - break; - } - case 3: { - attribMAN->share = ARM_SHARE_ATTR_INNER; - break; - } - default: { - FATAL(); - } - } - - ARM_MRC_CP15(MAIR0, mair0); - ARM_MRC_CP15(MAIR1, mair1); - - attr = MVP_EXTRACT_FIELD(kernL3D->blockS1.attrIndx >= 4 ? mair1 : mair0, - 8 * (kernL3D->blockS1.attrIndx % 4), - 8); - - /* - * See B4-1615 ARM DDI 0406C-2c for magic. - */ -#define MAIR_ATTR_2_CACHE_ATTR(x, y) \ - switch (x) { \ - case 2: { \ - (y) = ARM_CACHE_ATTR_NORMAL_WT; \ - break; \ - } \ - case 3: { \ - (y) = ARM_CACHE_ATTR_NORMAL_WB; \ - break; \ - } \ - default: { \ - FATAL(); \ - } \ - } - - MAIR_ATTR_2_CACHE_ATTR(MVP_EXTRACT_FIELD(attr, 2, 2), attribMAN->innerCache); - MAIR_ATTR_2_CACHE_ATTR(MVP_EXTRACT_FIELD(attr, 6, 2), attribMAN->outerCache); - -#undef MAIR_ATTR_2_CACHE_ATTR - - printk(KERN_INFO - "DetermineMemAttr: innerCache %x outerCache %x share %x\n", - attribMAN->innerCache, - attribMAN->outerCache, - attribMAN->share); - - free_pages(hkva, 0); -} - -#else - -/** - * @brief Determine host cacheability/shareability attributes. - * - * Used to ensure monitor/guest shared mappings are consistent with - * those of host user/kernel. - * - * @param[out] attribL2D when setting up the LPV monitor a template L2D - * containing cacheability attributes {S, TEX,CB} used by - * host kernel for normal memory mappings. These may be - * used directly for monitor/guest mappings, since both - * worlds share a common {TRE, PRRR, NMRR}. - * @param[out] attribMAN when setting up TTBR0 in the LPV monitor and the page - * tables for the HW monitor this provides the attributes - * in the generic ARM_MemAttrNormal form, suitable for - * configuring TTBR0 + the monitor and guest's [H]MAIR0 - * and setting the shareability attributes of the LPAE - * descriptors. - */ -static void -DetermineMemAttrNonLPAE(ARM_L2D *attribL2D, ARM_MemAttrNormal *attribMAN) -{ - /* - * We use set_pte_ext to sample what {S,TEX,CB} bits Linux is using for - * normal kernel/user L2D mappings. These bits should be consistent both - * with each other and what we use in the monitor since we share various - * pages with both host processes, the kernel module and monitor, and the - * ARM ARM requires that synonyms have the same cacheability attributes, - * see end of A3.5.{4,7} ARM DDI 0406A. - */ - HKVA hkva = __get_free_pages(GFP_KERNEL, 0); - uint32 sctlr; - ARM_L2D *pt = (ARM_L2D *)hkva; - ARM_L2D *kernL2D = &pt[0], *userL2D = &pt[1]; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) - /* - * Linux uses the magic 2048 offset in set_pte_ext. See include/asm/pgtable.h - * for PAGE_NONE and PAGE_KERNEL semantics. - */ - const uint32 set_pte_ext_offset = 2048; -#else - /* - * Linux 2.6.38 switched the order of Linux vs hardware page tables. - * See mainline d30e45eeabefadc6039d7f876a59e5f5f6cb11c6. - */ - const uint32 set_pte_ext_offset = 0; -#endif - - set_pte_ext((pte_t *)(kernL2D + set_pte_ext_offset/sizeof(ARM_L2D)), - pfn_pte(0, PAGE_KERNEL), - 0); - set_pte_ext((pte_t *)(userL2D + set_pte_ext_offset/sizeof(ARM_L2D)), - pfn_pte(0, PAGE_NONE), - 0); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) - /* - * Linux 2.6.38 switched the order of Linux vs hardware page tables. - * See mainline d30e45eeabefadc6039d7f876a59e5f5f6cb11c6. - */ - kernL2D += 2048/sizeof(ARM_L2D); - userL2D += 2048/sizeof(ARM_L2D); -#endif - - printk(KERN_INFO - "DetermineMemAttr: Kernel L2D TEX=%x CB=%x S=%x\n", - kernL2D->small.tex, - kernL2D->small.cb, - kernL2D->small.s); - - printk(KERN_INFO - "DetermineMemAttr: User L2D TEX=%x CB=%x S=%x\n", - userL2D->small.tex, - userL2D->small.cb, - userL2D->small.s); - - ASSERT((kernL2D->small.tex & 1) == (userL2D->small.tex & 1)); - ASSERT(kernL2D->small.cb == userL2D->small.cb); - ASSERT(kernL2D->small.s == userL2D->small.s); - - *attribL2D = *kernL2D; - - /* - * We now decode TEX remap and obtain the more generic form for use in - * the LPV monitor's TTBR0 initialization and the HW monitor. - */ - - ARM_MRC_CP15(CONTROL_REGISTER, sctlr); - - if (sctlr & ARM_CP15_CNTL_TRE) { - uint32 prrr, nmrr, indx, type, innerCache, outerCache, outerShare, - share; - - printk(KERN_INFO - "DetermineMemAttr: TEX remapping enabled\n"); - - ARM_MRC_CP15(PRIMARY_REGION_REMAP, prrr); - ARM_MRC_CP15(NORMAL_MEMORY_REMAP, nmrr); - - printk(KERN_INFO - "DetermineMemAttr: PRRR=%x NMRR=%x\n", - prrr, - nmrr); - - /* - * Decode PRRR/NMRR below. See B3.7 ARM DDI 0406B for register - * encodings, tables and magic numbers. - */ - - indx = (MVP_BIT(kernL2D->small.tex, 0) << 2) | kernL2D->small.cb; - - /* - * Only normal memory makes sense here. - */ - type = MVP_EXTRACT_FIELD(prrr, 2 * indx, 2); - ASSERT(type == 2); - - innerCache = MVP_EXTRACT_FIELD(nmrr, 2 * indx, 2); - outerCache = MVP_EXTRACT_FIELD(nmrr, 16 + 2 * indx, 2); - outerShare = !MVP_BIT(prrr, 24 + indx); - share = MVP_BIT(prrr, 18 + kernL2D->small.s); - - printk(KERN_INFO - "DetermineMemAttr: type %x innerCache %x outerCache %x" - " share %x outerShare %x\n", - type, - innerCache, - outerCache, - share, - outerShare); - - if (share) { - if (outerShare) { - attribMAN->share = ARM_SHARE_ATTR_OUTER; - } else { - attribMAN->share = ARM_SHARE_ATTR_INNER; - } - } else { - attribMAN->share = ARM_SHARE_ATTR_NONE; - } - - attribMAN->innerCache = innerCache; - attribMAN->outerCache = outerCache; - } else { - NOT_IMPLEMENTED_JIRA(1849); - } - - free_pages(hkva, 0); -} -#endif - -/** - * @brief The ioctl file operation. - * - * The ioctl command is the main communication method between the - * vmx and the mvpkm kernel module. - * - * @param filp which VM we're dealing with - * @param cmd select which cmd function needs to be performed - * @param arg argument for command - * @return error code, 0 on success - */ -long -MvpkmUnlockedIoctl(struct file *filp, - unsigned int cmd, - unsigned long arg) -{ - MvpkmVM *vm = filp->private_data; - int retval = 0; - - switch (cmd) { - - - case MVPKM_DISABLE_FAULT: { - if (!vm->stubPageMPN) { - uint32 *ptr; - - vm->stubPageMPN = - AllocZeroedFreePages(vm, 0, false, MEMREGION_MAINMEM, (HKVA*)&ptr); - if (!vm->stubPageMPN) { - break; - } - ptr[0] = MVPKM_STUBPAGE_BEG; - ptr[PAGE_SIZE/sizeof(uint32) - 1] = MVPKM_STUBPAGE_END; - } - break; - } - - /* - * Allocate some pinned pages from kernel. - * Returns -ENOMEM if no host pages available for allocation. - */ - case MVPKM_LOCK_MPN: { - struct MvpkmLockMPN buf; - - if (copy_from_user(&buf, (void *)arg, sizeof buf)) { - return -EFAULT; - } - - buf.mpn = AllocZeroedFreePages(vm, - buf.order, - false, - buf.forRegion, - NULL); - if (buf.mpn == 0) { - return -ENOMEM; - } - - if (copy_to_user((void *)arg, &buf, sizeof buf)) { - return -EFAULT; - } - break; - } - - case MVPKM_UNLOCK_MPN: { - struct MvpkmLockMPN buf; - - if (copy_from_user(&buf, (void *)arg, sizeof buf)) { - return -EFAULT; - } - - if (!LockedListDel(vm, buf.mpn)) { - return -EINVAL; - } - break; - } - - case MVPKM_MAP_WSPHKVA: { - MvpkmMapHKVA mvpkmMapInfo; - HkvaMapInfo mapInfo[WSP_PAGE_COUNT]; - - if (copy_from_user(&mvpkmMapInfo, (void *)arg, sizeof mvpkmMapInfo)) { - return -EFAULT; - } - - if (copy_from_user(mapInfo, (void *)mvpkmMapInfo.mapInfo, sizeof mapInfo)) { - return -EFAULT; - } - - mvpkmMapInfo.hkva = MapWSPHKVA(vm, mapInfo); - BUG_ON(mvpkmMapInfo.hkva == 0); - - if (mvpkmMapInfo.forRegion == MEMREGION_WSP) { - vm->wsp = (WorldSwitchPage *) mvpkmMapInfo.hkva; - } - - if (copy_to_user((void *)arg, &mvpkmMapInfo, sizeof mvpkmMapInfo)) { - return -EFAULT; - } - break; - } - - case MVPKM_RUN_MONITOR: { - if (!vm->isMonitorInited) { - vm->isMonitorInited = ((retval = SetupMonitor(vm)) == 0); - } - - if (vm->isMonitorInited) { - retval = RunMonitor(vm); - } - - break; - } - - case MVPKM_ABORT_MONITOR: { - if (!vm->isMonitorInited) { - return -EINVAL; - } - - ASSERT(vm->wsp != NULL); - - Mvpkm_WakeGuest(vm, ACTION_ABORT); - break; - } - - case MVPKM_CPU_INFO: { - struct MvpkmCpuInfo buf; - uint32 mpidr; - -#ifdef CONFIG_ARM_LPAE - DetermineMemAttrLPAE(&buf.attribMAN); - /** - * We need to add support to the LPV monitor for LPAE page tables if we - * want to use it on a LPAE host, due to the costs involved in - * transitioning between LPAE and non-LPAE page tables without Hyp - * assistance. - * - * @knownjira{MVP-2184} - */ - buf.attribL2D.u = 0; -#else - DetermineMemAttrNonLPAE(&buf.attribL2D, &buf.attribMAN); -#endif - /* - * Are MP extensions implemented? See B4-1618 ARM DDI 0406C-2c for - * magic. - */ - ARM_MRC_CP15(MPIDR, mpidr); - - buf.mpExt = mpidr & ARM_CP15_MPIDR_MP; - - if (copy_to_user((int *)arg, &buf, sizeof(struct MvpkmCpuInfo))) { - retval = -EFAULT; - } - break; - } - - default: { - retval = -EINVAL; - break; - } - } - - PRINTK(KERN_INFO "returning from IOCTL(%d) retval = %d %s\n", - cmd, retval, signal_pending(current)?"(pending signal)":"" ); - - return retval; -} - - - -/********************************************************************* - * - * Locked page management - * - *********************************************************************/ - -/* - * Pages locked by the kernel module are remembered so an unlockAll - * operation can be performed when the vmm is closed. The locked page - * identifiers are stored in a red-black tree to support O(log n) - * removal and search (required for /dev/mem-like mmap). - */ - -/** - * @brief Descriptor of a locked page range - */ -typedef struct { - struct { - __u32 mpn : 20; ///< MPN. - __u32 order : 6; ///< Size/alignment exponent for page. - __u32 forRegion : 6; ///< Annotation to identify guest page allocation - } page; - struct rb_node rb; -} LockedPage; - -static void FreeLockedPages(LockedPage *lp); - -/** - * @brief Search for an mpn inside a RB tree of LockedPages. The mpn - * will match a LockedPage as long as it is covered by the - * entry, i.e. in a non-zero order entry it doesn't have to be - * the base MPN. - * - * This must be called with the relevant vm->lockedSem held. - * - * @param root RB tree root. - * @param mpn MPN to search for. - * - * @return reference to LockedPage entry if found, otherwise NULL. - */ -static LockedPage * -LockedListSearch(struct rb_root *root, __u32 mpn) -{ - struct rb_node *n = root->rb_node; - - while (n) { - LockedPage *lp = rb_entry(n, LockedPage, rb); - - if (lp->page.mpn == (mpn & (~0UL << lp->page.order))) { - return lp; - } - - if (mpn < lp->page.mpn) { - n = n->rb_left; - } else { - n = n->rb_right; - } - } - - return NULL; -} - -/** - * @brief Delete an mpn from the list of locked pages. - * - * @param vm Mvpkm module control structure pointer - * @param mpn MPN to be unlocked and freed for reuse - * @return true if list contained MPN and it was deleted from list - */ - -static _Bool -LockedListDel(MvpkmVM *vm, __u32 mpn) -{ - LockedPage *lp; - - down_write(&vm->lockedSem); - - lp = LockedListSearch(&vm->lockedRoot, mpn); - - /* - * The MPN should be in the locked pages RB tree and it should be the - * base of an entry, i.e. we can't fragment existing allocations for - * a VM. - */ - if (lp == NULL || lp->page.mpn != mpn) { - up_write(&vm->lockedSem); - return false; - } - - FreeLockedPages(lp); - - if (lp->page.forRegion == MEMREGION_MAINMEM) { - ATOMIC_SUBV(vm->usedPages, 1U << lp->page.order); - } - - rb_erase(&lp->rb, &vm->lockedRoot); - kfree(lp); - - up_write(&vm->lockedSem); - - return true; -} - -/** - * @brief Scan the list of locked pages to see if an MPN matches. - * - * @param vm Mvpkm module control structure pointer - * @param mpn MPN to check - * - * @return true iff list contains MPN. - */ -static _Bool -LockedListLookup(MvpkmVM *vm, __u32 mpn) -{ - LockedPage *lp; - - down_read(&vm->lockedSem); - - lp = LockedListSearch(&vm->lockedRoot, mpn); - - up_read(&vm->lockedSem); - - return lp != NULL; -} - -/** - * @brief Add a new mpn to the locked pages RB tree. - * - * @param vm control structure pointer - * - * @param mpn mpn of page that was locked with get_user_pages or some sort of - * get that is undone by put_page. - * The mpn is assumed to be non-zero - * @param order size/alignment exponent for page - * @param forRegion Annotation for Page pool to identify guest page allocations - * - * @return false: couldn't allocate internal memory to record mpn in
- * true: successful. - */ -static _Bool -LockedListAdd(MvpkmVM *vm, - __u32 mpn, - __u32 order, - PhysMem_RegionType forRegion) -{ - struct rb_node *parent, **p; - LockedPage *tp, *lp = kmalloc(sizeof *lp, GFP_KERNEL); - - if (!lp) { - return false; - } - - lp->page.mpn = mpn; - lp->page.order = order; - lp->page.forRegion = forRegion; - - down_write(&vm->lockedSem); - - if (forRegion == MEMREGION_MAINMEM) { - ATOMIC_ADDV(vm->usedPages, 1U << order); - } - - /* - * Insert as a red leaf in the tree (see include/linux/rbtree.h). - */ - p = &vm->lockedRoot.rb_node; - parent = NULL; - - while (*p) { - parent = *p; - tp = rb_entry(parent, LockedPage, rb); - - /* - * MPN should not already exist in the tree. - */ - ASSERT(tp->page.mpn != (mpn & (~0UL << tp->page.order))); - - if (mpn < tp->page.mpn) { - p = &(*p)->rb_left; - } else { - p = &(*p)->rb_right; - } - } - - rb_link_node(&lp->rb, parent, p); - - /* - * Restructure tree if necessary (see include/linux/rbtree.h). - */ - rb_insert_color(&lp->rb, &vm->lockedRoot); - - up_write(&vm->lockedSem); - - return true; -} - -/** - * @brief Traverse RB locked tree, freeing every entry. - * - * This must be called with the relevant vm->lockedSem held. - * - * @param node reference to RB node at root of subtree. - */ -static void -LockedListNuke(struct rb_node *node) -{ - while (node) { - if (node->rb_left) { - node = node->rb_left; - } else if (node->rb_right) { - node = node->rb_right; - } else { - /* - * We found a leaf, free it and go back to parent. - */ - LockedPage *lp = rb_entry(node, LockedPage, rb); - - if ((node = rb_parent(node))) { - if (node->rb_left) { - node->rb_left = NULL; - } else { - node->rb_right = NULL; - } - } - - FreeLockedPages(lp); - kfree(lp); - } - } -} - -/** - * @brief Unlock all pages at vm close time. - * - * @param vm control structure pointer - */ -static void -LockedListUnlockAll(MvpkmVM *vm) -{ - - down_write(&vm->lockedSem); - - LockedListNuke(vm->lockedRoot.rb_node); - - ATOMIC_SETV(vm->usedPages, 0); - - up_write(&vm->lockedSem); -} - - -/** - * @brief Allocate zeroed free pages - * - * @param[in] vm which VM the pages are for so they will be freed when the vm - * closes - * @param[in] order log2(number of contiguous pages to allocate) - * @param[in] highmem is it OK to allocate this page in ZONE_HIGHMEM? This - * option should only be specified for pages the host kernel - * will not need to address directly. - * @param[out] hkvaRet where to return host kernel virtual address of the - * allocated pages, if non-NULL, and ONLY IF !highmem. - * @param forRegion Annotation for Page pool to identify guest page allocations - * @return 0: no host memory available
- * else: starting MPN
- * *hkvaRet = filled in - */ -static MPN -AllocZeroedFreePages(MvpkmVM *vm, - uint32 order, - _Bool highmem, - PhysMem_RegionType forRegion, - HKVA *hkvaRet) -{ - MPN mpn; - struct page *page; - - if (order > PAGE_ALLOC_COSTLY_ORDER) { - printk(KERN_WARNING "Order %d allocation for region %d exceeds the safe " - "maximum order %d\n", - order, - forRegion, - PAGE_ALLOC_COSTLY_ORDER); - } - - /* - * Get some pages for the requested range. They will be physically - * contiguous and have the requested alignment. They will also - * have a kernel virtual mapping if !highmem. - * - * We allocate out of ZONE_MOVABLE even though we can't just pick up our - * bags. We do this to support platforms that explicitly configure - * ZONE_MOVABLE, such as the Qualcomm MSM8960, to enable deep power down of - * memory banks. When the kernel attempts to take a memory bank offline, it - * will try and place the pages on the isolate LRU - only pages already on an - * LRU, such as anon/file, can get there, so it will not be able to - * migrate/move our pages (and hence the bank will not be offlined). The - * other alternative is to live withing ZONE_NORMAL, and only have available - * a small fraction of system memory. Long term we plan on hooking the - * offlining callback in mvpkm and perform our own migration with the - * cooperation of the monitor, but we don't have dev board to support this - * today. - * - * @knownjira{MVP-3477} - */ - page = alloc_pages(GFP_USER | __GFP_COMP | __GFP_ZERO | - (highmem ? __GFP_HIGHMEM | __GFP_MOVABLE : 0), - order); - - if (page == NULL) { - return 0; - } - - /* - * Return the corresponding page number. - */ - mpn = page_to_pfn(page); - ASSERT(mpn != 0); - - /* - * Remember to unlock the pages when the FD is closed. - */ - if (!LockedListAdd(vm, mpn, order, forRegion)) { - __free_pages(page, order); - return 0; - } - - if (hkvaRet) { - *hkvaRet = highmem ? 0 : __phys_to_virt(page_to_phys(page)); - } - - return mpn; -} - -/** - * @brief Map already-pinned WSP memory in host kernel virtual address(HKVA) - * space. Assumes 2 world switch pages on an 8k boundary. - * - * @param[in] vm which VM the HKVA Area is to be mapped for - * @param[in] mapInfo array of MPNs and execute permission flags to be used in - inserting a new contiguous map in HKVA space - * @return 0: HKVA space could not be mapped - else: HKVA where mapping was inserted - */ -static HKVA -MapWSPHKVA(MvpkmVM *vm, HkvaMapInfo *mapInfo) -{ - unsigned int i; - struct page **pages = NULL; - struct page **pagesPtr; - pgprot_t prot; - int retval; - int allocateCount = WSP_PAGE_COUNT + 1; // Reserve one page for alignment - int pageIndex = 0; - HKVA dummyPage = (HKVA)NULL; - HKVA start; - HKVA startSegment; - HKVA endSegment; - - /* - * Add one page for alignment purposes in case __get_vm_area returns an - * unaligned address. - */ - ASSERT(allocateCount == 3); - ASSERT_ON_COMPILE(WSP_PAGE_COUNT == 2); - - /* - * NOT_IMPLEMENTED if MapHKVA is called more than once. - */ - BUG_ON(vm->wspHkvaArea); - - /* - * Reserve virtual address space. - */ - vm->wspHkvaArea = __get_vm_area((allocateCount * PAGE_SIZE), VM_ALLOC, MODULES_VADDR, MODULES_END); - if (!vm->wspHkvaArea) { - return 0; - } - - pages = kmalloc(allocateCount * sizeof(struct page *), GFP_TEMPORARY); - if (!pages) { - goto err; - } - pagesPtr = pages; - - /* - * Use a dummy page to boundary align the section, if needed. - */ - dummyPage = __get_free_pages(GFP_KERNEL, 0); - if (!dummyPage) { - goto err; - } - vm->wspHKVADummyPage = dummyPage; - - /* - * Back every entry with the dummy page. - */ - for (i = 0; i < allocateCount; i++) { - pages[i] = virt_to_page(dummyPage); - } - - /* - * World switch pages must not span a 1MB boundary in order to maintain only - * a single L2 page table. - */ - start = (HKVA)vm->wspHkvaArea->addr; - startSegment = start & ~(ARM_L1D_SECTION_SIZE - 1); - endSegment = (start + PAGE_SIZE) & ~(ARM_L1D_SECTION_SIZE - 1); - /* - * Insert dummy page at pageIndex, if needed. - */ - pageIndex = (startSegment != endSegment); - - /* - * Back the rest with the actual world switch pages - */ - for (i = pageIndex; i < pageIndex + WSP_PAGE_COUNT; i++) { - pages[i] = pfn_to_page(mapInfo[i - pageIndex].mpn); - } - - /* - * Given the lack of functionality in the kernel for being able to mark - * mappings for a given vm area with different sets of protection bits, - * we simply mark the entire vm area as PAGE_KERNEL_EXEC for now - * (i.e., union of all the protection bits). Given that the kernel - * itself does something similar while loading modules, this should be a - * reasonable workaround for now. In the future, we should set the - * protection bits to strictly adhere to what has been requested in the - * mapInfo parameter. - */ - prot = PAGE_KERNEL_EXEC; - - retval = map_vm_area(vm->wspHkvaArea, prot, &pagesPtr); - if (retval < 0) { - goto err; - } - - kfree(pages); - - return (HKVA)(vm->wspHkvaArea->addr) + pageIndex * PAGE_SIZE; - -err: - if (dummyPage) { - free_pages(dummyPage, 0); - vm->wspHKVADummyPage = (HKVA)NULL; - } - - if (pages) { - kfree(pages); - } - - free_vm_area(vm->wspHkvaArea); - vm->wspHkvaArea = (HKVA)NULL; - - return 0; -} - -static void -UnmapWSPHKVA(MvpkmVM *vm) -{ - if (vm->wspHkvaArea) { - free_vm_area(vm->wspHkvaArea); - } - - if (vm->wspHKVADummyPage) { - free_pages(vm->wspHKVADummyPage, 0); - vm->wspHKVADummyPage = (HKVA)NULL; - } -} - -/** - * @brief Clean and release locked pages - * - * @param lp Reference to the locked pages - */ -static void -FreeLockedPages(LockedPage *lp) -{ - struct page *page; - int count; - - page = pfn_to_page(lp->page.mpn); - count = page_count(page); - - if (count == 0) { - printk(KERN_ERR "%s: found locked page with 0 reference (mpn %05x)\n", - __func__, lp->page.mpn); - return; - } - - if (count == 1) { - int i; - - /* - * There is no other user for this page, clean it. - * - * We don't bother checking if the page was highmem or not, clear_highmem - * works for both. - * We clear the content of the page, and rely on the fact that the previous - * worldswitch has cleaned the potential VIVT I-CACHE. - */ - for (i = 0; i < (1 << lp->page.order); i++) { - clear_highpage(page + i); - } - } else if (lp->page.forRegion != MEMREGION_MAINMEM) { - printk(KERN_WARNING "%s: mpn 0x%05x for region %d is still in use\n", - __func__, lp->page.mpn, lp->page.forRegion); - } - - __free_pages(page, lp->page.order); -} - -/********************************************************************* - * - * Communicate with monitor - * - *********************************************************************/ - -/** - * @brief Register a new monitor page. - * - * @param vm which virtual machine we're running - * @return 0: successful
- * else: -errno - */ -static int -SetupMonitor(MvpkmVM *vm) -{ - int retval; - WorldSwitchPage *wsp = vm->wsp; - - if (!wsp || - wsp->wspHKVA != (HKVA)wsp) { - return -EINVAL; - } - - if ((retval = Mksck_WspInitialize(vm))) { - return retval; - } - - vm->kobj.kset = mvpkmKSet; - retval = kobject_init_and_add(&vm->kobj, &mvpkmKType, NULL, "%d", wsp->guestId); - if (retval) { - goto error; - } - - /* - * Get a reference to this module such that it cannot be unloaded until - * our kobject's release function completes. - */ - - __module_get(THIS_MODULE); - vm->haveKObj = true; - - /* - * Caution: From here on, if we fail, we must not call kobject_put() - * on vm->kobj since that may / will deallocate 'vm'. Unregistering VM - * ksets on failures, is fine and should be done for proper ref counting. - */ - - vm->devicesKSet = kset_create_and_add("devices", NULL, &vm->kobj); - if (!vm->devicesKSet) { - retval = -ENOMEM; - goto error; - } - - vm->miscKSet = kset_create_and_add("misc", NULL, &vm->kobj); - if (!vm->miscKSet) { - kset_unregister(vm->devicesKSet); - vm->devicesKSet = NULL; - retval = -ENOMEM; - goto error; - } - - down_write(&vm->wspSem); - - /* - * The VE monitor needs to issue a SMC to bootstrap Hyp mode. - */ - if (wsp->monType == MONITOR_TYPE_VE) { - /* - * Here we assemble the monitor's HMAIR0 based on wsp->memAttr. We map - * from the inner/outer normal page cacheability attributes obtained - * from DetermineCacheabilityAttribs to the format required in 4.2.8 - * ARM PRD03-GENC-008469 13.0 (see this document for the magic numbers). - * - * Where a choice is available, we opt for read and/or write allocation. - */ - static const uint32 normalCacheAttr2MAIR[4] = { 0x4, 0xf, 0xa, 0xe }; - - uint32 hmair0 = - ((normalCacheAttr2MAIR[wsp->memAttr.innerCache] | - (normalCacheAttr2MAIR[wsp->memAttr.outerCache] << 4)) - << 8 * MVA_MEMORY) | - (0x4 << 8 * MVA_DEVICE); - - /* - * See B4.1.74 ARM DDI 0406C-2c for the HTCR magic. - */ - uint32 htcr = - 0x80000000 | - (wsp->memAttr.innerCache << 8) | - (wsp->memAttr.outerCache << 10) | - (wsp->memAttr.share << 12); - - /** - * @knownjira{MVP-377} - * Set HSCTLR to enable MMU and caches. We should really run the - * monitor WXN, in non-MVP_DEVEL builds. See - * 13.18 ARM PRD03-GENC-008353 11.0 for the magic. - */ - static const uint32 hsctlr = 0x30c5187d; - - register uint32 r0 asm("r0") = wsp->monVA.excVec; - register uint32 r1 asm("r1") = wsp->regSave.ve.mHTTBR; - register uint32 r2 asm("r2") = htcr; - register uint32 r3 asm("r3") = hmair0; - register uint32 r4 asm("r4") = hsctlr; - - asm volatile ( - ".arch_extension sec\n" - "smc 0" - : - : "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4) - : "memory" - ); - } - - /* - * Initialize guest wait-for-interrupt waitqueue. - */ - init_waitqueue_head(&vm->wfiWaitQ); - - MonitorTimer_Setup(vm); - -#ifdef CONFIG_HAS_WAKELOCK - wake_lock_init(&vm->wakeLock, WAKE_LOCK_SUSPEND, "mvpkm"); -#endif - - wsp->mvpkmVersion = MVP_VERSION_CODE; - up_write(&vm->wspSem); - /* - * Ensure coherence of monitor loading and page tables. - */ - flush_cache_all(); - return 0; - -error: - Mksck_WspRelease(wsp); - vm->wsp = NULL; - return retval; -} - -/** - * @brief dummy function to drop the info parameter - * @param info ignored - */ -static -void FlushAllCpuCaches(void *info) -{ - flush_cache_all(); -} - -/** - * @brief return to where monitor called worldswitch - * - * @param vm which virtual machine we're running - * @return 0: successful, just call back when ready
- * 1: successful, process code in WSP_Params(wsp)->callno
- * else: -errno - */ -static int -RunMonitor(MvpkmVM *vm) -{ - int ii; - unsigned long flags; - WorldSwitchPage *wsp = vm->wsp; - int retval = 0; - - ASSERT(wsp); - -#ifdef CONFIG_HAS_WAKELOCK - wake_lock(&vm->wakeLock); -#endif - - /* - * Set VCPUThread affinity - */ - if (cpumask_intersects(to_cpumask(vcpuAffinity), cpu_active_mask)) { - set_cpus_allowed_ptr(current, to_cpumask(vcpuAffinity)); - } - - /* - * Record the the current task structure, so an ABORT will know, - * who to wake. - */ - down_write(&vm->monThreadTaskSem); - vm->monThreadTask = get_current(); - up_write(&vm->monThreadTaskSem); - - /* - * Keep going as long as the monitor is in critical section or - * there are no pending signals such as SIGINT or SIGKILL. Block - * interrupts before checking so any IPI sent will remain pending - * if our check just misses detecting the signal. - */ - local_irq_save(flags); - while (wsp->critSecCount > 0 || - (!signal_pending(current) && - !(ATOMIC_GETO(wsp->hostActions) & ACTION_ABORT))) { - /* - * ARMv7 Performance counters are per CPU core and might be disabled over - * CPU core sleep if there is nothing else in the system to re-enable - * them, so now that we have been allocated a CPU core to run the guest, - * enable them and in particular the TSC (CCNT) which is used for monitor - * timing between world switches. - */ - { - uint32 pmnc; - uint32 pmcnt; - - /* make sure that the Performance Counters are enabled */ - ARM_MRC_CP15(PERF_MON_CONTROL_REGISTER, pmnc); - if ((pmnc & (ARM_PMNC_E | ARM_PMNC_D)) != (ARM_PMNC_E)) { - pmnc |= ARM_PMNC_E; // Enable TSC - pmnc &= ~ARM_PMNC_D; // Disable cycle count divider - ARM_MCR_CP15(PERF_MON_CONTROL_REGISTER, pmnc); - } - - /* make sure that the CCNT is enabled */ - ARM_MRC_CP15(PERF_MON_COUNT_SET, pmcnt); - if ((pmcnt & ARM_PMCNT_C) != ARM_PMCNT_C) { - pmcnt |= ARM_PMCNT_C; - ARM_MCR_CP15(PERF_MON_COUNT_SET, pmcnt); - } - } - - /* - * Update TSC to RATE64 ratio - */ - { - struct TscToRate64Cb *ttr = &__get_cpu_var(tscToRate64); - wsp->tscToRate64Mult = ttr->mult; - wsp->tscToRate64Shift = ttr->shift; - } - - /* - * Save the time of day for the monitor's timer facility. The timing - * facility in the vmm needs to compute current time in the host linux's - * time representation. It uses the formula: - * now = wsp->switchedAt64 + (uint32)(TSC_READ() - wsp->lowerTSC) - * - * Read the timestamp counter *immediately after* ktime_get() as that - * will give the most consistent offset between reading the hardware - * clock register in ktime_get() and reading the hardware timestamp - * counter with TSC_READ(). - */ - ASSERT_ON_COMPILE(MVP_TIMER_RATE64 == NSEC_PER_SEC); - { - ktime_t now = ktime_get(); - TSC_READ(wsp->switchedAtTSC); - wsp->switchedAt64 = ktime_to_ns(now); - } - - /* - * Save host FPU contents and load monitor contents. - */ - SWITCH_VFP_TO_MONITOR; - - /* - * Call into the monitor to run guest instructions until it wants us to - * do something for it. Note that any hardware interrupt request will - * cause it to volunteer. - */ - switch (wsp->monType) { - case MONITOR_TYPE_LPV: { - uint32 hostVBAR; - - ARM_MRC_CP15(VECTOR_BASE, hostVBAR); - (*wsp->switchToMonitor)(&wsp->regSave); - ARM_MCR_CP15(VECTOR_BASE, hostVBAR); - break; - } - case MONITOR_TYPE_VE: { - register uint32 r1 asm("r1") = wsp->regSave.ve.mHTTBR; - - asm volatile ( - ".word " MVP_STRINGIFY(ARM_INSTR_HVC_A1_ENC(0)) - : "=r" (r1) : "r" (r1) : "r0", "r2", "memory" - ); - break; - } - default: FATAL(); - } - - /* - * Save monitor FPU contents and load host contents. - */ - SWITCH_VFP_TO_HOST; - - /* - * Re-enable local interrupts now that we are back in the host world - */ - local_irq_restore(flags); - - - /* - * Maybe the monitor wrote some messages to monitor->host sockets. - * This will wake the corresponding host threads to receive them. - */ - /** - * @todo This lousy loop is in the critical path. It should be changed - * to some faster algorithm to wake blocked host sockets. - */ - for (ii = 0; ii < MKSCK_MAX_SHARES; ii++) { - if (wsp->isPageMapped[ii]) { - Mksck_WakeBlockedSockets(MksckPage_GetFromIdx(ii)); - } - } - - switch (WSP_Params(wsp)->callno) { - case WSCALL_ACQUIRE_PAGE: { - uint32 i; - - for (i = 0; i < WSP_Params(wsp)->pages.pages; ++i) { - MPN mpn = AllocZeroedFreePages(vm, - WSP_Params(wsp)->pages.order, - true, - WSP_Params(wsp)->pages.forRegion, - NULL); - if (mpn == 0) { - printk(KERN_WARNING "WSCALL_ACQUIRE_PAGE: no order %u pages available\n", - WSP_Params(wsp)->pages.order); - WSP_Params(wsp)->pages.pages = i; - break; - } - - WSP_Params(wsp)->pages.mpns[i] = mpn; - } - - break; - } - case WSCALL_RELEASE_PAGE: { - uint32 i; - - for (i = 0; i < WSP_Params(wsp)->pages.pages; ++i) { - if (!LockedListDel(vm, WSP_Params(wsp)->pages.mpns[i])) { - WSP_Params(wsp)->pages.pages = i; - break; - } - } - - break; - } - case WSCALL_MUTEXLOCK: { - retval = Mutex_Lock((void *)WSP_Params(wsp)->mutex.mtxHKVA, - WSP_Params(wsp)->mutex.mode); - - if (retval < 0) { - WSP_Params(wsp)->mutex.ok = false; - goto monitorExit; - } - - /* - * The locking succeeded. From this point on the monitor - * is in critical section. Even if an interrupt comes - * right here, it must return to the monitor to unlock the - * mutex. - */ - wsp->critSecCount++; - WSP_Params(wsp)->mutex.ok = true; - break; - } - case WSCALL_MUTEXUNLOCK: { - Mutex_Unlock((void *)WSP_Params(wsp)->mutex.mtxHKVA, - WSP_Params(wsp)->mutex.mode); - break; - } - case WSCALL_MUTEXUNLSLEEP: { - /* - * The vcpu has just come back from the monitor. During - * the transition interrupts were disabled. Above, - * however, interrupts were enabled again and it is - * possible that a context switch happened into a thread - * (serve_vmx) that instructed the vcpu thread to - * abort. After returning to this thread the vcpu may - * enter a sleep below never to return from it. To avoid - * this deadlock we need to test the abort flag in - * Mutex_UnlSleepTest. - */ - retval = - Mutex_UnlSleepTest((void *)WSP_Params(wsp)->mutex.mtxHKVA, - WSP_Params(wsp)->mutex.mode, - WSP_Params(wsp)->mutex.cvi, - &wsp->hostActions, - ACTION_ABORT); - if (retval < 0) { - goto monitorExit; - } - break; - } - case WSCALL_MUTEXUNLWAKE: { - Mutex_UnlWake((void *)WSP_Params(wsp)->mutex.mtxHKVA, - WSP_Params(wsp)->mutex.mode, - WSP_Params(wsp)->mutex.cvi, - WSP_Params(wsp)->mutex.all); - break; - } - - /* - * The monitor wants us to block (allowing other host threads to run) - * until an async message is waiting for the monitor to process. - * - * If MvpkmWaitForInt() returns an error, it should only be if there - * is another signal pending (such as SIGINT). So we pretend it - * completed normally, as the monitor is ready to be called again (it - * will see no messages to process and wait again), and return to user - * mode so the signals can be processed. - */ - case WSCALL_WAIT: { -#ifdef CONFIG_HAS_WAKELOCK - if (WSP_Params(wsp)->wait.suspendMode) { - /* guest has ok'ed suspend mode, so release SUSPEND wakelock */ - wake_unlock(&vm->wakeLock); - retval = MvpkmWaitForInt(vm, true); - wake_lock(&vm->wakeLock); - WSP_Params(wsp)->wait.suspendMode = 0; - } else { - /* guest has asked for WFI not suspend so keep holding SUSPEND - * wakelock */ - retval = MvpkmWaitForInt(vm, false); - } -#else - retval = MvpkmWaitForInt(vm, WSP_Params(wsp)->wait.suspendMode); -#endif - if (retval < 0) { - goto monitorExit; - } - break; - } - - /* - * The only reason the monitor returned was because there was a - * pending hardware interrupt. The host serviced and cleared that - * interrupt when we enabled interrupts above. Now we call the - * scheduler in case that interrupt woke another thread, we want to - * allow that thread to run before returning to do more guest code. - */ - case WSCALL_IRQ: { - break; - } - - case WSCALL_GET_PAGE_FROM_VMID: { - MksckPage *mksckPage; - mksckPage = MksckPage_GetFromVmIdIncRefc(WSP_Params(wsp)->pageMgmnt.vmId); - - if (mksckPage) { - int ii; - - WSP_Params(wsp)->pageMgmnt.found = true; - for (ii = 0; ii < MKSCKPAGE_TOTAL; ii++) { - WSP_Params(wsp)->pageMgmnt.mpn[ii] = - vmalloc_to_pfn( (void*)(((HKVA)mksckPage) + ii*PAGE_SIZE) ); - } - - ASSERT(!wsp->isPageMapped[MKSCK_VMID2IDX(mksckPage->vmId)]); - wsp->isPageMapped[MKSCK_VMID2IDX(mksckPage->vmId)] = true; - } else { - WSP_Params(wsp)->pageMgmnt.found = false; - } - break; - } - - case WSCALL_REMOVE_PAGE_FROM_VMID: { - MksckPage *mksckPage; - mksckPage = MksckPage_GetFromVmId(WSP_Params(wsp)->pageMgmnt.vmId); - ASSERT(wsp->isPageMapped[MKSCK_VMID2IDX(mksckPage->vmId)]); - wsp->isPageMapped[MKSCK_VMID2IDX(mksckPage->vmId)] = false; - MksckPage_DecRefc(mksckPage); - break; - } - - /* - * Read current wallclock time. - */ - case WSCALL_READTOD: { - struct timeval nowTV; - do_gettimeofday(&nowTV); - WSP_Params(wsp)->tod.now = nowTV.tv_sec; - WSP_Params(wsp)->tod.nowusec = nowTV.tv_usec; - break; - } - - case WSCALL_LOG: { - int len = strlen(WSP_Params(wsp)->log.messg); - printk(KERN_INFO - "VMM: %s%s", - WSP_Params(wsp)->log.messg, - (WSP_Params(wsp)->log.messg[len-1] == '\n') ? "" : "\n"); - break; - } - - case WSCALL_ABORT: { - retval = WSP_Params(wsp)->abort.status; - goto monitorExit; - } - - case WSCALL_QP_GUEST_ATTACH: { - int32 rc; - QPInitArgs args; - uint32 base; - uint32 nrPages; - - args.id = WSP_Params(wsp)->qp.id; - args.capacity = WSP_Params(wsp)->qp.capacity; - args.type = WSP_Params(wsp)->qp.type; - base = WSP_Params(wsp)->qp.base; - nrPages = WSP_Params(wsp)->qp.nrPages; - - rc = QP_GuestAttachRequest(vm, &args, base, nrPages); - - WSP_Params(wsp)->qp.rc = rc; - WSP_Params(wsp)->qp.id = args.id; - break; - } - - case WSCALL_QP_NOTIFY: { - QPInitArgs args; - - args.id = WSP_Params(wsp)->qp.id; - args.capacity = WSP_Params(wsp)->qp.capacity; - args.type = WSP_Params(wsp)->qp.type; - - WSP_Params(wsp)->qp.rc = QP_NotifyListener(&args); - break; - } - - case WSCALL_MONITOR_TIMER: { - MonitorTimer_Request(&vm->monTimer, WSP_Params(wsp)->timer.when64); - break; - } - - case WSCALL_COMM_SIGNAL: { - Mvpkm_CommEvSignal(&WSP_Params(wsp)->commEvent.transpID, - WSP_Params(wsp)->commEvent.event); - break; - } - - case WSCALL_FLUSH_ALL_DCACHES: { - /* - * Broadcast Flush DCache request to all cores. - * Block while waiting for all of them to get done. - */ - on_each_cpu(FlushAllCpuCaches, NULL, 1); - break; - } - default: { - retval = -EPIPE; - goto monitorExit; - } - } - - /* - * The params.callno callback was handled in kernel mode and completed - * successfully. Repeat for another call without returning to user mode, - * unless there are signals pending. - * - * But first, call the Linux scheduler to switch threads if there is - * some other thread Linux wants to run now. - */ - if (need_resched()) { - schedule(); - } - - /* - * Check if cpus allowed mask has to be updated. - * Updating it must be done outside of an atomic context. - */ - if (cpumask_intersects(to_cpumask(vcpuAffinity), cpu_active_mask) && - !cpumask_equal(to_cpumask(vcpuAffinity), ¤t->cpus_allowed)) { - set_cpus_allowed_ptr(current, to_cpumask(vcpuAffinity)); - } - - local_irq_save(flags); - } - - /* - * There are signals pending so don't try to do any more monitor/guest - * stuff. But since we were at the point of just about to run the monitor, - * return success status as user mode can simply call us back to run the - * monitor again. - */ - local_irq_restore(flags); - -monitorExit: - ASSERT(wsp->critSecCount == 0); - - if (ATOMIC_GETO(wsp->hostActions) & ACTION_ABORT) { - PRINTK(KERN_INFO "Monitor has ABORT flag set.\n"); - retval = ExitStatusHostRequest; - } - -#ifdef CONFIG_HAS_WAKELOCK - wake_unlock(&vm->wakeLock); -#endif - - down_write(&vm->monThreadTaskSem); - vm->monThreadTask = NULL; - up_write(&vm->monThreadTaskSem); - - return retval; -} - -/** - * @brief Guest is waiting for interrupts, sleep if necessary - * - * @param vm which virtual machine we're running - * @param suspend is the guest entering suspend or just WFI? - * @return 0: woken up, hostActions should have pending events - * -ERESTARTSYS: broke out because other signals are pending - * - * This function is called in the VCPU context after the world switch to wait - * for an incoming message. If any message gets queued to this VCPU, the - * sender will wake us up. - */ -int -MvpkmWaitForInt(MvpkmVM *vm, _Bool suspend) -{ - WorldSwitchPage *wsp = vm->wsp; - wait_queue_head_t *q = &vm->wfiWaitQ; - - if (suspend) { - return wait_event_interruptible(*q, ATOMIC_GETO(wsp->hostActions) != 0); - } else { - int ret; - ret = wait_event_interruptible_timeout(*q, ATOMIC_GETO(wsp->hostActions) != 0, 10*HZ); - if (ret == 0) { - printk("MvpkmWaitForInt: guest stuck for 10s in WFI! (hostActions %08x)\n", - ATOMIC_GETO(wsp->hostActions)); - } - return ret > 0 ? 0 : ret; - } -} - - -/** - * @brief Force the guest to evaluate its hostActions flag field - * - * @param vm which guest needs waking - * @param why why should be guest be woken up? - * - * This function updates the hostAction flag field as and wakes up the guest as - * required so that it can evaluate it. The guest could be executing guest - * code in an SMP system, in that case send an IPI; or it could be sleeping, in - * the case wake it up. - */ -void -Mvpkm_WakeGuest(MvpkmVM *vm, int why) -{ - ASSERT(why != 0); - - /* set the host action */ - if (ATOMIC_ORO(vm->wsp->hostActions, why) & why) { - /* guest has already been woken up so no need to do it again */ - return; - } - - /* - * VCPU is certainly in 'wait for interrupt' wait. Wake it up ! - */ -#ifdef CONFIG_HAS_WAKELOCK - /* - * To prevent the system to go in suspend mode before the monitor had a - * chance on being scheduled, we will hold the VM wakelock from now. - * As the wakelocks are not managed as reference counts, this is not an - * an issue to take a wake_lock twice in a row. - */ - wake_lock(&vm->wakeLock); -#endif - - /* - * On a UP system, we ensure the monitor thread isn't blocked. - * - * On an MP system the other CPU might be running the guest. This - * is noop on UP. - * - * When the guest is running, it is an invariant that monThreadTaskSem is not - * held as a write lock, so we should not fail to acquire the lock. - * Mvpkm_WakeGuest may be called from an atomic context, so we can't sleep - * here. - */ - if (down_read_trylock(&vm->monThreadTaskSem)) { - if (vm->monThreadTask) { - wake_up_process(vm->monThreadTask); - kick_process(vm->monThreadTask); - } - up_read(&vm->monThreadTaskSem); - } else { - printk("Unexpected failure to acquire monThreadTaskSem!\n"); - } -} diff --git a/arch/arm/mvp/mvpkm/mvpkm_private.h b/arch/arm/mvp/mvpkm/mvpkm_private.h deleted file mode 100644 index 3dfc8d4..0000000 --- a/arch/arm/mvp/mvpkm/mvpkm_private.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Private interface between user level wrappers and kernel module. - * The communication uses the ioctl linux call. The command operand is one - * of the MVPKM_xxx macros defined below, the custom operand is a pointer - * to the respective structure below. - */ - - -#ifndef _MVPKMPRIVATE_H -#define _MVPKMPRIVATE_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include -/* - * For details how to create ioctl numbers, see - * Documentation/ioctl/ioctl-number.txt. The letter '9' is - * unused. The 0xa0-0xaf block is even more unused. Note however, that - * ioctl numbers are desired to be unique for debug purposes, they - * may conflict. - */ -#define MVP_IOCTL_LETTER '9' -#define MVPKM_DISABLE_FAULT _IO( MVP_IOCTL_LETTER, 0xa0) -#define MVPKM_LOCK_MPN _IOW(MVP_IOCTL_LETTER, 0xa1, MvpkmLockMPN) -#define MVPKM_UNLOCK_MPN _IOW(MVP_IOCTL_LETTER, 0xa2, MvpkmLockMPN) -#define MVPKM_RUN_MONITOR _IO( MVP_IOCTL_LETTER, 0xa3) -#define MVPKM_CPU_INFO _IOR(MVP_IOCTL_LETTER, 0xa4, MvpkmCpuInfo) -#define MVPKM_ABORT_MONITOR _IO( MVP_IOCTL_LETTER, 0xa5) -#define MVPKM_MAP_WSPHKVA _IOW(MVP_IOCTL_LETTER, 0xa7, MvpkmMapHKVA) - -#include "mksck.h" -#include "monva_common.h" -#include "mvpkm_types.h" - -/** - * @brief Operand for the MVPKM_LOCK_MPN call - */ -typedef struct MvpkmLockMPN { - uint32 order; /* IN */ - PhysMem_RegionType forRegion; /* IN */ - uint32 mpn; /* OUT */ -} MvpkmLockMPN; - -/** - * @brief Operand for the MVPKM_MAP_HKVA call - */ -typedef struct MvpkmMapHKVA { - HkvaMapInfo *mapInfo; /* IN */ - PhysMem_RegionType forRegion; /* IN */ - HKVA hkva; /* OUT */ -} MvpkmMapHKVA; - -#define WSP_PAGE_COUNT 2 - -/** - * @brief Operand for the MVPKM_CPU_INFO call - */ -typedef struct MvpkmCpuInfo { - ARM_L2D attribL2D; /* OUT */ - ARM_MemAttrNormal attribMAN; /* OUT */ - _Bool mpExt; /* OUT */ -} MvpkmCpuInfo; - -/** - * @brief These magic numbers mark the beginning and end of the - * special page that is mapped into the virtual address space of MVPD - * when it's monitor coredumper requests an unavailable page. - */ -#define MVPKM_STUBPAGE_BEG 0x78d10c67 -#define MVPKM_STUBPAGE_END 0x8378f3dd -#endif diff --git a/arch/arm/mvp/mvpkm/mvpkm_types.h b/arch/arm/mvp/mvpkm/mvpkm_types.h deleted file mode 100644 index ce23554..0000000 --- a/arch/arm/mvp/mvpkm/mvpkm_types.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Types used in the interface between users, user level wrappers, - * and the kernel module implementation. - */ - - -#ifndef _MVPKMTYPES_H -#define _MVPKMTYPES_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - - -/** - * @brief HkvaMapInfo structure describing the mpn and execute permission - * flag to use to map a given page in HKVA space - */ -typedef struct HkvaMapInfo { - uint32 mpn; - _Bool write; - _Bool exec; -} HkvaMapInfo; - -#endif diff --git a/arch/arm/mvp/mvpkm/nottested.h b/arch/arm/mvp/mvpkm/nottested.h deleted file mode 100644 index 5226a22..0000000 --- a/arch/arm/mvp/mvpkm/nottested.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief NOT_TESTED() and related. - */ - -#ifndef _NOTTESTED_H -#define _NOTTESTED_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include - -#ifdef NOT_TESTED_ENABLED -#define NotTestedEnabled true -#else -#define NotTestedEnabled false -#endif - -#define NOT_TESTED() NOT_TESTED_JIRA(0) -#define NOT_TESTED_JIRA(_tkt,...) NotTested(_tkt, __FILE__, __LINE__) - -void NotTested(int tkt, char const *file, int line); - -#endif diff --git a/arch/arm/mvp/mvpkm/platdefx.h b/arch/arm/mvp/mvpkm/platdefx.h deleted file mode 100644 index 70fb8d7..0000000 --- a/arch/arm/mvp/mvpkm/platdefx.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Basic platform definitions needed various places. - */ - -#ifndef _PLATDEFX_H -#define _PLATDEFX_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define PAGE_ORDER 12 - -#ifndef PAGE_SIZE -#define PAGE_SIZE (1UL << PAGE_ORDER) -#endif -#if PAGE_SIZE != 4096 -#error bad page size PAGE_SIZE -#endif - -#define PA_2_PPN(_pa) ((_pa) / PAGE_SIZE) -#define PPN_2_PA(_ppn) ((_ppn) * PAGE_SIZE) - -#define VMM_DOMAIN 0x0 -#define VMM_DOMAIN_NO_ACCESS 0x3 -#define VMM_DOMAIN_CLIENT 0x1 -#define VMM_DOMAIN_MANAGER 0x4 - -#define INVALID_CVA (-(CVA)1) -#define INVALID_GVA (-(GVA)1) -#define INVALID_MVA (-(MVA)1) -#define INVALID_HKVA (-(HKVA)1) -#define INVALID_HUVA (-(HUVA)1) - -#define INVALID_MPN (((MPN)-1) >> ARM_L2D_SMALL_ORDER) -#define INVALID_PPN (((PPN)-1) >> ARM_L2D_SMALL_ORDER) - -#endif diff --git a/arch/arm/mvp/mvpkm/psr_defs.h b/arch/arm/mvp/mvpkm/psr_defs.h deleted file mode 100644 index 4fa53bc..0000000 --- a/arch/arm/mvp/mvpkm/psr_defs.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Constant definitions for ARM CPSR/SPSR registers. See A2.5 - * ARM DDI 0100I. - */ - -#ifndef _PSR_DEFS_H_ -#define _PSR_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define ARM_PSR_MODE_USER 0x10 -#define ARM_PSR_MODE_FIQ 0x11 -#define ARM_PSR_MODE_IRQ 0x12 -#define ARM_PSR_MODE_SUPERVISOR 0x13 -#define ARM_PSR_MODE_ABORT 0x17 -#define ARM_PSR_MODE_HVC 0x1a -#define ARM_PSR_MODE_UNDEFINED 0x1b -#define ARM_PSR_MODE_SYSTEM 0x1f - -/* Bit 31: N */ -#define ARM_PSR_N (1 << 31) - -/* Bit 30: Z */ -#define ARM_PSR_Z (1 << 30) - -/* Bit 29: C */ -#define ARM_PSR_C (1 << 29) - -/* Bit 28: V */ -#define ARM_PSR_V (1 << 28) - -/* Bit 27: Q */ -#define ARM_PSR_Q (1 << 27) - -#define ARM_PSR_COND_FLAGS \ - (ARM_PSR_N | ARM_PSR_Z | ARM_PSR_C | ARM_PSR_V | ARM_PSR_Q) - -/* Bits 26..25: ITSTATE<1..0> */ -#define ARM_PSR_ITSTATE_LOW MVP_MASK(25, 2) - -/* Bit 24: J */ -#define ARM_PSR_J (1 << 24) - -/* Bits 23..20 are reserved as of ARMv7 */ -#define ARM_PSR_RESERVED MVP_MASK(20, 4) - -/* Bits 19..16: GE<3..0> */ -#define ARM_PSR_GE MVP_MASK(16, 4) - -/* Bits 15..10: ITSTATE<7..2> */ -#define ARM_PSR_ITSTATE_HIGH MVP_MASK(10, 6) -#define ARM_PSR_ITSTATE (ARM_PSR_ITSTATE_LOW | ARM_PSR_ITSTATE_HIGH) - -/* Bit 9: E */ -#define ARM_PSR_E_POS (9) -#define ARM_PSR_E (1 << ARM_PSR_E_POS) - -/* Bit 8: A */ -#define ARM_PSR_A_POS (8) -#define ARM_PSR_A (1 << ARM_PSR_A_POS) - -/* Bit 7: I */ -#define ARM_PSR_I_POS (7) -#define ARM_PSR_I (1 << ARM_PSR_I_POS) - -/* Bit 6: F */ -#define ARM_PSR_F_POS (6) -#define ARM_PSR_F (1 << ARM_PSR_F_POS) - -/* Bit 5: T */ -#define ARM_PSR_T_POS (5) -#define ARM_PSR_T (1 << ARM_PSR_T_POS) - -/* Bits 4..0: Mode */ -#define ARM_PSR_MODE_MASK 0x1f - -#define ARM_PSR_MODE(cpsr) ((cpsr) & ARM_PSR_MODE_MASK) -#define ARM_PSR_USER_MODE(cpsr) (ARM_PSR_MODE(cpsr) == ARM_PSR_MODE_USER) - - -/* - * We shadow the 10 LSBs in the CPSR, with the exception of the T bit, as they - * are managed by the VMM on behalf of the guest and are potentially different - * than the physical CPSR during DE. - */ -#define ARM_PSR_MONITOR_BITS 10 -#define ARM_PSR_MONITOR_MASK (((1 << ARM_PSR_MONITOR_BITS) - 1) & ~ARM_PSR_T) - -#endif /// ifndef _PSR_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/qp.h b/arch/arm/mvp/mvpkm/qp.h deleted file mode 100644 index a8d7ac1..0000000 --- a/arch/arm/mvp/mvpkm/qp.h +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MVP Queue Pairs function and structure declarations - * - * MVP Queue Pairs: - * - * Queue pairs are intended to be a generic bulk data transport mechanism - * between the guest and host kernels. The queue pair abstraction is based - * on two ring buffers (queues) placed on a shared memory region mapped - * into both guest and host kernel address spaces. - * - * NOTE: Queue pairs are SINGLE-READER, SINGLE-WRITER. Any caller is - * responsible for multi-reader/writer serialization!!! - * - * There are a maximum of QP_MAX_QUEUE_PAIRS in the system, with a maximum - * size of QP_MAX_CAPACITY per pair. Each queue pair is identified by - * an ID. - * - * Each peer follows a producer-consumer model in which one side is the - * producer on one queue, and the other side is the consumer on that queue - * (and vice-versa for its pair). - * - * Data is enqueued and dequeued into the pair in transactional stages, - * meaning each enqueue/dequeue can be followed by zero or more - * enqueue/dequeues, but the enqueue/dequeue is not visible to the peer - * until it has been committed with the *Commit() function. - * In PVTCP, for example, this is used to enqueue a short header, then - * followed by 'segments' of iovecs, then followed by a commit. This - * model prevents a peer from reading the header, expecting a payload, - * but not being able to read the payload because it hasn't been - * enqueued yet. - * - * Queue Pair setup: - * - * Before data can be passed, the guest and host kernel must perform - * the following connection handshake: - * - * 1). A host kernel service registers a listener with the queue pair - * subsystem with a callback to be called when guests create - * and attach to a shared memory region. - * - * 2). Guest initiates an QP_Attach() operation to a shared memory region - * keyed by ID. This step allocates memory, maps it into the host - * address space, and optionally notifies any host services who are - * listening for attach requests from the guest (see previous step). - * Host listeners are provided with a copy of the initialization - * arguments used by the guest (id, size, service type). All registered - * listeners are iterated over until one of them handles the attach - * request and acknowledges with QP_SUCCESS. - * - * 3). The registered host callback is called, notifying the host that - * the guest has attached. - * - * 4). The host can now QP_Attach() to the shared memory region with the same - * arguments as the guest. The queue pair is now well formed and enqueues - * and dequeues can proceed on either side. - * - * Queue Pair teardown: - * - * 1). As before, teardowns are initiated by the guest. Hosts can register - * a callback to be called upon detach. Guests initiate a teardown - * through a call to QP_Detach(). - * - * 2). Registered hosts are notified through the aforementioned callback. - * 3). The host service can call QP_Detach() at its own leisure. Memory - * is freed, the queue pair is destroyed. - * - * If at any point the guest unexpectedly shuts down, the host will be - * notified at monitor shutdown time. Memory is freed, and the queue - * pair is destroyed. - * - */ - -#ifndef _QP_H -#define _QP_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -//#define QP_DEBUG 1 - -typedef enum QPState { - QP_STATE_FREE = 0x1, ///< No peers, not memory-backed - QP_STATE_CONNECTED, ///< Both peers attached , memory backed - QP_STATE_GUEST_ATTACHED, ///< Guest allocated memory, host not yet attached - QP_STATE_MAX // leave this at the end! -} QPState; - -typedef struct QPId { - uint32 context; - uint32 resource; -} QPId; - -/* - * Initialization arguments for each queue pair - */ -typedef struct QPInitArgs { - QPId id; ///< Shared memory region ID - uint32 capacity; ///< Total size of shared region in bytes - uint32 type; ///< Type of queue pair (PVTCP, other)... -} QPInitArgs; - -/* - * Placed on the shared region, two per region - */ -typedef struct QHandle { - volatile uint32 head; ///< queue head offset - volatile uint32 tail; ///< queue tail offset - volatile uint32 phantom_head; ///< queue shadow head offset - volatile uint32 phantom_tail; ///< queue shadow tail offset - uint8 data[0]; ///< start of data, runs off - // the struct -} QHandle; - -/* - * Local to each peer - */ -typedef struct QPHandle { - QPId id; ///< shared memory region ID - uint32 capacity; ///< size of region in bytes - QHandle *produceQ; ///< producer queue - QHandle *consumeQ; ///< consumer queue - uint32 queueSize; ///< size of each queue in bytes - uint32 type; ///< type of queue pair - - /* - * Following fields unused by guest - */ - QPState state; - void (*peerDetachCB)(void* data); ///< detach notification callback - void *detachData; ///< data for the detach cb - struct page **pages; ///< page pointers for shared region -} QPHandle; - -/* - * QP Error codes - */ -#define QP_SUCCESS 0 -#define QP_ERROR_NO_MEM (-1) -#define QP_ERROR_INVALID_HANDLE (-2) -#define QP_ERROR_INVALID_ARGS (-3) -#define QP_ERROR_ALREADY_ATTACHED (-4) - -/* - * Hard-coded limits - */ -#define QP_MIN_CAPACITY (PAGE_SIZE * 2) -#define QP_MAX_CAPACITY (1024*1024) // 1M -#define QP_MAX_QUEUE_PAIRS 32 -#define QP_MAX_ID QP_MAX_QUEUE_PAIRS -#define QP_MAX_LISTENERS QP_MAX_QUEUE_PAIRS -#define QP_MAX_PAGES (QP_MAX_CAPACITY/PAGE_SIZE) // 256 pages - -#define QP_INVALID_ID 0xFFFFFFFF -#define QP_INVALID_SIZE 0xFFFFFFFF -#define QP_INVALID_REGION 0xFFFFFFFF -#define QP_INVALID_TYPE 0xFFFFFFFF - -#ifdef __KERNEL__ -/** - * @brief Utility function to sanity check arguments - * @param args argument structure to check - * @return true if arguments are sane, false otherwise - */ -static inline -_Bool QP_CheckArgs(QPInitArgs *args) -{ - if (!args || - !is_power_of_2(args->capacity) || - (args->capacity < QP_MIN_CAPACITY) || - (args->capacity > QP_MAX_CAPACITY) || - !(args->id.resource < QP_MAX_ID || args->id.resource == QP_INVALID_ID) || - (args->type == QP_INVALID_TYPE)) { - return false; - } else { - return true; - } -} -#endif - - -/** - * @brief Utility function to sanity check a queue pair handle - * @param qp handle to the queue pair - * @return true if the handle is sane, false otherwise - */ -static inline -_Bool QP_CheckHandle(QPHandle *qp) -{ -#ifdef MVP_DEBUG - if (!(qp) || - !(qp->produceQ) || - !(qp->consumeQ) || - (qp->state >= (uint32)QP_STATE_MAX) || - !(qp->queueSize < (QP_MAX_CAPACITY/2))) { - return false; - } else { - return true; - } -#else - return true; -#endif -} - - -/** - * @brief Initializes an invalid handle - * @param[in, out] qp handle to the queue pair - */ -static inline void -QP_MakeInvalidQPHandle(QPHandle *qp) -{ - if (!qp) { - return; - } - - qp->id.context = QP_INVALID_ID; - qp->id.resource = QP_INVALID_ID; - qp->capacity = QP_INVALID_SIZE; - qp->produceQ = NULL; - qp->consumeQ = NULL; - qp->queueSize = QP_INVALID_SIZE; - qp->type = QP_INVALID_TYPE; - qp->state = QP_STATE_FREE; - qp->peerDetachCB = NULL; - qp->detachData = NULL; -} - -/* - * Host only - */ -typedef int32 (*QPListener)(const QPInitArgs*); -int32 QP_RegisterListener(const QPListener); -int32 QP_UnregisterListener(const QPListener); -int32 QP_RegisterDetachCB(QPHandle *qp, void (*callback)(void*), void *data); - - -/* - * Host and guest specific implementations, see qp_host.c and qp_guest.c - */ -int32 QP_Attach(QPInitArgs *args, QPHandle** qp); -int32 QP_Detach(QPHandle* qp); -int32 QP_Notify(QPInitArgs *args); - -/* - * Common implementation, see qp_common.c - */ -int32 QP_EnqueueSpace(QPHandle *qp); -int32 QP_EnqueueSegment(QPHandle *qp, const void *buf, size_t length); -int32 QP_EnqueueCommit(QPHandle *qp); -int32 QP_EnqueueReset(QPHandle *qp); - -static inline int32 -QP_EnqueueAtomic(QPHandle *qp, const void *buf, size_t length) -{ - int32 rc; - QP_EnqueueReset(qp); - rc = QP_EnqueueSegment(qp, buf, length); - if (rc < 0) { - return rc; - } else { - QP_EnqueueCommit(qp); - } - return rc; -} - -int32 QP_DequeueSpace(QPHandle *qp); -int32 QP_DequeueSegment(QPHandle *qp, const void *buf, size_t length); -int32 QP_DequeueReset(QPHandle *qp); -int32 QP_DequeueCommit(QPHandle *qp); - -static inline int32 -QP_DequeueAtomic(QPHandle *qp, const void *buf, size_t length) -{ - int32 rc; - QP_DequeueReset(qp); - rc = QP_DequeueSegment(qp, buf, length); - if (rc < 0) { - return rc; - } else { - QP_DequeueCommit(qp); - } - return rc; -} - -/* - * HVC methods and signatures - */ -#define MVP_QP_SIGNATURE 0x53525051 ///< 'QPRS' -#define MVP_QP_ATTACH (MVP_OBJECT_CUSTOM_BASE + 0) ///< attach to a queue pair -#define MVP_QP_DETACH (MVP_OBJECT_CUSTOM_BASE + 1) ///< detach from a queue pair -#define MVP_QP_NOTIFY (MVP_OBJECT_CUSTOM_BASE + 2) ///< notify host of attach -#define MVP_QP_LAST (MVP_OBJECT_CUSTOM_BASE + 3) ///< Number of methods - -/* - * Debug macros - */ -#ifdef QP_DEBUG - #ifdef IN_MONITOR - #define QP_DBG(...) Log(__VA_ARGS__) - #else - #define QP_DBG(...) printk(KERN_INFO __VA_ARGS__) - #endif -#else - #define QP_DBG(...) -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/qp_common.c b/arch/arm/mvp/mvpkm/qp_common.c deleted file mode 100644 index 8d121a1..0000000 --- a/arch/arm/mvp/mvpkm/qp_common.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MVP Queue Pairs common enqueue and dequeue functions. - * Does not include Attach(), and Detach(), as this will be specific - * to host/guest - * implementations. - */ - -#include - -#include "mvp_types.h" -#include "comm_os.h" -#include "qp.h" - - -/** - * @brief Calculate free space in the queue, convenience function - * @param head queue head offset - * @param tail queue tail offset - * @param queueSize size of queue - * @return free space in the queue - */ -static inline int32 -FreeSpace(uint32 head, uint32 tail, uint32 queueSize) { - /* Leave 1 byte free to resolve ambiguity between empty - * and full conditions */ - return (tail >= head) ? (queueSize - (tail - head) - 1) : - (head - tail - 1); -} - - -/** - * @brief Returns available space for enqueue, in bytes - * @param qp handle to the queue pair - * @return available space in bytes in the queue for enqueue operations, - * QP_ERROR_INVALID_HANDLE if the handle is malformed - */ -int32 -QP_EnqueueSpace(QPHandle *qp) -{ - uint32 head; - uint32 phantom; - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - head = qp->produceQ->head; - phantom = qp->produceQ->phantom_tail; - - if (head >= qp->queueSize || - phantom >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - return FreeSpace(head, phantom, qp->queueSize); -} - - -/** - * @brief Enqueues a segment of data into the producer queue - * @param qp handle to the queue pair - * @param buf data to enqueue - * @param bufSize size in bytes to enqueue - * @return number of bytes enqueued on success, appropriate error - * code otherwise - * @sideeffects May move phantom tail pointer - */ -int32 -QP_EnqueueSegment(QPHandle *qp, const void *buf, size_t bufSize) -{ - int32 freeSpace; - uint32 head; - uint32 phantom; - - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - head = qp->produceQ->head; - phantom = qp->produceQ->phantom_tail; - - /* - * This check must go after the assignment above, - * otherwise a malicious guest could write bogus - * offsets to the queue and cause the memcpy to - * copy into unpleasant places. - */ - if (head >= qp->queueSize || - phantom >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - freeSpace = FreeSpace(head, phantom, qp->queueSize); - - if (bufSize <= freeSpace) { - if (bufSize + phantom < qp->queueSize) { - memcpy(qp->produceQ->data + phantom, buf, bufSize); - phantom += bufSize; - } else { - uint32 written = qp->queueSize - phantom; - memcpy(qp->produceQ->data + phantom, buf, written); - memcpy(qp->produceQ->data, (uint8*)buf + written, bufSize - written); - phantom = bufSize - written; - } - } else { - return QP_ERROR_NO_MEM; - } - - qp->produceQ->phantom_tail = phantom; - - return bufSize; -} - - -/** - * @brief Commits any previous EnqueueSegment operations to the queue - * pair - * @param qp handle to the queue pair. - * @return QP_SUCCESS on success, appropriate error code otherwise. - * @sideeffects May move tail pointer - */ -int32 -QP_EnqueueCommit(QPHandle *qp) -{ - uint32 phantom; - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - phantom = qp->produceQ->phantom_tail; - if (phantom >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - qp->produceQ->tail = phantom; - return QP_SUCCESS; -} - - -/** - * @brief Returns any available bytes for dequeue - * @param qp handle to the queue pair - * @return available bytes for dequeue, appropriate error code - * otherwise - */ -int32 -QP_DequeueSpace(QPHandle *qp) -{ - uint32 tail; - uint32 phantom; - int32 bytesAvailable; - - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - tail = qp->consumeQ->tail; - phantom = qp->consumeQ->phantom_head; - - if (tail >= qp->queueSize || - phantom >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - bytesAvailable = (tail - phantom); - if ((int32)bytesAvailable < 0) { - bytesAvailable += qp->queueSize; - } - return bytesAvailable; -} - - -/** - * @brief Dequeues a segment of data from the consumer queue into - * a buffer - * @param qp handle to the queue pair - * @param[out] buf buffer to copy to - * @param bytesDesired number of bytes to dequeue - * @return number of bytes dequeued on success, appropriate error - * code otherwise - * @sideeffects May move phantom head pointer - */ -int32 -QP_DequeueSegment(QPHandle *qp, const void *buf, size_t bytesDesired) -{ - uint32 tail; - uint32 phantom; - int32 bytesAvailable = 0; - - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - tail = qp->consumeQ->tail; - phantom = qp->consumeQ->phantom_head; - - /* - * This check must go after the assignment above, - * otherwise a malicious guest could write bogus - * offsets to the queue and cause the memcpy to - * copy into unpleasant places. - */ - if (tail >= qp->queueSize || - phantom >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - bytesAvailable = (tail - phantom); - if ((int32)bytesAvailable < 0) { - bytesAvailable += qp->queueSize; - } - - if (bytesDesired <= bytesAvailable) { - if (bytesDesired + phantom < qp->queueSize) { - memcpy((void*)buf, qp->consumeQ->data + phantom, bytesDesired); - phantom += bytesDesired; - } else { - uint32 written = qp->queueSize - phantom; - memcpy((void*)buf, qp->consumeQ->data + phantom, written); - memcpy((uint8*)buf + written, qp->consumeQ->data, bytesDesired - written); - phantom = bytesDesired - written; - } - } else { - return QP_ERROR_NO_MEM; - } - - qp->consumeQ->phantom_head = phantom; - - return bytesDesired; -} - - -/** - * @brief Commits any previous DequeueSegment operations to the queue - * pair - * @param qp handle to the queue pair - * @return QP_SUCCESS on success, QP_ERROR_INVALID_HANDLE if the handle - * is malformed - * @sideeffects Moves the head pointer - */ -int32 -QP_DequeueCommit(QPHandle *qp) -{ - uint32 phantom; - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - phantom = qp->consumeQ->phantom_head; - if (phantom >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - qp->consumeQ->head = phantom; - return QP_SUCCESS; -} - - -/** - * @brief Resets the phantom tail pointer and discards any pending - * enqueues - * @param qp handle to the queue pair - * @return QP_SUCCESS on success, QP_ERROR_INVALID_HANDLE if the handle - * is malformed - * @sideeffects Resets the phantom tail pointer - */ -int32 -QP_EnqueueReset(QPHandle *qp) -{ - uint32 tail; - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - tail = qp->produceQ->tail; - if (tail >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - qp->produceQ->phantom_tail = tail; - return QP_SUCCESS; -} - -/** - * @brief Resets the phantom head pointer and discards any pending - * dequeues - * @param qp handle to the queue pair - * @return QP_SUCCESS on success, QP_ERROR_INVALID_HANDLE if the handle - * is malformed - * @sideeffects Resets the phantom head pointer - */ -int32 -QP_DequeueReset(QPHandle *qp) -{ - uint32 head; - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - head = qp->consumeQ->head; - if (head >= qp->queueSize) { - return QP_ERROR_INVALID_HANDLE; - } - - qp->consumeQ->phantom_head = head; - return QP_SUCCESS; -} - -EXPORT_SYMBOL(QP_EnqueueSpace); -EXPORT_SYMBOL(QP_EnqueueSegment); -EXPORT_SYMBOL(QP_EnqueueCommit); -EXPORT_SYMBOL(QP_DequeueSpace); -EXPORT_SYMBOL(QP_DequeueSegment); -EXPORT_SYMBOL(QP_DequeueCommit); -EXPORT_SYMBOL(QP_EnqueueReset); -EXPORT_SYMBOL(QP_DequeueReset); diff --git a/arch/arm/mvp/mvpkm/qp_host_kernel.c b/arch/arm/mvp/mvpkm/qp_host_kernel.c deleted file mode 100644 index c53f315..0000000 --- a/arch/arm/mvp/mvpkm/qp_host_kernel.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief MVP host kernel implementation of the queue pairs API - * - */ - -#include -#include -#include -#include -#include -#include - -#include "mvp.h" -#include "mvpkm_kernel.h" -#include "qp.h" -#include "qp_host_kernel.h" - -static QPHandle queuePairs[QP_MAX_QUEUE_PAIRS]; -static QPListener listeners[QP_MAX_LISTENERS]; - -/* - * Protect listeners and queuePairs. - */ -static DEFINE_MUTEX(qpLock); - -#define QPLock() mutex_lock(&qpLock) -#define QPUnlock() mutex_unlock(&qpLock) - -/** - * @brief Map a vector of pages into virtually contiguous kernel space - * @param vm this vm's vm struct - * @param base base machine page number that lists pages to map - * @param nrPages number of pages to map - * @param[out] qp handle to qp to set up - * @param[out] hkva virtual address mapping - * @return QP_SUCCESS on success, error code otherwise. Mapped address - * is returned in hkva - */ - -static int32 -MapPages(MvpkmVM *vm, - MPN base, - uint32 nrPages, - QPHandle *qp, - HKVA *hkva) -{ - HKVA *va; - uint32 i; - uint32 rc; - struct page *basepfn = pfn_to_page(base); - struct page **pages; - - BUG_ON(!vm); // this would be very bad. - - if (!hkva) { - return QP_ERROR_INVALID_ARGS; - } - - pages = kmalloc(nrPages * sizeof (MPN), GFP_KERNEL); - if (!pages) { - return QP_ERROR_NO_MEM; - } - - /* - * Map in the first page, read out the MPN vector - */ - down_write(&vm->lockedSem); - va = kmap(basepfn); - if (!va) { - rc = QP_ERROR_INVALID_ARGS; - kfree(pages); - qp->pages = NULL; - goto out; - } - - /* - * Grab references and translate MPNs->PFNs - */ - for (i = 0; i < nrPages; i++) { - pages[i] = pfn_to_page(((MPN*)va)[i]); - get_page(pages[i]); - } - - /* - * Clean up the first mapping and remap the entire vector - */ - kunmap(basepfn); - va = vmap(pages, nrPages, VM_MAP, PAGE_KERNEL); - if (!va) { - rc = QP_ERROR_NO_MEM; - for (i = 0; i < nrPages; i++) { - put_page(pages[i]); - } - kfree(pages); - qp->pages = NULL; - goto out; - } else { - *hkva = (HKVA)va; - qp->pages = pages; - } - - /* - * Let's not leak mpns.. - */ - memset(va, 0x0, nrPages * PAGE_SIZE); - - rc = QP_SUCCESS; - -out: - up_write(&vm->lockedSem); - return rc; -} - -/** - * @brief Initialize all free queue pair entries and listeners - */ - -void -QP_HostInit(void) -{ - uint32 i; - - for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { - QP_MakeInvalidQPHandle(&queuePairs[i]); - } - - for (i = 0; i < QP_MAX_LISTENERS; i++) { - listeners[i] = NULL; - } -} - - -/** - * @brief Detaches a guest from a queue pair and notifies - * any registered listeners through the detach callback - * @param id id that guest requested a detach from, detaches all - * queue pairs associated with a VM if the resource id == QP_INVALID_ID - * @return QP_SUCCESS on success, appropriate error code otherwise - */ - -int32 -QP_GuestDetachRequest(QPId id) -{ - QPHandle *qp; - uint32 i; - - if (id.resource >= QP_MAX_ID && id.resource != QP_INVALID_ID) { - return QP_ERROR_INVALID_ARGS; - } - - QPLock(); - - /* - * Invalidate all queue pairs associated with this VM if - * resource == QP_INVALID_ID - */ - if (id.resource == QP_INVALID_ID) { - for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { - qp = &queuePairs[i]; - if (qp->id.context == id.context && qp->peerDetachCB) { - qp->peerDetachCB(qp->detachData); - } - } - } else { - qp = &queuePairs[id.resource]; - if (qp->peerDetachCB) { - qp->peerDetachCB(qp->detachData); - } - } - - QPUnlock(); - - return QP_SUCCESS; -} - - -/** - * @brief Attaches a guest to shared memory region - * @param vm guest to attach - * @param args queue pair args structure: - * - args->id: id of the region to attach to, if id.resource == QP_INVALID_ID, then - * an id is assigned - * - args->capacity: total size of the region in bytes - * - args->type: type of queue pair (e.g PVTCP) - * @param base base machine page number that lists pages to map - * @param nrPages number of pages to map - * @return QP_SUCCESS on success, appropriate error code otherwise. - */ - -int32 -QP_GuestAttachRequest(MvpkmVM *vm, - QPInitArgs *args, - MPN base, - uint32 nrPages) -{ - int32 rc; - HKVA hkva = 0; - QPHandle *qp; - uint32 i; - - if ((!QP_CheckArgs(args)) || - (vm->wsp->guestId != (Mksck_VmId)args->id.context) || - (args->capacity != (nrPages * PAGE_SIZE))) { - return QP_ERROR_INVALID_ARGS; - } - - QP_DBG("%s: Guest requested attach to [%u:%u] capacity: %u type: %x base: %x nrPages: %u\n", - __FUNCTION__, - args->id.context, - args->id.resource, - args->capacity, - args->type, - base, - nrPages); - - QPLock(); - - /* - * Assign a resource id if id == QP_INVALID_ID - */ - if (args->id.resource == QP_INVALID_ID) { - for (i = 0; i < QP_MAX_QUEUE_PAIRS; i++) { - if (queuePairs[i].state == QP_STATE_FREE) { - args->id.resource = i; - QP_DBG("%s: Guest requested anonymous region, assigning resource id %u\n", - __FUNCTION__, args->id.resource); - goto found; - } - } - - rc = QP_ERROR_NO_MEM; - goto out; - } - -found: - qp = queuePairs + args->id.resource; - - if (qp->state != QP_STATE_FREE) { - rc = QP_ERROR_ALREADY_ATTACHED; - goto out; - } - - /* - * Brand new queue pair, allocate some memory to back it and - * initialize the entry - */ - rc = MapPages(vm, base, nrPages, qp, &hkva); - if (rc != QP_SUCCESS) { - goto out; - } - - /* NB: reversed from the guest */ - qp->id = args->id; - qp->capacity = args->capacity; - qp->produceQ = (QHandle*)hkva; - qp->consumeQ = (QHandle*)(hkva + args->capacity/2); - qp->queueSize = args->capacity/2 - sizeof(QHandle); - qp->type = args->type; - qp->state = QP_STATE_GUEST_ATTACHED; - - /* - * The qp is now assumed to be well-formed - */ - QP_DBG("%s: Guest attached to region [%u:%u] capacity: %u HKVA: %x\n", - __FUNCTION__, - args->id.context, - args->id.resource, - args->capacity, - (uint32)hkva); - rc = QP_SUCCESS; - -out: - QPUnlock(); - if (rc != QP_SUCCESS) { - QP_DBG("%s: Failed to attach: %u\n", __FUNCTION__, rc); - } - return rc; -} - - -/** - * @brief Attaches the host to the shared memory region. The guest - * MUST have allocated the shmem region already or else this will fail. - * @param args structure with the shared memory region id to attach to, - * total size of the region in bytes, and type of queue pair (e.g PVTCP) - * @param[in, out] qp handle to the queue pair to return - * @return QP_SUCCESS on success, appropriate error code otherwise - */ - -int32 -QP_Attach(QPInitArgs *args, - QPHandle** qp) -{ - uint32 rc; - - if (!qp || !QP_CheckArgs(args)) { - return QP_ERROR_INVALID_ARGS; - } - - QP_DBG("%s: Attaching to id: [%u:%u] capacity: %u\n", - __FUNCTION__, - args->id.context, - args->id.resource, - args->capacity); - - QPLock(); - *qp = queuePairs + args->id.resource; - - if (!QP_CheckHandle(*qp)) { - *qp = NULL; - rc = QP_ERROR_INVALID_HANDLE; - goto out; - } - - if ((*qp)->state == QP_STATE_CONNECTED) { - rc = QP_ERROR_ALREADY_ATTACHED; - goto out; - } - - if ((*qp)->state != QP_STATE_GUEST_ATTACHED) { - rc = QP_ERROR_INVALID_HANDLE; - goto out; - } - - (*qp)->state = QP_STATE_CONNECTED; - - QP_DBG("%s: Attached!\n", __FUNCTION__); - rc = QP_SUCCESS; - -out: - QPUnlock(); - return rc; -} - -/** - * @brief Detaches the host to the shared memory region. - * @param[in, out] qp handle to the queue pair - * @return QP_SUCCESS on success, appropriate error code otherwise - * @sideeffects Frees memory - */ - -int32 -QP_Detach(QPHandle* qp) -{ - uint32 rc; - uint32 i; - - QPLock(); - if (!QP_CheckHandle(qp)) { - rc = QP_ERROR_INVALID_HANDLE; - goto out; - } - - QP_DBG("%s: Freeing queue pair [%u:%u]\n", - __FUNCTION__, - qp->id.context, - qp->id.resource); - - BUG_ON(!qp->produceQ); - BUG_ON(!qp->pages); - BUG_ON((qp->state != QP_STATE_CONNECTED) && - (qp->state != QP_STATE_GUEST_ATTACHED)); - - vunmap(qp->produceQ); - - for (i = 0; i < qp->capacity/PAGE_SIZE; i++) { - put_page(qp->pages[i]); - } - kfree(qp->pages); - - QP_DBG("%s: Host detached from [%u:%u]\n", - __FUNCTION__, - qp->id.context, - qp->id.resource); - - QP_MakeInvalidQPHandle(qp); - rc = QP_SUCCESS; - -out: - QPUnlock(); - return rc; -} - - -/** - * @brief Detaches and destroys all queue pairs associated with a given guest - * @param vmID which VM to clean up - * @sideeffects Destroys all queue pairs for guest vmID - */ - -void QP_DetachAll(Mksck_VmId vmID) { - QPId id = { - .context = (uint32)vmID, - .resource = QP_INVALID_ID - }; - - QP_DBG("%s: Detaching all queue pairs from vmId context %u\n", __FUNCTION__, vmID); - QP_GuestDetachRequest(id); -} - -/** - * @brief Registers a listener into the queue pair system. Callbacks are - * called with interrupts disabled and must not sleep. - * @param listener listener to be called - * @return QP_SUCCESS on success, QP_ERROR_NO_MEM if no more - * listeners can be registered - */ - -int32 -QP_RegisterListener(const QPListener listener) -{ - uint32 i; - int32 rc = QP_ERROR_NO_MEM; - - QPLock(); - for (i = 0; i < QP_MAX_LISTENERS; i++) { - if (!listeners[i]) { - listeners[i] = listener; - QP_DBG("%s: Registered listener\n", __FUNCTION__); - rc = QP_SUCCESS; - break; - } - } - QPUnlock(); - - return rc; -} - - -/** - * @brief Unregister a listener service from the queue pair system. - * @param listener listener to unregister - * @return QP_SUCCESS on success, appropriate error code otherwise - */ - -int32 -QP_UnregisterListener(const QPListener listener) -{ - uint32 i; - int32 rc = QP_ERROR_INVALID_HANDLE; - - QPLock(); - for (i = 0; i < QP_MAX_LISTENERS; i++) { - if (listeners[i] == listener) { - listeners[i] = NULL; - QP_DBG("%s: Unregistered listener\n", __FUNCTION__); - rc = QP_SUCCESS; - break; - } - } - QPUnlock(); - - return rc; -} - - -/** - * @brief Registers a callback to be called when the guest detaches - * from a queue pair. Callbacks are called with interrupts and - * must not sleep. - * @param qp handle to the queue pair - * @param callback callback to be called - * @param data data to deliver to the callback - * @return QP_SUCCESS on success, appropriate error code otherwise - */ - -int32 -QP_RegisterDetachCB(QPHandle *qp, - void (*callback)(void*), - void *data) -{ - if (!QP_CheckHandle(qp)) { - return QP_ERROR_INVALID_HANDLE; - } - - if (!callback) { - return QP_ERROR_INVALID_ARGS; - } - - qp->peerDetachCB = callback; - qp->detachData = data; - QP_DBG("%s: Registered detach callback\n", __FUNCTION__); - return QP_SUCCESS; -} - - -/** - * @brief Noop on the host, only guests can initiate a notify - * @param args noop - * @return QP_SUCCESS - */ - - -int32 QP_Notify(QPInitArgs *args) { - return QP_SUCCESS; -} - - -/** - * @brief Notify any registered listeners for the given queue pair - * @param args initialization arguments used by the guest - * @return QP_SUCCESS on success, error otherwise - */ - -int32 QP_NotifyListener(QPInitArgs *args) { - int32 i; - QPHandle *qp = NULL; - - if (!QP_CheckArgs(args)) { - return QP_ERROR_INVALID_ARGS; - } - - /* - * Iterate over listeners until one of them reports they handled it - */ - QPLock(); - for (i = 0; i < QP_MAX_LISTENERS; i++) { - if (listeners[i]) { - QP_DBG("Delivering attach event to listener...\n"); - if (listeners[i](args) == QP_SUCCESS) { - break; - } - } - } - - if (i == QP_MAX_LISTENERS) { - /* - * No listener successfully probed this QP. - * The guest DETACH HVC isn't implemented; we need compensate for it - * by deallocating the QP here. - * This is a workaround which assumes, more-or-less correctly, that - * unsuccessful QP probes never lead to subsequent host-attaching. - */ - - qp = &queuePairs[args->id.resource]; - } - - QPUnlock(); - - if (qp) { - QP_Detach(qp); - } - return QP_SUCCESS; -} - - -EXPORT_SYMBOL(QP_Attach); -EXPORT_SYMBOL(QP_Detach); -EXPORT_SYMBOL(QP_RegisterListener); -EXPORT_SYMBOL(QP_UnregisterListener); -EXPORT_SYMBOL(QP_RegisterDetachCB); -EXPORT_SYMBOL(QP_Notify); diff --git a/arch/arm/mvp/mvpkm/qp_host_kernel.h b/arch/arm/mvp/mvpkm/qp_host_kernel.h deleted file mode 100644 index 111524a..0000000 --- a/arch/arm/mvp/mvpkm/qp_host_kernel.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief QP host function prototypes - */ - - -#ifndef _QP_HOST_KERNEL_H -#define _QP_HOST_KERNEL_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -void QP_HostInit(void); -int32 QP_GuestAttachRequest(MvpkmVM *vm, - QPInitArgs *args, - MPN base, - uint32 nr_pages); -int32 QP_GuestDetachRequest(QPId id); -void QP_DetachAll(Mksck_VmId vmID); -int32 QP_NotifyListener(QPInitArgs *args); - -#endif diff --git a/arch/arm/mvp/mvpkm/tsc.h b/arch/arm/mvp/mvpkm/tsc.h deleted file mode 100644 index 0b3149b..0000000 --- a/arch/arm/mvp/mvpkm/tsc.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Time stamp and event counters. - */ - -#ifndef _TSC_H_ -#define _TSC_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "arm_inline.h" - -#define ARM_PMNC_E (1 << 0) -#define ARM_PMNC_D (1 << 3) - -#define ARM_PMCNT_C (1 << 31) - -#define ARM_PMNC_INVALID_EVENT -1 - -#define TSC_READ(_reg) ARM_MRC_CP15(CYCLE_COUNT, (_reg)) -#define TSC_WRITE(_reg) ARM_MCR_CP15(CYCLE_COUNT, (_reg)) - -#endif // ifndef _TSC_H_ diff --git a/arch/arm/mvp/mvpkm/utils.h b/arch/arm/mvp/mvpkm/utils.h deleted file mode 100644 index 1fc56e9..0000000 --- a/arch/arm/mvp/mvpkm/utils.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief General architecture-independent definitions, typedefs, and macros. - */ - -#ifndef _UTILS_H -#define _UTILS_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_WORKSTATION -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define MAX_FILENAME 128 - -// Round address up to given size boundary -// Note: ALIGN() conflicts with Linux - -#define MVP_ALIGN(_v, _n) (((_v) + (_n) - 1) & -(_n)) - -#define ALIGNVA(_addr, _size) MVP_ALIGN(_addr, _size) - -#define alignof(t) offsetof(struct { char c; typeof(t) x; }, x) - -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#define MAX(x,y) ((x) > (y) ? (x) : (y)) - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#define KB(_X_) ((_X_)*1024U) -#define MB(_X_) (KB(_X_)*1024) -#define GB(_X_) (MB(_X_)*1024) - -#define NELEM(x) (sizeof(x)/sizeof((x)[0])) - -/* - * x in [low,high) - * args evaluated once - */ -#define RANGE(x,low,high) \ - ({ \ - typeof(x) _x = (x); \ - typeof(x) _low = (typeof(x))(low); \ - typeof(x) _high =(typeof(x))(high); \ - (_Bool)( (_low <= _x) && (_x < _high)); \ - }) - -#define OBJECTS_PER_PAGE(_type) (PAGE_SIZE / sizeof(_type)) - -#define MA_2_MPN(_ma) ((MPN)((_ma) / PAGE_SIZE)) -#define MPN_2_MA(_mpn) ((MA)((_mpn) * PAGE_SIZE)) - -#define VA_2_VPN(_va) ((_va) / PAGE_SIZE) -#define VPN_2_vA(_vpn) ((_vpn) * PAGE_SIZE) - -/* - * The following convenience macro can be used in a following situation - * - * send(..., &foo, sizeof(foo)) --> send(..., PTR_N_SIZE(foo)) - */ - -#define PTR_N_SIZE(_var) &(_var), sizeof(_var) - - -/* - * - * BIT-PULLING macros - * - */ -#define MVP_BIT(val,n) ( ((val)>>(n))&1) -#define MVP_BITS(val,m,n) (((val)<<(31-(n))) >> ((31-(n))+(m)) ) -#define MVP_EXTRACT_FIELD(w, m, n) MVP_BITS((w), (m), ((m) + (n) - 1)) -#define MVP_MASK(m, n) (MVP_EXTRACT_FIELD(~(uint32)0U, (m), (n)) << (m)) -#define MVP_UPDATE_FIELD(old_val, field_val, m, n) \ - (((old_val) & ~MVP_MASK((m), (n))) | (MVP_EXTRACT_FIELD((field_val), 0, (n)) << (m))) - -/* - * - * 64BIT-PULLING macros - * - */ -#define MVP_BITS64(val,m,n) (((val)<<(63-(n))) >> ((63-(n))+(m)) ) -#define MVP_EXTRACT_FIELD64(w, m, n) MVP_BITS64((w), (m), ((m) + (n) - 1)) -#define MVP_MASK64(m, n) (MVP_EXTRACT_FIELD64(~(uint64)0ULL, (m), (n)) << (m)) -#define MVP_UPDATE_FIELD64(old_val, field_val, m, n) \ - (((old_val) & ~MVP_MASK64((m), (n))) | (MVP_EXTRACT_FIELD64(((uint64)(field_val)), 0ULL, (n)) << (m))) - -/* - * - * BIT-CHANGING macros - * - */ -#define MVP_SETBIT(val,n) ((val)|=(1<<(n))) -#define MVP_CLRBIT(val,n) ((val)&=(~(1<<(n)))) - -/* - * Fixed bit-width sign extension. - */ -#define MVP_SIGN_EXTEND(val,width) \ - (((val) ^ (1 << ((width) - 1))) - (1 << ((width) - 1))) - - -/* - * Assembler helpers. - */ -#define _MVP_HASH # -#define MVP_HASH() _MVP_HASH - -#define _MVP_STRINGIFY(...) #__VA_ARGS__ -#define MVP_STRINGIFY(...) _MVP_STRINGIFY(__VA_ARGS__) - -#ifndef __ASSEMBLER__ - -#include -#include - -/* - * Constant equivalents of build-flags. - * - * Test these when possible instead of using #ifdef so that your code - * gets parsed. - */ -#ifdef MVP_DEBUG -static const _Bool mvpDebug = true; -#else -static const _Bool mvpDebug = false; -#endif - -#ifdef MVP_STATS -static const _Bool mvpStats = true; -#else -static const _Bool mvpStats = false; -#endif - -#ifdef MVP_DEVEL -static const _Bool mvpDevel = true; -#else -static const _Bool mvpDevel = false; -#endif - -#endif - -#endif diff --git a/arch/arm/mvp/mvpkm/ve_defs.h b/arch/arm/mvp/mvpkm/ve_defs.h deleted file mode 100644 index bd1d975..0000000 --- a/arch/arm/mvp/mvpkm/ve_defs.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Virtualization extension definitions. - * - * See ARM PRD03-GENC-008353 11.0. - */ -#ifndef _VE_DEFS_H_ -#define _VE_DEFS_H_ - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define ARM_VE_HSR_EC_BIT_POS 26 -#define ARM_VE_HSR_EC_LENGTH 6 - -#define ARM_VE_HSR_EC_UNKNOWN 0x00 -#define ARM_VE_HSR_EC_WFI_WFE 0x01 -#define ARM_VE_HSR_EC_MCR_MRC_CP15 0x03 -#define ARM_VE_HSR_EC_MCRR_MRRC_CP15 0x04 -#define ARM_VE_HSR_EC_MCR_MRC_CP14 0x05 -#define ARM_VE_HSR_EC_LDC_STC_CP14 0x06 -#define ARM_VE_HSR_EC_HCPTR 0x07 -#define ARM_VE_HSR_EC_MRC_CP10 0x08 -#define ARM_VE_HSR_EC_JAZELLE 0x09 -#define ARM_VE_HSR_EC_BXJ 0x0a -#define ARM_VE_HSR_EC_MRRC_CP14 0x0c -#define ARM_VE_HSR_EC_SVC_HYP 0x11 -#define ARM_VE_HSR_EC_HVC 0x12 -#define ARM_VE_HSR_EC_SMC 0x13 -#define ARM_VE_HSR_EC_IABORT_SND 0x20 -#define ARM_VE_HSR_EC_IABORT_HYP 0x21 -#define ARM_VE_HSR_EC_DABORT_SND 0x24 -#define ARM_VE_HSR_EC_DABORT_HYP 0x25 - -#define ARM_VE_HSR_FS_BIT_POS 0 -#define ARM_VE_HSR_FS_LENGTH 6 - -#define ARM_VE_HSR_FS_TRANS_L1 0x5 -#define ARM_VE_HSR_FS_TRANS_L2 0x6 -#define ARM_VE_HSR_FS_TRANS_L3 0x7 - -#define ARM_VE_HSR_FS_PERM_L1 0xd -#define ARM_VE_HSR_FS_PERM_L2 0xe -#define ARM_VE_HSR_FS_PERM_L3 0xf - -#endif /// ifndef _VE_DEFS_H_ diff --git a/arch/arm/mvp/mvpkm/vfp_switch.S b/arch/arm/mvp/mvpkm/vfp_switch.S deleted file mode 100644 index 49d3987..0000000 --- a/arch/arm/mvp/mvpkm/vfp_switch.S +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#include "arm_defs.h" -#include "platdefx.h" -#include "arm_as_macros.h" - -/** - * @file - * - * @brief Save and Load VFP entire VFP state. - */ - - .text - -/** - * @brief Save VFP context - * @param R0 = save area pointer: - * .long fpexc,fpscr,fpinst,fpinst2,cpacr,fpexc' - * .double d0..d15 - * .double d16..d31 - * Note: VFP is left in an enable state regardless of initial state. - */ - .align 4 - .global SaveVFP -SaveVFP: - /* - * Save registers. GCC does not expect us to preserve R0..R3,R12,LR. - */ - stmdb sp!, {r4-r6} - - /* - * Save Coproc Access Control register. - */ - mrc_p15 COPROC_ACCESS_CONTROL, r5 - - /* - * If CP10/11 are disabled, enable them so we can save VFP state. - * The host (or guest) may have left data in the data registers that - * must be preserved. - */ - orr r2, r5, #CPACR_CP10_CP11_PRIV_ONLY - mcr_p15 COPROC_ACCESS_CONTROL, r2 - isb - - /* - * Follow procedure on AppxB-22 ARM DDI0406B to save FPINST[2]. - * Also enable VFP access with FPEXC_EN. - */ - fmrx r1, fpexc @ get existing FPEXC system register - orr r6, r1, #ARM_VFP_SYSTEM_REG_FPEXC_EX|ARM_VFP_SYSTEM_REG_FPEXC_FP2V|ARM_VFP_SYSTEM_REG_FPEXC_EN -#if !defined(MVP_HOST_CODE_forceon) - fmxr fpexc, r6 @ set FPEXC.EX, .FP2V and .EN - fmrx r6, fpexc @ read them back - tst r6, #ARM_VFP_SYSTEM_REG_FPEXC_EX @ see if either one is valid - beq 1000f @ neither, skip it all - fmrx r3, FPINST @ FPINST is valid, save it - tst r6, #ARM_VFP_SYSTEM_REG_FPEXC_FP2V @ see if FPINST2 is valid - beq 1000f - fmrx r4, FPINST2 @ FPINST2 is valid, save it -1000: -#else - mov r6, r1 -#endif - fmrx r2, FPSCR @ always save FPSCR system register - - /* - * At this point: - * R1 = original FPEXC - * R2 = FPSCR - * R3 = FPINST - * R4 = FPINST2 - * R5 = original CPACR - * R6 = FPEXC readback with FPEXC.EX, .FP2V and .EN set - * telling us whether FPINST/2 are valid - */ - stmia r0!, {r1-r6} - - /* - * Save floating point data registers. - */ - vstmia r0!, {d0-d15} @ Save d0 thru d15 - - /** - * @todo We should probably just read MVFR0 once at boot/initialization - * time and store it in some variable, to save having to do what might - * be expensive coprocessor accesses. - */ - fmrx r1, MVFR0 @ Read Media and VFP Feature Register 0 - and r1, r1, #ARM_VFP_SYSTEM_REG_MVFR0_A_SIMD_MASK @ A_SIMD field - cmp r1, #2 @ 32 x 64bit registers? - bne 2000f - vstmia r0!, {d16-d31} -2000: - - /* - * Restore scratch registers and return. - */ - ldmia sp!, {r4-r6} - mov pc, lr - - -/** - * @brief Load VFP context - * @param R0 = load area pointer: - * .long fpexc,fpscr,fpinst,fpinst2,cpacr,fpexc' - * .double d0..d15 - * .double d16..d31 - * @note VFP is assumed to be in an enabled state on entry. - */ - .align 4 - .global LoadVFP -LoadVFP: - /* - * Save registers. GCC does not expect us to preserve R0..R3,R12,LR. - */ - stmdb sp!, {r4-r6} - - /* - * Get status register contents: - * R1 = original FPEXC - * R2 = FPSCR - * R3 = FPINST - * R4 = FPINST2 - * R5 = original CPACR - * R6 = FPEXC readback with FPEXC.EX, .FP2V and .EN set - * telling us whether FPINST/2 are valid - */ - ldmia r0!, {r1-r6} - - /* - * Restore some initial FP status registers. - */ - fmxr fpexc, r6 @ with FPEXC.EX, .FP2V and .EN set - fmxr FPSCR, r2 @ always load FPSCR system register - - /* - * Follow procedure on AppxB-22 ARM DDI0406B to load FPINST[2]. - */ -#if !defined(MVP_HOST_CODE_forceon) - fmrx r6, fpexc @ initial call might have different bits - @ ... because FPEXC.EX, .FP2V and .EN - @ are forced set by init code in - @ mvpd.c SetupMonitor() - tst r6, #ARM_VFP_SYSTEM_REG_FPEXC_EX @ see if either one is valid - beq 1000f @ neither, skip it all - fmxr FPINST, r3 @ FPINST is valid, save it - tst r6, #ARM_VFP_SYSTEM_REG_FPEXC_FP2V @ see if FPINST2 is valid - beq 1000f - fmxr FPINST2, r4 @ FPINST2 is valid, save it -1000: -#endif - - /* - * Load floating point data registers. - */ - vldmia r0!, {d0-d15} - - /** - * @todo We should probably just read MVFR0 once at boot/initialization - * time and store it in some variable, to save having to do what might - * be expensive coprocessor accesses. - */ - fmrx r3, MVFR0 @ Read Media and VFP Feature Register 0 - and r3, r3, #ARM_VFP_SYSTEM_REG_MVFR0_A_SIMD_MASK @ A_SIMD field - cmp r3, #2 @ 32 x 64bit registers? - bne 2000f - vldmia r0!, {d16-d31} -2000: - - /* - * Now that VFP registers are all loaded, we put the restored values - * back in the registers, possibly disabling the VFP. - */ - fmxr fpexc, r1 @ with original FPEXC.EX, FPEXC.FP2V - @ and FPEXC.EN values - - /* - * Load Coproc Access Control CP10/CP11 enable bits, possibly disabling - * VFP access. - */ - mrc_p15 COPROC_ACCESS_CONTROL, r0 - bic r0, r0, #CPACR_CP10_CP11_MASK - and r5, r5, #CPACR_CP10_CP11_MASK - orr r0, r0, r5 - mcr_p15 COPROC_ACCESS_CONTROL, r0 - isb - - /* - * Restore scratch registers and return. - */ - ldmia sp!, {r4-r6} - mov pc, lr - - .align 4 - .global GetFPEXC -GetFPEXC: - fmrx r0, fpexc @ get existing FPEXC system register - mov pc, lr diff --git a/arch/arm/mvp/mvpkm/vmid.h b/arch/arm/mvp/mvpkm/vmid.h deleted file mode 100644 index dd89965..0000000 --- a/arch/arm/mvp/mvpkm/vmid.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -#ifndef _VMID_H -#define _VMID_H - -/** - * @file - * - * @brief The vmid definition - */ - - - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_HOSTUSER -#define INCLUDE_ALLOW_GUESTUSER -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define VMID_UNDEF (uint16)0xffff -typedef uint16 VmId; - -#endif diff --git a/arch/arm/mvp/mvpkm/worldswitch.h b/arch/arm/mvp/mvpkm/worldswitch.h deleted file mode 100644 index 785f2cd..0000000 --- a/arch/arm/mvp/mvpkm/worldswitch.h +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Definition of the world switch page - * - * Two pages are maintained to facilitate switching from the vmx to - * the monitor - a data and code page. The data page contains: - * - the necessary information about itself (its MPN, KVA, ...) - * - the saved register file of the other world (including some cp15 regs) - * - some information about the monitor's address space (the monVA member) - * that needed right after the w.s before any communication channels - * could have been established - * - a world switch related L2 table of the monitor -- this could be - * elsewhere. - * - * The code page contains: - * - the actual switching code that saves/restores the registers - * - * The world switch data page is mapped into the user, kernel, and the monitor - * address spaces. In case of the user and monitor spaces the global variable - * wsp points to the world switch page (in the vmx and the monitor - * respectively). The kernel address of the world switch page is saved on - * the page itself: wspHKVA. - * - * The kernel virtual address for both code and data pages is mapped into - * the monitor's space temporarily at the time of the actual switch. This is - * needed to provide a stable code and data page while the L1 page table - * base is changing. As the monitor does not need the world switch data page - * at its KVA for its internal operation, that map is severed right after the - * switching to the monitor and re-established before switching back. - */ -#ifndef _WORLDSWITCH_H -#define _WORLDSWITCH_H - -#define INCLUDE_ALLOW_MVPD -#define INCLUDE_ALLOW_VMX -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/** - * @brief Area for saving the monitor/kernel register files. - * - * The order of the registers in this structure was designed to - * facilitate the organization of the switching code. For example - * all Supervisor Mode registers are grouped together allowing the - * @code - * switch to svc, - * stm old svc regs - * ldm new svc regs - * @endcode - * code to work using a single base register for both the store and - * load area. - */ -#define MAX_REGISTER_SAVE_SIZE 464 - -#ifndef __ASSEMBLER__ -typedef struct { - uint32 kSPSR_svc; - uint32 kr13_svc; - uint32 kr14_svc; - uint32 mSPSR_svc; - uint32 mR13_svc; - uint32 mR14_svc; - - uint32 kSPSR_abt; - uint32 kr13_abt; - uint32 kr14_abt; - uint32 mSPSR_abt; - uint32 mR13_abt; - uint32 mR14_abt; - - uint32 kSPSR_und; - uint32 kr13_und; - uint32 kr14_und; - uint32 mSPSR_und; - uint32 mR13_und; - uint32 mR14_und; - - uint32 kSPSR_irq; - uint32 kr13_irq; - uint32 kr14_irq; - uint32 mSPSR_irq; - uint32 mR13_irq; - uint32 mR14_irq; - - uint32 kSPSR_fiq; - uint32 kr8_fiq; - uint32 kr9_fiq; - uint32 kr10_fiq; - uint32 kr11_fiq; - uint32 kr12_fiq; - uint32 kr13_fiq; - uint32 kr14_fiq; - uint32 mSPSR_fiq; - uint32 mR8_fiq; - uint32 mR9_fiq; - uint32 mR10_fiq; - uint32 mR11_fiq; - uint32 mR12_fiq; - uint32 mR13_fiq; - uint32 mR14_fiq; -} BankedRegisterSave; - -/** - * @brief Registers for monitor execution context. - */ -typedef struct { - uint32 mCPSR; - uint32 mR1; - uint32 mR4; - uint32 mR5; - uint32 mR6; - uint32 mR7; - uint32 mR8; - uint32 mR9; - uint32 mR10; - uint32 mR11; - uint32 mSP; - uint32 mLR; // =mPC -} MonitorRegisterSave; - -/** - * @brief LPV monitor register save/restore. - */ -typedef struct { - uint32 kR2; // =kCPSR - uint32 kR4; - uint32 kR5; - uint32 kR6; - uint32 kR7; - uint32 kR8; - uint32 kR9; - uint32 kR10; - uint32 kR11; - uint32 kR13; - uint32 kR14; // =kPC - - BankedRegisterSave bankedRegs; - - uint32 kCtrlReg; - uint32 kTTBR0; - uint32 kDACR; - uint32 kASID; - uint32 kTIDUserRW; - uint32 kTIDUserRO; - uint32 kTIDPrivRW; - uint32 kCSSELR; - uint32 kPMNCIntEn; - uint32 kPMNCCCCNT; - uint32 kPMNCOvFlag; - uint32 kOpEnabled; - uint32 mCtrlReg; - uint32 mTTBR0; - uint32 mASID; - uint32 mTIDUserRW; - uint32 mTIDUserRO; - uint32 mTIDPrivRW; - uint32 mCSSELR; - - MonitorRegisterSave monRegs; -} RegisterSaveLPV; - -/** - * @brief VE monitor register save/restore. - */ -typedef struct { - uint32 mHTTBR; - - uint32 kR3; - uint32 kR4; - uint32 kR5; - uint32 kR6; - uint32 kR7; - uint32 kR8; - uint32 kR9; - uint32 kR10; - uint32 kR11; - uint32 kR12; - uint32 kCPSR; - uint32 kRet; - - BankedRegisterSave bankedRegs; - - uint32 kCSSELR; - uint32 kCtrlReg; - uint32 kTTBR0[2]; - uint32 kTTBR1[2]; - uint32 kTTBRC; - uint32 kDACR; - uint32 kDFSR; - uint32 kIFSR; - uint32 kAuxDFSR; - uint32 kAuxIFSR; - uint32 kDFAR; - uint32 kIFAR; - uint32 kPAR[2]; - uint32 kPRRR; - uint32 kNMRR; - uint32 kASID; - uint32 kTIDUserRW; - uint32 kTIDUserRO; - uint32 kTIDPrivRW; - uint32 mCSSELR; - uint32 mCtrlReg; - uint32 mTTBR0[2]; - uint32 mTTBR1[2]; - uint32 mTTBRC; - uint32 mDACR; - uint32 mDFSR; - uint32 mIFSR; - uint32 mAuxDFSR; - uint32 mAuxIFSR; - uint32 mDFAR; - uint32 mIFAR; - uint32 mPAR[2]; - uint32 mPRRR; - uint32 mNMRR; - uint32 mASID; - uint32 mTIDUserRW; - uint32 mTIDUserRO; - uint32 mTIDPrivRW; - - uint32 mHCR; - uint32 mHDCR; - uint32 mHCPTR; - uint32 mHSTR; - uint32 mVTTBR[2]; - uint32 mVTCR; - - MonitorRegisterSave monRegs; -} RegisterSaveVE; - -typedef union { - unsigned char reserve_space[MAX_REGISTER_SAVE_SIZE]; - RegisterSaveLPV lpv; - RegisterSaveVE ve; -} RegisterSave; - -MY_ASSERTS(REGSAVE, - ASSERT_ON_COMPILE(sizeof(RegisterSave) == MAX_REGISTER_SAVE_SIZE); -) - -/** - * @brief Area for saving the monitor/kernel VFP state. - */ -typedef struct VFPSave { - uint32 fpexc, fpscr, fpinst, fpinst2, cpacr, fpexc_; - - uint64 fpregs[32]; // Hardware requires that this must be 8-byte (64-bit) - // aligned, however the SaveVFP/LoadVFP code does not - // align its pointer before accessing so we don't have - // an 'aligned(8)' attribute here. However, the - // alignment is checked via asserts in SetupMonitor() - // where it initializes the contents. - - // So if the preceding uint32's are changed and fpregs[] - // is no longer 8-byte aligned, the assert will fire. - // Then the uint32's will have to be fixed AND THE CODE - // in SaveVFP/LoadVFP will have to be CHANGED EQUALLY to - // compensate, as simply padding the uint32's (or - // sticking an aligned(8) attribute here) will leave the - // this structure mismatched with the code. - -} VFPSave __attribute__((aligned(8))); - // Keep the aligned(8) attribute here though so the - // VFPSave structures begin on an 8-byte boundary. - -typedef struct WorldSwitchPage WorldSwitchPage; -typedef void (SwitchToMonitor)(RegisterSave *regSave); -typedef void (SwitchToUser)(RegisterSave *regSaveEnd); - -#include "atomic.h" -#include "monva_common.h" -#include "mksck_shared.h" - -struct WorldSwitchPage { - uint32 mvpkmVersion; ///< The version number of mvpkm - - HKVA wspHKVA; ///< host kernel virtual address of this page - ARM_L1D wspKVAL1D; ///< The l1D entry at the above location - - SwitchToMonitor*switchToMonitor;///< entrypoint of the switching function - SwitchToUser *switchToUser; ///< ditto - - MonVA monVA; ///< monitor virtual address space description - union { - ARM_L2D monAttribL2D; ///< {S,TEX,CB} attributes for monitor mappings (LPV) - ARM_MemAttrNormal memAttr; ///< Normal memory attributes for monitor (VE) - }; - - MonitorType monType; ///< the type of the monitor. Used by mvpkm - _Bool allowInts; ///< true: monitor runs with ints enabled as much as possible (normal) - ///< false: monitor runs with ints blocked as much as possible (debug) - - struct { - uint64 switchedAt64; ///< approx time CP15 TSC was set to... - uint32 switchedAtTSC; ///< CP15 TSC value on entry from monitor - uint32 tscToRate64Mult; ///< multiplier to convert TSC_READ()s to our RATE64s - uint32 tscToRate64Shift; ///< shift to convert TSC_READ()s to our RATE64s - }; - - struct { - AtmUInt32 hostActions; ///< actions for monitor on instruction boundary - Mksck_VmId guestId; ///< vmId of the monitor page - }; - - struct { ///< Mksck attributes needed by Mksck_WspRelease() - uint32 critSecCount; ///< if >0 the monitor is in critical section - ///< and expects to regain control - _Bool isPageMapped[MKSCK_MAX_SHARES]; ///< host mksckPages known to the monitor - _Bool guestPageMapped;///< the guest Mksck page has been mapped in MVA space - uint32 isOpened; ///< bitfield indicating which mkscks - ///< are open on the guest's mksckPage. - /* Note that isOpened is per VM not per VCPU. Also note - * that this and other bitfields in the MksckPage structure - * limit the number of sockets to 32. - */ - }; - -#define WSP_PARAMS_SIZE 512 - uint8 params_[WSP_PARAMS_SIZE]; ///< opaque worldswitch call parameters - - RegisterSave regSave; ///< Save area for the worldswitch code below - VFPSave hostVFP; ///< Save areas for monitor/kernel VFP state - VFPSave monVFP; - -__attribute__((aligned(ARM_L2PT_COARSE_SIZE))) - ARM_L2D wspDoubleMap[ARM_L2PT_COARSE_ENTRIES]; ///< maps worldswitch page at its HKVA - uint8 secondHalfPadding[ARM_L2PT_COARSE_SIZE]; -}; - -/* - * These asserts duplicate the assert at the beginning of SetL1L2esc. - */ -MY_ASSERTS(WSP, - ASSERT_ON_COMPILE(offsetof(struct WorldSwitchPage, wspDoubleMap) % - ARM_L2PT_COARSE_SIZE == 0); -) - -extern void SaveVFP(VFPSave *); -extern void LoadVFP(VFPSave *); - -#define SWITCH_VFP_TO_MONITOR \ - do { \ - SaveVFP(&wsp->hostVFP); \ - LoadVFP(&wsp->monVFP); \ - } while(0) - -#define SWITCH_VFP_TO_HOST \ - do { \ - SaveVFP(&wsp->monVFP); \ - LoadVFP(&wsp->hostVFP); \ - } while(0) - -#endif /// __ASSEMBLER__ - -#define OFFSETOF_KR3_REGSAVE_VE_WSP 616 - -#endif /// _WORLDSWITCH_H diff --git a/arch/arm/mvp/mvpkm/wscalls.h b/arch/arm/mvp/mvpkm/wscalls.h deleted file mode 100644 index 4864f21..0000000 --- a/arch/arm/mvp/mvpkm/wscalls.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP Hypervisor Support - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Worldswitch call parameters - */ - -#ifndef _WSCALLS_H -#define _WSCALLS_H - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#define WSCALL_ACQUIRE_PAGE 1 -#define WSCALL_FLUSH_ALL_DCACHES 2 -#define WSCALL_IRQ 3 -#define WSCALL_ABORT 4 -#define WSCALL_LOG 5 -#define WSCALL_WAIT 6 -#define WSCALL_MUTEXLOCK 7 -#define WSCALL_MUTEXUNLOCK 8 -#define WSCALL_MUTEXUNLSLEEP 9 -#define WSCALL_MUTEXUNLWAKE 10 -#define WSCALL_GET_PAGE_FROM_VMID 11 -#define WSCALL_REMOVE_PAGE_FROM_VMID 12 -#define WSCALL_RELEASE_PAGE 13 -#define WSCALL_READTOD 14 -#define WSCALL_QP_GUEST_ATTACH 15 -#define WSCALL_MONITOR_TIMER 16 -#define WSCALL_COMM_SIGNAL 17 -#define WSCALL_QP_NOTIFY 18 -/* - * MVPKM V0.5.2.0 supports all the calls above. If new API calls are - * introduced then make sure that the calling function (probably in - * mkhost.c) checks the mvpkm's version stored in wsp->mvpkmVersion - * and invokes the wscall only when it is supported. - */ - -#define WSCALL_MAX_CALLNO 20 - -#define WSCALL_LOG_MAX 256 - -#define WSCALL_MAX_MPNS 16 - -#include "exitstatus.h" -#include "mutex.h" -#include "mksck_shared.h" -#include "qp.h" -#include "comm_transp.h" -#include "comm_transp_impl.h" - -typedef struct WSParams { - uint32 callno; - union { - /** - * @brief Used for both WSCALL_ACQUIRE_PAGE and WSCALL_RELEASE_PAGE. - */ - struct { - uint16 pages; ///< IN Number of pages - uint16 order; /**< IN Size of each page - - 2^(12+order) sized and aligned - in machine space. - (WSCALL_ACQUIRE_PAGE only) */ - PhysMem_RegionType forRegion; /**< IN Region identifier for pages - (WSCALL_ACQUIRE_PAGE only) */ - MPN mpns[WSCALL_MAX_MPNS]; /**< OUT (on WSCALL_ACQUIRE_PAGE) - IN (on WSCALL_RELEASE_PAGE) - Vector of page base MPNs. */ - } pages; - - union { - MPN mpn; ///< IN MPN to query refcount. - _Bool referenced; ///< OUT Do host page tables contain the MPN? - } refCount; - - struct { - ExitStatus status; ///< IN the final status of the monitor - } abort; - - struct { - int level; - char messg[WSCALL_LOG_MAX]; - } log; - - struct { - HKVA mtxHKVA; ///< IN mutex's host kernel virt addr - MutexMode mode; ///< IN shared or exclusive - uint32 cvi; ///< IN condition variable index - _Bool all; ///< IN wake all waiting threads? - _Bool ok; ///< OUT Mutex_Lock completed - } mutex; - - struct { - Mksck_VmId vmId; ///< IN translate and lock this vmID - _Bool found; /**< OUT true if the lookup was successful, - page is found, and refc incremented */ - MPN mpn[MKSCKPAGE_TOTAL]; ///< OUT array of MPNs of the requested vmId - } pageMgmnt; - - struct { - unsigned int now; ///< OUT current time-of-day seconds - unsigned int nowusec; ///< OUT current time-of-day microseconds - } tod; - - struct { - QPId id; ///< IN/OUT shared memory id - uint32 capacity; ///< IN size of shared region requested - uint32 type; ///< IN type of queue pair - uint32 base; ///< IN base MPN of PA vector page - uint32 nrPages; ///< IN number of pages to map - int32 rc; ///< OUT return code - } qp; - - struct { - CommTranspID transpID; - CommTranspIOEvent event; - } commEvent; - - struct { - uint64 when64; ///< IN timer request - } timer; - - struct { - _Bool suspendMode; ///< Is the guest in suspend mode? - } wait; - - }; ///< anonymous union -} WSParams; - - -/** - * @brief Cast the opaque param_ member of the wsp to WSParams type - * @param wsp_ the world switch page structure pointer - * @return the cast pointer - */ -static inline WSParams* UNUSED -WSP_Params(WorldSwitchPage *wsp_) { - return (WSParams*)(wsp_->params_); -} - -MY_ASSERTS(WSParFn, - ASSERT_ON_COMPILE(sizeof(WSParams) <= WSP_PARAMS_SIZE); -) -#endif diff --git a/arch/arm/mvp/pvtcpkm/COPYING b/arch/arm/mvp/pvtcpkm/COPYING deleted file mode 100644 index 10828e0..0000000 --- a/arch/arm/mvp/pvtcpkm/COPYING +++ /dev/null @@ -1,341 +0,0 @@ - - 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/arch/arm/mvp/pvtcpkm/Kbuild b/arch/arm/mvp/pvtcpkm/Kbuild deleted file mode 100644 index d2ec844..0000000 --- a/arch/arm/mvp/pvtcpkm/Kbuild +++ /dev/null @@ -1,9 +0,0 @@ -# Warning: autogenerated -obj-m := pvtcpkm.o -pvtcpkm-objs := check_kconfig.o pvtcp_off_io_linux.o pvtcp_off_linux.o comm_os_linux.o comm_os_mod_linux.o pvtcp.o pvtcp_off.o pvtcp_off_linux_shim.o - -ccflags-y += -fno-pic -fno-dwarf2-cfi-asm -march=armv7-a -D__linux__ -ccflags-y += -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -DPVTCP_BUILDING_SERVER -ccflags-y += -mfpu=neon -DIN_MODULE -DGPLED_CODE -ccflags-y += --std=gnu89 -O2 -g2 -ggdb -mapcs -fno-optimize-sibling-calls -mno-sched-prolog -ccflags-$(CONFIG_VMWARE_MVP_DEBUG) += -DMVP_DEBUG diff --git a/arch/arm/mvp/pvtcpkm/Makefile b/arch/arm/mvp/pvtcpkm/Makefile deleted file mode 100644 index 16eb389..0000000 --- a/arch/arm/mvp/pvtcpkm/Makefile +++ /dev/null @@ -1 +0,0 @@ -# Warning: autogenerated diff --git a/arch/arm/mvp/pvtcpkm/check_kconfig.c b/arch/arm/mvp/pvtcpkm/check_kconfig.c deleted file mode 100644 index 6fc27a1..0000000 --- a/arch/arm/mvp/pvtcpkm/check_kconfig.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * @brief Check for required kernel configuration - * - * Check to make sure that the kernel options that the MVP hypervisor requires - * have been enabled in the kernel that this kernel module is being built - * against. - */ -#include - -/* - * Minimum kernel version - * - network namespace support is only really functional starting in 2.6.29 - * - Android Gingerbread requires 2.6.35 - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -#error "MVP requires a host kernel newer than 2.6.35" -#endif - -/* module loading ability */ -#ifndef CONFIG_MODULES -#error "MVP requires kernel loadable module support be enabled (CONFIG_MODULES)" -#endif -#ifndef CONFIG_MODULE_UNLOAD -#error "MVP requires kernel module unload support be enabled (CONFIG_MODULE_UNLOAD)" -#endif - -/* sysfs */ -#ifndef CONFIG_SYSFS -#error "MVP requires sysfs support (CONFIG_SYSFS)" -#endif - -/* network traffic isolation */ -#ifndef CONFIG_NAMESPACES -#error "MVP networking support requires namespace support (CONFIG_NAMESPACES)" -#endif -#ifndef CONFIG_NET_NS -#error "MVP networking support requires Network Namespace support to be enabled (CONFIG_NET_NS)" -#endif - -/* TCP/IP networking */ -#ifndef CONFIG_INET -#error "MVP networking requires IPv4 support (CONFIG_INET)" -#endif -#ifndef CONFIG_IPV6 -#error "MVP networking requires IPv6 support (CONFIG_IPV6)" -#endif - -/* VPN support */ -#if !defined(CONFIG_TUN) && !defined(CONFIG_TUN_MODULE) -#error "MVP VPN support requires TUN device support (CONFIG_TUN)" -#endif - -#if !defined(CONFIG_NETFILTER) && !defined(PVTCP_DISABLE_NETFILTER) -#error "MVP networking support requires netfilter support (CONFIG_NETFILTER)" -#endif - -/* Force /proc/config.gz support for eng/userdebug builds */ -#ifdef MVP_DEBUG -#if !defined(CONFIG_IKCONFIG) || !defined(CONFIG_IKCONFIG_PROC) -#error "MVP kernel /proc/config.gz support required for debuggability (CONFIG_IKCONFIG_PROC)" -#endif -#endif - -/* Sanity check we're only dealing with the memory hotplug + migrate and/or - * compaction combo */ -#ifdef CONFIG_MIGRATION -#if defined(CONFIG_NUMA) || defined(CONFIG_CPUSETS) || defined(CONFIG_MEMORY_FAILURE) -#error "MVP not tested with migration features other than CONFIG_MEMORY_HOTPLUG and CONFIG_COMPACTION" -#endif -#endif diff --git a/arch/arm/mvp/pvtcpkm/comm.h b/arch/arm/mvp/pvtcpkm/comm.h deleted file mode 100644 index 877731d..0000000 --- a/arch/arm/mvp/pvtcpkm/comm.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Communication functions based on queue pair transport APIs. - * - * Comm is a shared memory-based mechanism that facilitates the implementation - * of kernel components that require host-to-guest, or guest-to-guest - * communication. - * This facility assumes the availability of a minimal shared memory queue pair - * implementation, such as MVP queue pairs or VMCI queue pairs. The latter must - * provide primitives for queue pair creation and destruction, and reading and - * writing from/to queue pairs. - * Comm assumes that the queue pair (transport) layer is not concerned with - * multi-threading, locking or flow control, and does not require such features. - */ - -#ifndef _COMM_H_ -#define _COMM_H_ - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "comm_os.h" -#include "comm_transp.h" - - -/* Default/maximum Comm timeouts (in milliseconds). */ -#define COMM_MAX_TO 60000ULL -#define COMM_MAX_TO_UNINT (COMM_MAX_TO + 1) - -#define COMM_OPF_SET_ERR(flags) ((flags) |= 128) -#define COMM_OPF_CLEAR_ERR(flags) ((flags) &= 127) -#define COMM_OPF_TEST_ERR(flags) ((flags) & 128) - -#define COMM_OPF_SET_VAL(flags, val) ((flags) |= ((val) & 127)) -#define COMM_OPF_GET_VAL(flags) ((flags) & 127) - -/** - * Packet (header) structure. - * NB: Do not change this structure, especially the first three fields; there - * will be consequences. It may be extended, but it's not recommended: all - * operations carry this header, so it's better kept in its minimal form. - */ - -typedef struct CommPacket { - unsigned int len; // Total length - unsigned char flags; // Operation flags - unsigned char opCode; // Operation to call - unsigned short data16; // Auxiliary data - unsigned long long data64; - unsigned long long data64ex; - union { - struct { - unsigned int data32; - unsigned int data32ex; - }; - unsigned long long data64ex2; - }; -} CommPacket; - - -/* Opaque structure representing a communication channel. */ - -struct CommChannelPriv; -typedef struct CommChannelPriv *CommChannel; - - -/* Input operations associated with a comm channel. */ - -typedef void (*CommOperationFunc)(CommChannel channel, - void *state, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen); - - -/* Helper macros */ - -#define COMM_DEFINE_OP(funcName) \ -void \ -funcName(CommChannel channel, \ - void *state, \ - CommPacket *packet, \ - struct kvec *vec, \ - unsigned int vecLen) - - -/* Comm-based implementations. */ - -typedef struct CommImpl { - struct module *owner; - int (*checkArgs)(CommTranspInitArgs *transpArgs); - void *(*stateCtor)(CommChannel channel); - void (*stateDtor)(void *state); - void *(*dataAlloc)(unsigned int dataLen); - void (*dataFree)(void *data); - const CommOperationFunc *operations; - void (*closeNtf)(void *closeNtfData, - const CommTranspInitArgs *transpArgs, - int inBH); - void *closeNtfData; - void (*activateNtf)(void *activateNtfData, - CommChannel channel); - void *activateNtfData; - unsigned long long openAtMillis; - unsigned long long openTimeoutAtMillis; - CommTranspID ntfCenterID; -} CommImpl; - - -int Comm_Init(unsigned int maxChannels); -int Comm_Finish(unsigned long long *timeoutMillis); -int Comm_RegisterImpl(const CommImpl *impl); -void Comm_UnregisterImpl(const CommImpl *impl); -int Comm_IsActive(CommChannel channel); -CommTranspInitArgs Comm_GetTranspInitArgs(CommChannel channel); -void *Comm_GetState(CommChannel channel); -int Comm_Dispatch(CommChannel channel); -unsigned int Comm_DispatchAll(void); -void Comm_Put(CommChannel channel); -void Comm_DispatchUnlock(CommChannel channel); -int Comm_Lock(CommChannel channel); -void Comm_Unlock(CommChannel channel); -int Comm_Zombify(CommChannel channel, int inBH); - -int -Comm_Alloc(const CommTranspInitArgs *transpArgs, - const CommImpl *impl, - int inBH, - CommChannel *newChannel); - - -int -Comm_Write(CommChannel channel, - const CommPacket *packet, - unsigned long long *timeoutMillis); - -int -Comm_WriteVec(CommChannel channel, - const CommPacket *packet, - struct kvec **vec, - unsigned int *vecLen, - unsigned long long *timeoutMillis, - unsigned int *iovOffset); - -unsigned int Comm_RequestInlineEvents(CommChannel channel); -unsigned int Comm_ReleaseInlineEvents(CommChannel channel); - -#endif // _COMM_H_ diff --git a/arch/arm/mvp/pvtcpkm/comm_os.h b/arch/arm/mvp/pvtcpkm/comm_os.h deleted file mode 100644 index 91305f1..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_os.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Cross-platform base type definitions and function declarations. - * Includes OS-specific base type definitions and function declarations. - */ - -#ifndef _COMM_OS_H_ -#define _COMM_OS_H_ - -/* For-ever timeout constant (in milliseconds). */ -#define COMM_OS_4EVER_TO ((unsigned long long)(~0UL >> 1)) - -/* Condition function prototype. Returns 1: true, 0: false, < 0: error code. */ -typedef int (*CommOSWaitConditionFunc)(void *arg1, void *arg2); - -/* Dispatch function prototype. Called by input (dispatch) kernel threads. */ -typedef unsigned int (*CommOSDispatchFunc)(void); - -/* Module initialization and exit callback functions. */ -extern int (*commOSModInit)(void *args); -extern void (*commOSModExit)(void); - -/* Macro to assign Init and Exit callbacks. */ -#define COMM_OS_MOD_INIT(init, exit) \ - int (*commOSModInit)(void *args) = init; \ - void (*commOSModExit)(void) = exit - - -/* - * OS-specific implementations must provide the following: - * 1. Types: - * CommOSAtomic - * CommOSSpinlock - * CommOSMutex - * CommOSWaitQueue - * CommOSWork - * CommOSWorkFunc - * CommOSList - * CommOSModule - * struct kvec - * - * 2. Definition, initializers: - * CommOSSpinlock_Define() - * - * 3. Functions: - * void CommOS_Debug(const char *format, ...); - * void CommOS_Log(const char *format, ...); - * void CommOS_WriteAtomic(CommOSAtomic *atomic, int val); - * int CommOS_ReadAtomic(CommOSAtomic *atomic); - * int CommOS_AddReturnAtomic(CommOSAtomic *atomic, int val); - * int CommOS_SubReturnAtomic(CommOSAtomic *atomic, int val); - * void CommOS_SpinlockInit(CommOSSpinlock *lock); - * void CommOS_SpinLockBH(CommOSSpinlock *lock); - * int CommOS_SpinTrylockBH(CommOSSpinlock *lock); - * void CommOS_SpinUnlockBH(CommOSSpinlock *lock); - * void CommOS_SpinLock(CommOSSpinlock *lock); - * int CommOS_SpinTrylock(CommOSSpinlock *lock); - * void CommOS_SpinUnlock(CommOSSpinlock *lock); - * void CommOS_MutexInit(CommOSMutex *mutex); - * void CommOS_MutexLock(CommOSMutex *mutex); - * int CommOS_MutexLockUninterruptible(CommOSMutex *mutex); - * int CommOS_MutexTrylock(CommOSMutex *mutex); - * void CommOS_MutexUnlock(CommOSMutex *mutex); - * void CommOS_WaitQueueInit(CommOSWaitQueue *wq); - * CommOS_DoWait(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc cond, - * void *condArg1, - * void *condArg2, - * unsigned long long *timeoutMillis, - * int interruptible); - * int CommOS_Wait(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc func, - * void *funcArg1, - * void *funcArg2, - * unsigned long long *timeoutMillis); - * int CommOS_WaitUninterruptible(CommOSWaitQueue *wq, - * CommOSWaitConditionFunc func, - * void *funcArg1, - * void *funcArg2, - * unsigned long long *timeoutMillis); - * void CommOS_WakeUp(CommOSWaitQueue *wq); - * void *CommOS_KmallocNoSleep(unsigned int size); - * void *CommOS_Kmalloc(unsigned int size); - * void CommOS_Kfree(void *arg); - * void CommOS_Yield(void); - * unsigned long long CommOS_GetCurrentMillis(void); - * void CommOS_ListInit(CommOSList *list); - * int CommOS_ListEmpty(CommOSList *list); - * void CommOS_ListAdd(CommOSList *list, CommOSList *listElem); - * void CommOS_ListAddTail(CommOSList *list, CommOSList *listElem); - * void int CommOS_ListDel(CommOSList *listElem); - * Macros: - * CommOS_ListForEach(*list, *item, itemListFieldName); - * CommOS_ListForEachSafe(*list, *item, *tmp, itemListFieldName); - * void CommOS_ListSplice(CommOSList *list, CommOSList *listToAdd); - * void CommOS_ListSpliceTail(CommOSList *list, CommOSList *listToAdd); - * CommOSModule CommOS_ModuleSelf(void); - * int CommOS_ModuleGet(CommOSModule module); - * void CommOS_ModulePut(CommOSModule module); - * void CommOS_MemBarrier(void); - * - * These cannot be defined here: a) non-pointer type definitions need size - * information, and b) functions may or may not be inlined, or macros may - * be used instead. - */ - - -#ifdef __linux__ -#include "comm_os_linux.h" -#else -#error "Unsupported OS" -#endif - -/* Functions to start and stop the dispatch and aio kernel threads. */ -void CommOS_StopIO(void); -void CommOS_ScheduleDisp(void); -void CommOS_InitWork(CommOSWork *work, CommOSWorkFunc func); -int CommOS_ScheduleAIOWork(CommOSWork *work); -void CommOS_FlushAIOWork(CommOSWork *work); - -int -CommOS_StartIO(const char *dispatchTaskName, - CommOSDispatchFunc dispatchHandler, - unsigned int interval, - unsigned int maxCycles, - const char *aioTaskName); - - -#endif /* _COMM_OS_H_ */ diff --git a/arch/arm/mvp/pvtcpkm/comm_os_linux.c b/arch/arm/mvp/pvtcpkm/comm_os_linux.c deleted file mode 100644 index 61ce929..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_os_linux.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Linux-specific functions/types. - */ - -#include "comm_os.h" - -#define DISPATCH_MAX_CYCLES 8192 - -/* Type definitions */ - -typedef struct workqueue_struct CommOSWorkQueue; - - -/* Static data */ - -static volatile int running; -static int numCpus; -static CommOSWorkQueue *dispatchWQ; -static CommOSDispatchFunc dispatch; -static CommOSWork dispatchWorksNow[NR_CPUS]; -static CommOSWork dispatchWorks[NR_CPUS]; -static unsigned int dispatchInterval = 1; -static unsigned int dispatchMaxCycles = 2048; -static CommOSWorkQueue *aioWQ; - - -/** - * @brief Initializes a workqueue consisting of per-cpu kernel threads. - * @param name workqueue name - * @return workqueue handle if successful, NULL otherwise - */ - -static inline CommOSWorkQueue * -CreateWorkqueue(const char *name) -{ - return create_workqueue(name); -} - - -/** - * @brief Destroys a workqueue and stops its threads. - * @param[in,out] wq workqueue to destroy. - * @return workqueue handle is successful, NULL otherwise. - */ - -static inline void -DestroyWorkqueue(CommOSWorkQueue *wq) -{ - destroy_workqueue(wq); -} - - -/** - * @brief Force execution of a work item. - * @param[in,out] work work item to dequeue. - */ - -static inline void -FlushDelayedWork(CommOSWork *work) -{ - flush_delayed_work(work); -} - - -/** - * @brief Enqueue a work item to a workqueue for execution on a given cpu - * and after the specified interval. - * @param cpu cpu number. If negative, work item is enqueued on current cpu. - * @param[in,out] wq target work queue. - * @param[in,out] work work item to enqueue. - * @param jif delay interval. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -QueueDelayedWorkOn(int cpu, - CommOSWorkQueue *wq, - CommOSWork *work, - unsigned long jif) -{ - if (cpu < 0) { - return !queue_delayed_work(wq, work, jif) ? -1 : 0; - } else { - return !queue_delayed_work_on(cpu, wq, work, jif) ? -1 : 0; - } -} - - -/** - * @brief Enqueues a work item to a workqueue for execution on the current cpu - * and after the specified interval. - * @param[in,out] wq target work queue. - * @param[in,out] work work item to enqueue. - * @param jif delay interval. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -QueueDelayedWork(CommOSWorkQueue *wq, - CommOSWork *work, - unsigned long jif) -{ - return QueueDelayedWorkOn(-1, wq, work, jif); -} - - -/** - * @brief Cancels a queued delayed work item and synchronizes with its - * completion. - * @param[in,out] work work item to cancel - */ - -static inline void -WaitForDelayedWork(CommOSWork *work) -{ - cancel_delayed_work_sync(work); -} - - -/** - * @brief Discards work items queued to the specified workqueue. - * @param[in,out] wq work queue to flush. - */ - -static inline void -FlushWorkqueue(CommOSWorkQueue *wq) -{ - flush_workqueue(wq); -} - - -/** - * @brief Schedules dispatcher threads for immediate execution. - */ - -void -CommOS_ScheduleDisp(void) -{ - CommOSWork *work = &dispatchWorksNow[get_cpu()]; - - put_cpu(); - if (running) { - QueueDelayedWork(dispatchWQ, work, 0); - } -} - - -/** - * @brief Default delayed work callback function implementation. - * Calls the input function specified at initialization. - * @param[in,out] work work item. - */ - -static void -DispatchWrapper(CommOSWork *work) -{ - unsigned int misses; - - for (misses = 0; running && (misses < dispatchMaxCycles); ) { - /* We run for at most dispatchMaxCycles worth of channel no-ops. */ - - if (!dispatch()) { - /* No useful work was done, on any of the channels. */ - - misses++; - if ((misses % 32) == 0) { - CommOS_Yield(); - } - } else { - misses = 0; - } - } - - if (running && - (work >= &dispatchWorks[0]) && - (work <= &dispatchWorks[NR_CPUS - 1])) { - /* - * If still running _and_ this was a regular, time-based run, then - * re-arm the timer. - */ - - QueueDelayedWork(dispatchWQ, work, dispatchInterval); - } -} - - -/** - * @brief Initializes work item with specified callback function. - * @param[in,out] work work queue to initialize. - * @param func work item to initialize the queue with. - */ - -void -CommOS_InitWork(CommOSWork *work, - CommOSWorkFunc func) -{ - INIT_DELAYED_WORK(work, (work_func_t)func); -} - - -/** - * @brief Flush execution of a work item - * @param{in,out] work work item to dequeue - */ -void -CommOS_FlushAIOWork(CommOSWork *work) -{ - if (aioWQ && work) { - FlushDelayedWork(work); - } -} - - -/** - * @brief Queue a work item to the AIO workqueue. - * @param[in,out] work work item to enqueue. - * @return zero if work enqueued, non-zero otherwise. - */ - -int -CommOS_ScheduleAIOWork(CommOSWork *work) -{ - if (running && aioWQ && work) { - return QueueDelayedWork(aioWQ, work, 0); - } - return -1; -} - - -/** - * @brief Initializes the base IO system. - * @param dispatchTaskName dispatch thread(s) name. - * @param dispatchFunc dispatch function. - * @param intervalMillis periodic interval in milliseconds to call dispatch. - * The floor is 1 jiffy, regardless of how small intervalMillis is - * @param maxCycles number of cycles to do adaptive polling before scheduling. - * The maximum number of cycles is DISPATCH_MAX_CYCLES. - * @param aioTaskName AIO thread(s) name. If NULL, AIO threads aren't started. - * @return zero is successful, -1 otherwise. - * @sideeffects Dispatch threads, and if applicable, AIO threads are started. - */ - -int -CommOS_StartIO(const char *dispatchTaskName, // IN - CommOSDispatchFunc dispatchFunc, // IN - unsigned int intervalMillis, // IN - unsigned int maxCycles, // IN - const char *aioTaskName) // IN -{ - int rc; - int cpu; - - if (running) { - CommOS_Debug(("%s: I/O tasks already running.\n", __FUNCTION__)); - return 0; - } - - /* - * OK, let's test the handler against NULL. Though, the whole concept - * of checking for NULL pointers, outside cases where NULL is meaningful - * to the implementation, is relatively useless: garbage, random pointers - * rarely happen to be all-zeros. - */ - - if (!dispatchFunc) { - CommOS_Log(("%s: a NULL Dispatch handler was passed.\n", __FUNCTION__)); - return -1; - } - dispatch = dispatchFunc; - - if (intervalMillis == 0) { - intervalMillis = 4; - } - if ((dispatchInterval = msecs_to_jiffies(intervalMillis)) < 1) { - dispatchInterval = 1; - } - if (maxCycles > DISPATCH_MAX_CYCLES) { - dispatchMaxCycles = DISPATCH_MAX_CYCLES; - } else if (maxCycles > 0) { - dispatchMaxCycles = maxCycles; - } - CommOS_Debug(("%s: Interval millis %u (jif:%u).\n", __FUNCTION__, - intervalMillis, dispatchInterval)); - CommOS_Debug(("%s: Max cycles %u.\n", __FUNCTION__, dispatchMaxCycles)); - - numCpus = num_present_cpus(); - dispatchWQ = CreateWorkqueue(dispatchTaskName); - if (!dispatchWQ) { - CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__, - dispatchTaskName)); - return -1; - } - - if (aioTaskName) { - aioWQ = CreateWorkqueue(aioTaskName); - if (!aioWQ) { - CommOS_Log(("%s: Couldn't create %s task(s).\n", __FUNCTION__, - aioTaskName)); - DestroyWorkqueue(dispatchWQ); - return -1; - } - } else { - aioWQ = NULL; - } - - running = 1; - for (cpu = 0; cpu < numCpus; cpu++) { - CommOS_InitWork(&dispatchWorksNow[cpu], DispatchWrapper); - CommOS_InitWork(&dispatchWorks[cpu], DispatchWrapper); - rc = QueueDelayedWorkOn(cpu, dispatchWQ, - &dispatchWorks[cpu], - dispatchInterval); - if (rc != 0) { - CommOS_StopIO(); - return -1; - } - } - CommOS_Log(("%s: Created I/O task(s) successfully.\n", __FUNCTION__)); - return 0; -} - - -/** - * @brief Stops the base IO system. - * @sideeffects Dispatch threads, and if applicable, AIO threads are stopped. - */ - -void -CommOS_StopIO(void) -{ - int cpu; - - if (running) { - running = 0; - if (aioWQ) { - FlushWorkqueue(aioWQ); - DestroyWorkqueue(aioWQ); - aioWQ = NULL; - } - FlushWorkqueue(dispatchWQ); - for (cpu = 0; cpu < numCpus; cpu++) { - WaitForDelayedWork(&dispatchWorksNow[cpu]); - WaitForDelayedWork(&dispatchWorks[cpu]); - } - DestroyWorkqueue(dispatchWQ); - dispatchWQ = NULL; - CommOS_Log(("%s: I/O tasks stopped.\n", __FUNCTION__)); - } -} diff --git a/arch/arm/mvp/pvtcpkm/comm_os_linux.h b/arch/arm/mvp/pvtcpkm/comm_os_linux.h deleted file mode 100644 index 81ee9d1..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_os_linux.h +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Contains linux-specific type definitions and function declarations - */ - -#ifndef _COMM_OS_LINUX_H_ -#define _COMM_OS_LINUX_H_ - -#include -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -#error "Kernel versions lower than 2.6.20 are not supported" -#endif - -#include -#include -#include -#include -#include -#include - - -/* - * Type definitions. - */ - -typedef atomic_t CommOSAtomic; -typedef spinlock_t CommOSSpinlock; -typedef struct mutex CommOSMutex; -typedef wait_queue_head_t CommOSWaitQueue; -typedef struct delayed_work CommOSWork; -typedef void (*CommOSWorkFunc)(CommOSWork *work); -typedef struct list_head CommOSList; -typedef struct module *CommOSModule; - - -/* - * Initializers. - */ - -#define CommOSSpinlock_Define DEFINE_SPINLOCK - - -#define COMM_OS_DOLOG(...) printk(KERN_INFO __VA_ARGS__) - - -/** - * @brief Logs given arguments in debug builds. - */ - -#if defined(COMM_OS_DEBUG) - #define CommOS_Debug(args) COMM_OS_DOLOG args -#else - #define CommOS_Debug(args) -#endif - - -/** - * @brief Logs given arguments. - */ - -#define CommOS_Log(args) COMM_OS_DOLOG args - - -/** - * @brief Logs function name and location. - */ - -#if defined(COMM_OS_TRACE) -#define TRACE(ptr) \ - do { \ - CommOS_Debug(("%p:%s: at [%s:%d] with arg ptr [0x%p].\n", current, \ - __FUNCTION__, __FILE__, __LINE__, (ptr))); \ - } while (0) -#else -#define TRACE(ptr) -#endif - - -/** - * @brief Write atomic variable - * @param[in,out] atomic variable to write - * @param val new value - */ - -static inline void -CommOS_WriteAtomic(CommOSAtomic *atomic, - int val) -{ - atomic_set(atomic, val); -} - - -/** - * @brief Reads atomic variable - * @param atomic variable to read - * @return value - */ - -static inline int -CommOS_ReadAtomic(CommOSAtomic *atomic) -{ - return atomic_read(atomic); -} - - -/** - * @brief Atomically add value to atomic variable, return new value. - * @param[in,out] atomic variable - * @param val value to add - * @return new value - */ - -static inline int -CommOS_AddReturnAtomic(CommOSAtomic *atomic, - int val) -{ - return atomic_add_return(val, atomic); -} - - -/** - * @brief Atomically substract value from atomic variable, return new value. - * @param[in,out] atomic variable - * @param val value to substract - * @return new value - */ - -static inline int -CommOS_SubReturnAtomic(CommOSAtomic *atomic, - int val) -{ - return atomic_sub_return(val, atomic); -} - - -/** - * @brief Initializes a given lock. - * @param[in,out] lock lock to initialize - */ - -static inline void -CommOS_SpinlockInit(CommOSSpinlock *lock) -{ - spin_lock_init(lock); -} - - -/** - * @brief Locks given lock and disables bottom half processing. - * @param[in,out] lock lock to lock - */ - -static inline void -CommOS_SpinLockBH(CommOSSpinlock *lock) -{ - spin_lock_bh(lock); -} - - -/** - * @brief Attempts to lock the given lock and disable BH processing. - * @param[in,out] lock lock to lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_SpinTrylockBH(CommOSSpinlock *lock) -{ - return !spin_trylock_bh(lock); -} - - -/** - * @brief Unlocks given lock and re-enables BH processing. - * @param[in,out] lock lock to unlock - */ - -static inline void -CommOS_SpinUnlockBH(CommOSSpinlock *lock) -{ - spin_unlock_bh(lock); -} - - -/** - * @brief Locks the given lock. - * @param[in,out] lock lock to lock - */ - -static inline void -CommOS_SpinLock(CommOSSpinlock *lock) -{ - spin_lock(lock); -} - - -/** - * @brief Attempts to lock the given lock. - * @param[in,out] lock lock to try-lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_SpinTrylock(CommOSSpinlock *lock) -{ - return !spin_trylock(lock); -} - - -/** - * @brief Unlocks given lock. - * @param[in,out] lock lock to unlock - */ - -static inline void -CommOS_SpinUnlock(CommOSSpinlock *lock) -{ - spin_unlock(lock); -} - - -/** - * @brief Initializes given mutex. - * @param[in,out] mutex mutex to initialize - */ - -static inline void -CommOS_MutexInit(CommOSMutex *mutex) -{ - mutex_init(mutex); -} - - -/** - * @brief Acquires mutex. - * @param[in,out] mutex mutex to lock - * @return zero if successful, non-zero otherwise (interrupted) - */ - -static inline int -CommOS_MutexLock(CommOSMutex *mutex) -{ - return mutex_lock_interruptible(mutex); -} - - -/** - * @brief Acquires mutex in uninterruptible mode. - * @param[in,out] mutex mutex to lock - */ - -static inline void -CommOS_MutexLockUninterruptible(CommOSMutex *mutex) -{ - mutex_lock(mutex); -} - - -/** - * @brief Attempts to acquire given mutex. - * @param[in,out] mutex mutex to try-lock - * @return zero if successful, non-zero otherwise - */ - -static inline int -CommOS_MutexTrylock(CommOSMutex *mutex) -{ - return !mutex_trylock(mutex); -} - - -/** - * @brief Releases a given mutex. - * @param[in,out] mutex mutex to unlock - */ - -static inline void -CommOS_MutexUnlock(CommOSMutex *mutex) -{ - mutex_unlock(mutex); -} - - -/** - * @brief Initializes a wait queue. - * @param[in,out] wq workqueue to initialize - */ - -static inline void -CommOS_WaitQueueInit(CommOSWaitQueue *wq) -{ - init_waitqueue_head(wq); -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * - a signal is pending - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @param interruptible enable/disable signal pending check - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, if a signal is pending or other error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_DoWait(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis, - int interruptible) -{ - int rc; - DEFINE_WAIT(wait); - long timeout; -#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) - long tmpTimeout; - long retTimeout; - const unsigned int interval = 50; -#endif - - if (!timeoutMillis) { - return -1; - } - if ((rc = cond(condArg1, condArg2)) != 0) { - return rc; - } - -#if defined(COMM_OS_LINUX_WAIT_WORKAROUND) - timeout = msecs_to_jiffies(interval < *timeoutMillis ? - interval : (unsigned int)*timeoutMillis); - retTimeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); - - for (; retTimeout >= 0; ) { - prepare_to_wait(wq, &wait, - (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); - if ((rc = cond(condArg1, condArg2))) { - break; - } - if (interruptible && signal_pending(current)) { - rc = -EINTR; - break; - } - if ((tmpTimeout = schedule_timeout(timeout))) { - retTimeout -= (timeout - tmpTimeout); - } else { - retTimeout -= timeout; - } - if (retTimeout < 0) { - retTimeout = 0; - } - } - finish_wait(wq, &wait); - if (rc == 0) { - rc = cond(condArg1, condArg2); - if (rc && (retTimeout == 0)) { - retTimeout = 1; - } - } - *timeoutMillis = (unsigned long long)jiffies_to_msecs(retTimeout); -#else // !defined(COMM_OS_LINUX_WAIT_WORKAROUND) - timeout = msecs_to_jiffies((unsigned int)(*timeoutMillis)); - - for (;;) { - prepare_to_wait(wq, &wait, - (interruptible?TASK_INTERRUPTIBLE:TASK_UNINTERRUPTIBLE)); - if ((rc = cond(condArg1, condArg2)) != 0) { - break; - } - if (interruptible && signal_pending(current)) { - rc = -EINTR; - break; - } - if ((timeout = schedule_timeout(timeout)) == 0) { - rc = 0; - break; - } - } - finish_wait(wq, &wait); - if (rc == 0) { - rc = cond(condArg1, condArg2); - if (rc && (timeout == 0)) { - timeout = 1; - } - } - *timeoutMillis = (unsigned long long)jiffies_to_msecs(timeout); -#endif - - return rc; -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * - a signal is pending - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, if a signal is pending or other error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_Wait(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis) -{ - return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 1); -} - - -/** - * @brief Puts the caller on a wait queue until either of the following occurs: - * - the condition function (predicate) evaluates to TRUE - * - the specified timeout interval elapsed - * @param[in,out] wq wait queue to put item on - * @param cond predicate to test - * @param condArg1 argument 1 for cond - * @param condArg2 argument 2 for cond - * @param[in,out] timeoutMillis timeout interval in milliseconds - * @return 1 if condition was met - * 0 if the timeout interval elapsed - * <0, error set by condition - * @sideeffect timeoutMillis is updated to time remaining - */ - -static inline int -CommOS_WaitUninterruptible(CommOSWaitQueue *wq, - CommOSWaitConditionFunc cond, - void *condArg1, - void *condArg2, - unsigned long long *timeoutMillis) -{ - return CommOS_DoWait(wq, cond, condArg1, condArg2, timeoutMillis, 0); -} - - -/** - * @brief Wakes up task(s) waiting on the given wait queue. - * @param[in,out] wq wait queue. - */ - -static inline void -CommOS_WakeUp(CommOSWaitQueue *wq) -{ - wake_up(wq); -} - - -/** - * @brief Allocates kernel memory of specified size; does not sleep. - * @param size size to allocate. - * @return Address of allocated memory or NULL if the allocation fails. - */ - -static inline void * -CommOS_KmallocNoSleep(unsigned int size) -{ - return kmalloc(size, GFP_ATOMIC); -} - - -/** - * @brief Allocates kernel memory of specified size; may sleep. - * @param size size to allocate. - * @return Address of allocated memory or NULL if the allocation fails. - */ - -static inline void * -CommOS_Kmalloc(unsigned int size) -{ - return kmalloc(size, GFP_KERNEL); -} - - -/** - * @brief Frees previously allocated kernel memory. - * @param obj object to free. - */ - -static inline void -CommOS_Kfree(void *obj) -{ - if (obj) { - kfree(obj); - } -} - - -/** - * @brief Yields the current cpu to other runnable tasks. - */ - -static inline void -CommOS_Yield(void) -{ - cond_resched(); -} - - -/** - * @brief Gets the current time in milliseconds. - * @return Current time in milliseconds, with precision of at most one tick. - */ - -static inline unsigned long long -CommOS_GetCurrentMillis(void) -{ - return (unsigned long long)jiffies_to_msecs(jiffies); -} - - -/** - * @brief Initializes given list. - * @param list list to initialize. - */ - -static inline void -CommOS_ListInit(CommOSList *list) -{ - INIT_LIST_HEAD(list); -} - - -/** - * @brief Tests if list is empty. - * @param list list to test. - * @return non-zero if empty, zero otherwise. - */ - -#define CommOS_ListEmpty(list) list_empty((list)) - - -/** - * @brief Adds given element to beginning of list. - * @param list list to add to. - * @param elem element to add. - */ - -#define CommOS_ListAdd(list, elem) list_add((elem), (list)) - - -/** - * @brief Adds given element to end of list. - * @param list list to add to. - * @param elem element to add. - */ - -#define CommOS_ListAddTail(list, elem) list_add_tail((elem), (list)) - - -/** - * @brief Deletes given element from its list. - * @param elem element to delete. - */ - -#define CommOS_ListDel(elem) \ - do { \ - list_del((elem)); \ - INIT_LIST_HEAD((elem)); \ - } while (0) - - -/** - * @brief Iterates over a list. - * @param list list to iterate over. - * @param[out] item stores next element. - * @param itemListFieldName name in the item structure storing the list head. - */ - -#define CommOS_ListForEach(list, item, itemListFieldName) \ - list_for_each_entry((item), (list), itemListFieldName) - - -/** - * @brief Iterates safely over a list. - * @param list list to iterate over. - * @param[out] item stores next element. May be deleted in the loop. - * @param[out] tmpItem saves iteration element. - * @param itemListFieldName name in the item structure storing the list head. - */ - -#define CommOS_ListForEachSafe(list, item, tmpItem, itemListFieldName) \ - list_for_each_entry_safe((item), (tmpItem), (list), itemListFieldName) - - -/** - * @brief Combines two lists, adds second list to beginning of first one. - * @param list list to add to. - * @param list2 list to add. - */ - -#define CommOS_ListSplice(list, list2) list_splice((list2), (list)) - - -/** - * @brief Combines two lists, adds second list to end of first one. - * @param list list to add to. - * @param list2 list to add. - */ - -#define CommOS_ListSpliceTail(list, list2) list_splice_tail((list2), (list)) - - -/** - * @brief Gets current module handle. - * @return module handle. - */ - -static inline CommOSModule -CommOS_ModuleSelf(void) -{ - return THIS_MODULE; -} - - -/** - * @brief Retains module. - * @param[in,out] module to retain. - * @return zero if successful, non-zero otherwise. - */ - -static inline int -CommOS_ModuleGet(CommOSModule module) -{ - int rc = 0; - - if (!module) { - goto out; - } - if (!try_module_get(module)) { - rc = -1; - } - -out: - return rc; -} - - -/** - * @brief Releases module. - * @param[in,out] module to release. - */ - -static inline void -CommOS_ModulePut(CommOSModule module) -{ - if (module) { - module_put(module); - } -} - - -/** - * @brief Inserts r/w memory barrier. - */ - -#define CommOS_MemBarrier smp_mb - -#endif /* _COMM_OS_LINUX_H_ */ diff --git a/arch/arm/mvp/pvtcpkm/comm_os_mod_linux.c b/arch/arm/mvp/pvtcpkm/comm_os_mod_linux.c deleted file mode 100644 index e196108..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_os_mod_linux.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Linux-specific module loading, unloading functions. - */ - -#include "comm_os.h" -#include "comm_os_mod_ver.h" - -#include - - -/* Module parameters -- passed as one 'name=value'-list string. */ - -static char modParams[256]; -module_param_string(COMM_OS_MOD_SHORT_NAME, modParams, sizeof modParams, 0644); - - -/** - * @brief Module initialization entry point. Calls the commOSModInit - * function pointer to perform upper layer initialization. - * @return zero if successful, non-zero otherwise. - */ - -static int __init -ModInit(void) -{ - int rc; - - if (!commOSModInit) { - CommOS_Log(("%s: Can't find \'init\' function for module \'" \ - COMM_OS_MOD_SHORT_NAME_STRING "\'.\n", __FUNCTION__)); - return -1; - } - - CommOS_Debug(("%s: Module parameters: [%s].\n", __FUNCTION__, modParams)); - - rc = (*commOSModInit)(modParams); - if (rc == 0) { - CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ - "\' has been successfully initialized.\n", __FUNCTION__)); - } else { - CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ - "\' could not be initialized [%d].\n", __FUNCTION__, rc)); - } - - return rc > 0 ? -rc : rc; -} - - -/** - * @brief Module exit function. Calls the commOSModExit function pointer - * to perform upper layer cleanup. - */ - -static void __exit -ModExit(void) -{ - if (!commOSModExit) { - CommOS_Log(("%s: Can't find \'fini\' function for module \'" \ - COMM_OS_MOD_SHORT_NAME_STRING "\'.\n", __FUNCTION__)); - return; - } - - (*commOSModExit)(); - CommOS_Log(("%s: Module \'" COMM_OS_MOD_SHORT_NAME_STRING \ - "\' has been stopped.\n", __FUNCTION__)); -} - - -module_init(ModInit); -module_exit(ModExit); - -/* Module information. */ -MODULE_AUTHOR("VMware, Inc."); -MODULE_DESCRIPTION(COMM_OS_MOD_NAME_STRING); -MODULE_VERSION(COMM_OS_MOD_VERSION_STRING); -MODULE_LICENSE("GPL v2"); -/* - * Starting with SLE10sp2, Novell requires that IHVs sign a support agreement - * with them and mark their kernel modules as externally supported via a - * change to the module header. If this isn't done, the module will not load - * by default (i.e., neither mkinitrd nor modprobe will accept it). - */ -MODULE_INFO(supported, "external"); diff --git a/arch/arm/mvp/pvtcpkm/comm_os_mod_ver.h b/arch/arm/mvp/pvtcpkm/comm_os_mod_ver.h deleted file mode 100644 index 5e14c62..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_os_mod_ver.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Version definitions for the pvTCP module. - */ - -#ifndef _COMM_OS_MOD_VER_H_ -#define _COMM_OS_MOD_VER_H_ - -#define COMM_OS_MOD_NAME_STRING "VMware paravirtualized tcp/ip module" -#define COMM_OS_MOD_SHORT_NAME pvtcp -#define COMM_OS_MOD_SHORT_NAME_STRING "pvtcp" - -#define COMM_OS_MOD_VERSION 1.0.0.0 -#define COMM_OS_MOD_VERSION_COMMAS 1,0,0,0 -#define COMM_OS_MOD_VERSION_STRING "1.0.0.0" - -#endif /* _COM_OS_MOD_VER_H_ */ diff --git a/arch/arm/mvp/pvtcpkm/comm_svc.h b/arch/arm/mvp/pvtcpkm/comm_svc.h deleted file mode 100644 index 784ec76..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_svc.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Communication functions exported by the comm_rt module. - */ - -#ifndef _COMM_SVC_H_ -#define _COMM_SVC_H_ - -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -#include "comm.h" - -int CommSvc_RegisterImpl(const CommImpl *impl); -void CommSvc_UnregisterImpl(const CommImpl *impl); -int CommSvc_Zombify(CommChannel channel, int inBH); -int CommSvc_IsActive(CommChannel channel); -CommTranspInitArgs CommSvc_GetTranspInitArgs(CommChannel channel); -void *CommSvc_GetState(CommChannel channel); -void CommSvc_Put(CommChannel channel); -void CommSvc_DispatchUnlock(CommChannel channel); -int CommSvc_Lock(CommChannel channel); -void CommSvc_Unlock(CommChannel channel); -int CommSvc_ScheduleAIOWork(CommOSWork *work); - -int -CommSvc_Alloc(const CommTranspInitArgs *transpArgs, - const CommImpl *impl, - int inBH, - CommChannel *newChannel); - -int -CommSvc_Write(CommChannel channel, - const CommPacket *packet, - unsigned long long *timeoutMillis); - -int -CommSvc_WriteVec(CommChannel channel, - const CommPacket *packet, - struct kvec **vec, - unsigned int *vecLen, - unsigned long long *timeoutMillis, - unsigned int *iovOffset); - -unsigned int CommSvc_RequestInlineEvents(CommChannel channel); -unsigned int CommSvc_ReleaseInlineEvents(CommChannel channel); - -#endif // _COMM_SVC_H_ diff --git a/arch/arm/mvp/pvtcpkm/comm_transp.h b/arch/arm/mvp/pvtcpkm/comm_transp.h deleted file mode 100644 index c46f849..0000000 --- a/arch/arm/mvp/pvtcpkm/comm_transp.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Generic shared memory transport API. - */ - -#ifndef _COMM_TRANSP_H_ -#define _COMM_TRANSP_H_ - -#define INCLUDE_ALLOW_PV -#define INCLUDE_ALLOW_MODULE -#define INCLUDE_ALLOW_MONITOR -#define INCLUDE_ALLOW_GPL -#include "include_check.h" - -/* - * Common shared memory identifier. - * External handle that makes sense to both hypervisor and guest. - */ - -#define COMM_TRANSP_ID_8_ANY ((unsigned char)-1) -#define COMM_TRANSP_ID_32_ANY ((unsigned int)-1) -#define COMM_TRANSP_ID_64_ANY ((unsigned long long)-1) - - -typedef struct CommTranspID { - union { - unsigned char d8[8]; - unsigned int d32[2]; - unsigned long long d64; - }; -} CommTranspID; - - -/* Basic initialization arguments. */ - -typedef enum CommTranspInitMode { - COMM_TRANSP_INIT_CREATE = 0x0, - COMM_TRANSP_INIT_ATTACH = 0x1 -} CommTranspInitMode; - -typedef struct CommTranspInitArgs { - unsigned int capacity; // Shared memory capacity. - unsigned int type; // Type / implementation using this area. - CommTranspID id; // ID (name) of shared memory area. - CommTranspInitMode mode; // Init mode (above). -} CommTranspInitArgs; - - -/** - * @brief Generate a type id from description (protocol) string. This function - * uses djb2, a string hashing algorithm by Dan Bernstein. - * (see http://www.cse.yorku.ca/~oz/hash.html) - * @param str string to hash - * @return 32-bit hash value - */ - -static inline unsigned int -CommTransp_GetType(const char *str) -{ - unsigned int hash = 5381; - int c; - - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - return hash; -} - -#endif // _COMM_TRANSP_H_ diff --git a/arch/arm/mvp/pvtcpkm/include_check.h b/arch/arm/mvp/pvtcpkm/include_check.h deleted file mode 100644 index 2eeafe7..0000000 --- a/arch/arm/mvp/pvtcpkm/include_check.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for Empty File Placeholder - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ diff --git a/arch/arm/mvp/pvtcpkm/pvtcp.c b/arch/arm/mvp/pvtcpkm/pvtcp.c deleted file mode 100644 index fdfb0d2..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Pvtcp common code. - */ - -#include "pvtcp.h" - - -/* - * Operation table. - */ - -CommOperationFunc pvtcpOperations[] = { - [PVTCP_OP_FLOW] = PvtcpFlowOp, - [PVTCP_OP_IO] = PvtcpIoOp, - [PVTCP_OP_CREATE] = PvtcpCreateOp, - [PVTCP_OP_RELEASE] = PvtcpReleaseOp, - [PVTCP_OP_BIND] = PvtcpBindOp, - [PVTCP_OP_LISTEN] = PvtcpListenOp, - [PVTCP_OP_ACCEPT] = PvtcpAcceptOp, - [PVTCP_OP_CONNECT] = PvtcpConnectOp, - [PVTCP_OP_SHUTDOWN] = PvtcpShutdownOp, - [PVTCP_OP_SETSOCKOPT] = PvtcpSetSockOptOp, - [PVTCP_OP_GETSOCKOPT] = PvtcpGetSockOptOp, - [PVTCP_OP_IOCTL] = PvtcpIoctlOp, - [PVTCP_OP_INVALID] = NULL -}; - - -/* - * Implementation block. - */ - -CommImpl pvtcpImpl = { - .owner = NULL, - .checkArgs = PvtcpCheckArgs, - .stateCtor = PvtcpStateAlloc, - .stateDtor = PvtcpStateFree, - .dataAlloc = PvtcpBufAlloc, - .dataFree = PvtcpBufFree, - .operations = pvtcpOperations, - .closeNtf = PvtcpCloseNtf, - .closeNtfData = &pvtcpImpl, - .ntfCenterID = {{ - .d32[0] = 2U /* x86 host context (vmci, only). */, - .d32[1] = 10000 /* Default, not yet reserved, resource (vmci, only). */ - }} -}; - - -/* - * Version array. - */ - -const char *pvtcpVersions[] = { - [PVTCP_VERS_1_1] = PVTCP_COMM_IMPL_VERS_1_1, - [PVTCP_VERS_1_0] = PVTCP_COMM_IMPL_VERS_1_0 -}; - -const unsigned int pvtcpVersionsSize = - (sizeof pvtcpVersions / sizeof pvtcpVersions[0]); - - -/* - * Client (pv) channel to offload side. We choose to define it here, although - * it's only applicable to the pv implementation. The reason is that we can - * share a common close notification function which does the right thing - * depending on the channel configuration. - */ - -CommChannel pvtcpClientChannel; - - -/* - * Built-in state interfaces. - */ - -static PvtcpIfConf ifUnbound = { - .family = PVTCP_PF_UNBOUND -}; -const PvtcpIfConf *pvtcpIfUnbound = &ifUnbound; - -static PvtcpIfConf ifDeathRow = { - .family = PVTCP_PF_DEATH_ROW -}; -const PvtcpIfConf *pvtcpIfDeathRow = &ifDeathRow; - -static PvtcpIfConf ifLoopbackInet4 = { - .family = PVTCP_PF_LOOPBACK_INET4 -}; -const PvtcpIfConf *pvtcpIfLoopbackInet4 = &ifLoopbackInet4; - - -/* Functions */ - -/** - * @brief Checks if the IF configuration has reasonable values. - * @param conf configuration to check - * @return zero if successful, -1 otherwise - */ - -static int -IfCheck(const PvtcpIfConf *conf) -{ - if (!conf || - ((conf->family != PF_INET) && - (conf->family != PF_INET6) && - (conf->family != PVTCP_PF_UNBOUND) && - (conf->family != PVTCP_PF_DEATH_ROW) && - (conf->family != PVTCP_PF_LOOPBACK_INET4))) { - return -1; - } - - /** @todo Need more checks for IP/netmask format validity. */ - return 0; -} - - -/** - * @brief Checks if the IF has reasonable values, but restricts types to - * AF_INET and AF_INET6 - * @param conf IF to check - * @return zero if successful, -1 otherwise - */ - -static int -IfRestrictedCheck(const PvtcpIfConf *conf) -{ - if (IfCheck(conf) || - ((conf->family != PF_INET) && - (conf->family != PF_INET6))) { - return -1; - } - return 0; -} - - -/** - * @brief Finds a netif given a state and a configuration. The configuration - * must have already been checked. This function doesn't lock, so it - * should not be called when the state, or the netif for the passed - * configuration may be deleted. - * @param state state to look for. - * @param conf configuration to look for. - * @return netif matching configuration, or NULL. - */ - -PvtcpIf * -PvtcpStateFindIf(PvtcpState *state, - const PvtcpIfConf *conf) -{ - PvtcpIf *netif; - - if (!state) { - return NULL; - } - - if (conf->family == PVTCP_PF_UNBOUND) { - return &state->ifUnbound; - } - - if (conf->family == PVTCP_PF_DEATH_ROW) { - return &state->ifDeathRow; - } - - if (conf->family == PVTCP_PF_LOOPBACK_INET4) { - return &state->ifLoopbackInet4; - } - - CommOS_ListForEach(&state->ifList, netif, stateLink) { - if (netif->conf.family == conf->family) { - if ((conf->family == PF_INET && - !memcmp(&netif->conf.addr.in, &conf->addr.in, - sizeof conf->addr.in)) || - (conf->family == PF_INET6 && - !memcmp(&netif->conf.addr.in6, &conf->addr.in6, - sizeof conf->addr.in6))) { - return netif; - } - } - } - return NULL; -} - - -/** - * @brief Creates and initializes a new netif for a given channel and with - * the specified configuration. Death row and unbound netifs may not - * be added using this function. - * @param[in,out] channel channel to make a new netif in - * @param conf configuration to set netif to - * @return 0 if successful, -1 otherwise - * @sideeffect May allocate memory - */ - -int -PvtcpStateAddIf(CommChannel channel, - const PvtcpIfConf *conf) -{ - int rc = -1; - PvtcpState *state; - PvtcpIf *netif; - - if (!channel || IfRestrictedCheck(conf)) { - return rc; - } - - if (CommSvc_Lock(channel)) { - return rc; /* channel isn't active. */ - } - - state = CommSvc_GetState(channel); - if (!state) { - goto out; - } - - if (PvtcpStateFindIf(state, conf)) { - goto out; /* Already configured. */ - } - - netif = CommOS_Kmalloc(sizeof *netif); - if (!netif) { - goto out; - } - - INIT_LIST_HEAD(&netif->stateLink); - INIT_LIST_HEAD(&netif->sockList); - netif->state = state; - netif->conf = *conf; - CommOS_ListAddTail(&state->ifList, &netif->stateLink); - rc = 0; - -out: - CommSvc_Unlock(channel); - return rc; -} - - -/** - * @brief Removes and potentially deallocates all sockets associated with the - * given netif and deallocates the latter. - * @param[in,out] netif netif to deallocate - * @sideeffect Closes sockets, deallocates memory - */ - -static void -IfFree(PvtcpIf *netif) -{ - PvtcpSock *pvsk; - PvtcpSock *tmp; - - if (netif) { - CommOS_ListForEachSafe(&netif->sockList, pvsk, tmp, ifLink) { - CommOS_ListDel(&pvsk->ifLink); - PvtcpReleaseSocket(pvsk); - } - if ((netif->conf.family != PVTCP_PF_UNBOUND) && - (netif->conf.family != PVTCP_PF_DEATH_ROW) && - (netif->conf.family != PVTCP_PF_LOOPBACK_INET4)) { - CommOS_ListDel(&netif->stateLink); - CommOS_Kfree(netif); - } - } -} - - -/** - * @brief Closes all sockets associated with, and deallocates the netif - * in the given channel and with the specified configuration. - * Death row and unbound netifs may not be removed using this function. - * @param[in,out] channel channel to remove from - * @param conf configuration specified - * @return zero if successful, error code otherwise - * @sideeffect Closes sockets, deallocates memory - */ - -void -PvtcpStateRemoveIf(CommChannel channel, - const PvtcpIfConf *conf) -{ - PvtcpState *state; - PvtcpIf *netif; - - if (!channel || IfRestrictedCheck(conf)) { - return; - } - - if (CommSvc_Lock(channel)) { - return; /* channel isn't active. */ - } - - state = CommSvc_GetState(channel); - if (state && (netif = PvtcpStateFindIf(state, conf))) { - if (netif->state == state) { - IfFree(netif); - } - } - - CommSvc_Unlock(channel); -} - - -/** - * @brief Adds a socket to an existing netif. If the socket is already on a - * different netif, it is removed from that netif. - * It locks the must-be-active channel. We use that lock to guard - * against concurrent removal of the netif. - * @param[in,out] channel channel to add to - * @param conf specified configuration - * @param[in,out] sock socket to add - * @return zero if successful, -1 otherwise - */ - -int -PvtcpStateAddSocket(CommChannel channel, - const PvtcpIfConf *conf, - PvtcpSock *sock) -{ - int rc = -1; - PvtcpState *state; - PvtcpIf *netif; - - if (!channel || !sock || (sock->channel != channel) || IfCheck(conf)) { - return rc; - } - - if (CommSvc_Lock(channel)) { - return rc; /* channel isn't active. */ - } - - state = CommSvc_GetState(channel); - if (!state) { - goto out; - } - - netif = PvtcpStateFindIf(state, conf); - if (!netif) { - goto out; - } - - CommOS_ListDel(&sock->ifLink); - sock->netif = netif; - CommOS_ListAddTail(&netif->sockList, &sock->ifLink); - rc = 0; - -out: - CommSvc_Unlock(channel); - return rc; -} - - -/** - * @brief Removes a socket from its netif. - * It locks the must-be-active channel. We use that lock to guard - * against concurrent removal of the netif. - * @param[in,out] channel channel to remove from - * @param[in,out] sock socket to remove - * @return zero if successful, -1 otherwise - */ - -int -PvtcpStateRemoveSocket(CommChannel channel, - PvtcpSock *sock) -{ - if (!channel || !sock || - (sock->channel && (sock->channel != channel))) { - return -1; - } - - if (CommSvc_Lock(channel)) { - return -1; /* channel isn't active. */ - } - - CommOS_ListDel(&sock->ifLink); - sock->channel = NULL; - sock->state = NULL; - sock->netif = NULL; - - CommSvc_Unlock(channel); - return 0; -} - - -/** - * @brief State constructor called when a channel is created. The netifs - * 'death row' and 'unbound' are always initialized. - * @param[in,out] channel channel to initialize - * @return pointer to a new state structure or NULL - * @sideeffect Allocates memory - */ - -void * -PvtcpStateAlloc(CommChannel channel) -{ - PvtcpState *state; - - state = CommOS_Kmalloc(sizeof *state); - if (state) { - state->channel = channel; - INIT_LIST_HEAD(&state->ifList); - - /* Initialize always-present netifs. */ - INIT_LIST_HEAD(&state->ifDeathRow.stateLink); /* Irrelevant */ - INIT_LIST_HEAD(&state->ifDeathRow.sockList); - state->ifDeathRow.state = state; - state->ifDeathRow.conf.family = PVTCP_PF_DEATH_ROW; - - INIT_LIST_HEAD(&state->ifUnbound.stateLink); /* Irrelevant */ - INIT_LIST_HEAD(&state->ifUnbound.sockList); - state->ifUnbound.state = state; - state->ifUnbound.conf.family = PVTCP_PF_UNBOUND; - - INIT_LIST_HEAD(&state->ifLoopbackInet4.stateLink); /* Irrelevant */ - INIT_LIST_HEAD(&state->ifLoopbackInet4.sockList); - state->ifLoopbackInet4.state = state; - state->ifLoopbackInet4.conf.family = PVTCP_PF_LOOPBACK_INET4; - - state->namespace = NULL; - state->mask = ((unsigned int)channel << 4) ^ (unsigned int)state; -#if defined(__linux__) - state->id = ((unsigned long long)random32() << 32) | - (unsigned long long)random32(); -#else - state->id = (unsigned long long)state; -#endif - } - return state; -} - - -/** - * @brief State destructor called when a channel is closed. - * The caller (Comm) guarantees proper locking. - * @param arg pointer to state structure - * @sideeffect Destroys all netifs and their sockets, deallocates memory - */ - -void -PvtcpStateFree(void *arg) -{ - PvtcpState *state = arg; - PvtcpIf *netif; - PvtcpIf *tmp; - - if (state) { - CommOS_ListForEachSafe(&state->ifList, netif, tmp, stateLink) { - IfFree(netif); - } - /* coverity[address_free] */ - IfFree(&state->ifLoopbackInet4); - /* coverity[address_free] */ - IfFree(&state->ifUnbound); - /* coverity[address_free] */ - IfFree(&state->ifDeathRow); - CommOS_Kfree(state); - } -} - - -/** - * @brief Checks transport arguments. - * @param transpArgs transport arguments. - * @return zero if successful, < 0 otherwise. - */ - -int -PvtcpCheckArgs(CommTranspInitArgs *transpArgs) -{ - int rc = -1; - const unsigned int minCapacity = - (PVTCP_SOCK_BUF_SIZE + sizeof(CommPacket)) * 2; - unsigned int versionIndex = pvtcpVersionsSize; - - if (transpArgs->capacity < minCapacity) { - return rc; - } - - while (versionIndex--) { - if (transpArgs->type == CommTransp_GetType(pvtcpVersions[versionIndex])) { - /* If a match, overwrite the hash with the actual version (index). */ - - transpArgs->type = versionIndex; - rc = 0; - break; - } - } - - return rc; -} - - -/** - * @brief Called after a channel is freed. - * @param ntfData callback data from implementation block. - * @param transpArgs transport arguments of closed channel. - * @param inBH whether called in bottom half. - */ - -void -PvtcpCloseNtf(void *ntfData, - const CommTranspInitArgs *transpArgs, - int inBH) -{ - CommImpl *impl = (CommImpl *)ntfData; - - pvtcpClientChannel = NULL; - CommOS_Log(("%s: Channel was reset!\n", __FUNCTION__)); - - /* - * If the impl. block owner is NULL, we're pv client: we attempt to - * reopen the channel in a few seconds. - */ - - if (impl && !impl->owner && !inBH) { - CommOS_Log(("%s: Attempting to re-initialize channel.\n", __FUNCTION__)); - impl->openAtMillis = CommOS_GetCurrentMillis(); - impl->openTimeoutAtMillis = - CommOS_GetCurrentMillis() + PVTCP_CHANNEL_OPEN_TIMEOUT; - if (CommSvc_Alloc(transpArgs, impl, inBH, &pvtcpClientChannel)) { - CommOS_Log(("%s: Failed to initialize channel!\n", __FUNCTION__)); - } - } -} - - -/** - * @brief Initializes the Pvtcp socket common fields. - * @param pvsk pvtcp socket. - * @param channel Comm channel this socket is associated with. - * @return 0 if successful, -1 otherwise. - */ - -int -PvtcpSockInit(PvtcpSock *pvsk, - CommChannel channel) -{ - PvtcpState *state; - int rc = -1; - - if (pvsk && channel && (state = CommSvc_GetState(channel))) { - /* Must _not_ zero out pvsk! */ - - CommOS_MutexInit(&pvsk->inLock); - CommOS_MutexInit(&pvsk->outLock); - CommOS_SpinlockInit(&pvsk->stateLock); - CommOS_ListInit(&pvsk->ifLink); - CommOS_InitWork(&pvsk->work, PvtcpProcessAIO); - pvsk->netif = NULL; - pvsk->state = state; - pvsk->stateID = state->id; - pvsk->channel = channel; - pvsk->peerSock = PVTCP_PEER_SOCK_NULL; - pvsk->peerSockSet = 0; - CommOS_WriteAtomic(&pvsk->deltaAckSize, - (1 << PVTCP_SOCK_SMALL_ACK_ORDER)); - CommOS_WriteAtomic(&pvsk->rcvdSize, 0); - CommOS_WriteAtomic(&pvsk->sentSize, 0); - CommOS_WriteAtomic(&pvsk->queueSize, 0); - CommOS_ListInit(&pvsk->queue); - pvsk->rpcReply = NULL; - pvsk->rpcStatus = 0; - pvsk->err = 0; - rc = 0; - } - return rc; -} diff --git a/arch/arm/mvp/pvtcpkm/pvtcp.h b/arch/arm/mvp/pvtcpkm/pvtcp.h deleted file mode 100644 index 7f4f2f5..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp.h +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Pvtcp common APIs. - */ - -#ifndef _PVTCP_H_ -#define _PVTCP_H_ - -/* - * Pvtcp state store ipv4 and ipv6 address structures. - * Platform-specific headers where these are defined, must be included here. - * Implementation-related header files should not be included in this file. - * - * NOTE: Pvtcp is not an API and none of its functions are exported. - */ - -#if defined(__linux__) -#include -#include -#else -#error "Unsupported OS." -#endif - -#include "comm_svc.h" - -/* Max time to wait for a channel to be created. */ -#define PVTCP_CHANNEL_OPEN_TIMEOUT 2000 - -/* Max payload size. Used to allocate offload per-cpu bounce buffers. */ -#define PVTCP_SOCK_BUF_SIZE (8 << 10) /* 8K */ - -#define PVTCP_SOCK_DGRAM_BUF_SIZE PVTCP_SOCK_BUF_SIZE -#define PVTCP_SOCK_STREAM_BUF_SIZE PVTCP_SOCK_BUF_SIZE - -/* Dgram payloads include a pseudo (udp/ip) header. */ -typedef struct PvtcpDgramPseudoHeader { - unsigned long long d0; - unsigned long long d1; - unsigned long long d2; - unsigned long long d3; -} PvtcpDgramPseudoHeader; - - -/* - * Flow control constants for pv/offload sockets. - * We are defining a receive size model: 1) small, 2) medium, 3)large. - * This seems sufficient in addressing most target environments, but more - * models may be defined. A smaller minimum model (1) cannot be defined. - * - * Short description of socket-level flow control. This applies to both - * dgram and stream sockets, in both directions. It follows that, with regard - * to 'comm' writes, dgram and stream writes are: a) lossless and b) ordered. - * - * 0. Both sides (offload, pv) of a socket maintain (almost) mirror values - * of input/output queue sizes. We say 'almost', because they're allowed - * to conservatively converge in time. - * 1. Senders never write out to the shmem channel, and destined to a socket - * (be it offload or pv), more bytes than that socket can hold/enqueue. - * This is based on socket fields storing information mentioned above. - * The upper limit is PVTCP_SOCK_RCVSIZE and cannot be exceeded under - * any circumstances. - * 2. There is a 'safe' limit value (per socket) which can be tested prior - * to writing one more max-sized packet to that socket. - * This value is PVTCP_SOCK_SAFE_RCVSIZE. - * 3. There is also a notion of 'large' acks, which controls the frequency of - * reporting socket queue size changes when bytes are consumed from it. - * When a sender is about to write out (to the channel, for a given socket) - * in excess of PVTCP_SOCK_LARGE_ACK_WM bytes, it sets, in the packet - * header flag field, the PVTCP_SOCK_LARGE_ACK_ORDER value. The other end - * updates its 'delta ack' value accordingly (1 << flag value). - * 4. As bytes are consumed (again, at either end), the operation or function, - * will send a size ack packet with the consumed size since the last ack, - * _iff_ that size is larger than, or equal to the 'delta ack' value. - * If an ack was sent, the 'delta ack' is decreased by half, to a minimum - * indicated by PVTCP_SOCK_SMALL_ACK_ORDER. - * Note that concurrently setting the 'delta ack' to its high value - * because of condition 3) above, is fine since the sender already has, - * or is about to put pressure on the socket. - */ - -#if !defined(PVTCP_SOCK_RCVSIZE_MODEL) - #define PVTCP_SOCK_RCVSIZE_MODEL 1 -#endif - -#if PVTCP_SOCK_RCVSIZE_MODEL == 1 - #define PVTCP_SOCK_LARGE_ACK_WM (64 << 10) /* 64K */ - #define PVTCP_SOCK_LARGE_ACK_ORDER 15 - #define PVTCP_SOCK_SMALL_ACK_ORDER 11 - #define PVTCP_SOCK_SAFE_RCVSIZE (128 << 10) /* 128K */ -#elif PVTCP_SOCK_RCVSIZE_MODEL == 2 - #define PVTCP_SOCK_LARGE_ACK_WM (128 << 10) /* 128K */ - #define PVTCP_SOCK_LARGE_ACK_ORDER 16 - #define PVTCP_SOCK_SMALL_ACK_ORDER 12 - #define PVTCP_SOCK_SAFE_RCVSIZE (256 << 10) /* 256K */ -#elif PVTCP_SOCK_RCVSIZE_MODEL == 3 - #define PVTCP_SOCK_LARGE_ACK_WM (128 << 10) /* 128K */ - #define PVTCP_SOCK_LARGE_ACK_ORDER 16 - #define PVTCP_SOCK_SMALL_ACK_ORDER 12 - #define PVTCP_SOCK_SAFE_RCVSIZE (512 << 10) /* 512K */ -#else - #error "Invalid PVTCP_SOCK_RCVSIZE_MODEL (one of 1, 2, 3)" -#endif - -#define PVTCP_SOCK_RCVSIZE \ - (PVTCP_SOCK_SAFE_RCVSIZE + \ - PVTCP_SOCK_BUF_SIZE + sizeof (PvtcpDgramPseudoHeader)) - - -/* - * Operation codes - */ - -enum PvtcpOpCodes { - PVTCP_OP_FLOW = 0, - PVTCP_OP_IO, - PVTCP_OP_CREATE, - PVTCP_OP_RELEASE, - PVTCP_OP_BIND, - PVTCP_OP_LISTEN, - PVTCP_OP_ACCEPT, - PVTCP_OP_CONNECT, - PVTCP_OP_SHUTDOWN, - PVTCP_OP_SETSOCKOPT, - PVTCP_OP_GETSOCKOPT, - PVTCP_OP_IOCTL, - PVTCP_OP_INVALID -}; - -#define PVTCP_FLOW_OP_INVALID_SIZE 0xffffffff - - -/* - * Operation functions - */ - -COMM_DEFINE_OP(PvtcpFlowOp); -COMM_DEFINE_OP(PvtcpIoOp); -COMM_DEFINE_OP(PvtcpCreateOp); -COMM_DEFINE_OP(PvtcpReleaseOp); -COMM_DEFINE_OP(PvtcpBindOp); -COMM_DEFINE_OP(PvtcpListenOp); -COMM_DEFINE_OP(PvtcpAcceptOp); -COMM_DEFINE_OP(PvtcpConnectOp); -COMM_DEFINE_OP(PvtcpShutdownOp); -COMM_DEFINE_OP(PvtcpSetSockOptOp); -COMM_DEFINE_OP(PvtcpGetSockOptOp); -COMM_DEFINE_OP(PvtcpIoctlOp); - - -/* - * Pvtcp/Comm type and supported versions. - */ - -#define PVTCP_COMM_IMPL_TYPE "com.vmware.comm.protocol.pvTCP@" - -#define PVTCP_COMM_IMPL_VERS_1_0 (PVTCP_COMM_IMPL_TYPE "1.0") -#define PVTCP_COMM_IMPL_VERS_1_1 (PVTCP_COMM_IMPL_TYPE "1.1") - -typedef enum { - PVTCP_VERS_1_0 = 0, - PVTCP_VERS_1_1 -} PvtcpVersion; - -extern const char *pvtcpVersions[]; -extern const unsigned int pvtcpVersionsSize; - - -/* - * State interface markers - */ - -#define PVTCP_PF_UNBOUND 0x0 -#define PVTCP_PF_DEATH_ROW 0xffffffff -#define PVTCP_PF_LOOPBACK_INET4 (PVTCP_PF_DEATH_ROW - 1) - - -/* - * Interface and interface configuration structures. - */ - -typedef struct PvtcpIfConf { - int family; // Values: - // unbound (PVTCP_PF_UNBOUND) - // deathRow (PVTCP_PF_DEATH_ROW) - // loopback (PVTCP_PF_LOOPBACK_INET4) - // inet4 (PF_INET) - // inet6 (PF_INET6) - union { - struct in_addr in; - struct in6_addr in6; - } addr; // inet4 or inet6 address. - union { - struct in_addr in; - struct in6_addr in6; - } mask; // inet4 or inet6 netmask. -} PvtcpIfConf; - - -struct PvtcpState; - -typedef struct PvtcpIf { - CommOSList sockList; // List of sockets. - CommOSList stateLink; // Link in PvtcpState.ifList. - struct PvtcpState *state; // Back reference to state. - PvtcpIfConf conf; // Interface configuration. -} PvtcpIf; - - -/* - * General pvtcp state associated with a channel. - */ - -typedef struct PvtcpState { - unsigned long long id; // Randomly generated state ID. - CommOSList ifList; // List of active interfaces. - CommChannel channel; // Comm channel back reference. - PvtcpIf ifDeathRow; // Always-present netif. - PvtcpIf ifUnbound; // Ditto. - PvtcpIf ifLoopbackInet4; // Ditto. - void *namespace; // Name space, where supported. - void *extra; // Used by upper layer to extend state as needed. - unsigned int mask; // Mask used to obfuscate socket pointers. -} PvtcpState; - - -/* - * Define pvtcp socket common fields and include the pv or offload header - * to get the right PvtcpSock definition. - */ - -#define PVTCP_SOCK_COMMON_FIELDS \ - CommOSMutex inLock; /* Input lock. */ \ - CommOSMutex outLock; /* Output lock. */ \ - CommOSSpinlock stateLock; /* State update lock. */ \ - CommOSList ifLink; /* Link in PvtcpIf.sockList. */ \ - CommOSWork work; /* Work item for AIO processing. */ \ - PvtcpIf *netif; /* Netif reference. */ \ - PvtcpState *state; /* State reference. */ \ - unsigned long long stateID; /* State ID. */ \ - CommChannel channel; /* Comm channel reference. */ \ - unsigned long long peerSock; /* Peer socket, opaque. */ \ - volatile int peerSockSet; /* Peer socket valid. */ \ - CommOSAtomic deltaAckSize; /* Recv size updates required by peer. */ \ - CommOSAtomic rcvdSize; /* Bytes received since last ack. */ \ - CommOSAtomic sentSize; /* Bytes sent; also updated by peer. */ \ - CommOSAtomic queueSize; /* Queue size. */ \ - CommOSList queue; /* Send queue (off) or recv queue (pv). */ \ - void *rpcReply; /* RPC reply. */ \ - int rpcStatus; /* RPC completion status. */ \ - int err /* Socket error. */ - -#define PVTCP_PEER_SOCK_NULL ((unsigned long long)0) - - -/* - * Helper macros - */ - -#define SOCK_STATE_LOCK(pvsk) CommOS_SpinLock(&(pvsk)->stateLock) -#define SOCK_STATE_UNLOCK(pvsk) CommOS_SpinUnlock(&(pvsk)->stateLock) - -#define SOCK_IN_TRYLOCK(pvsk) CommOS_MutexTrylock(&(pvsk)->inLock) -#define SOCK_IN_LOCK(pvsk) CommOS_MutexLock(&(pvsk)->inLock) -#define SOCK_IN_UNLOCK(pvsk) CommOS_MutexUnlock(&(pvsk)->inLock) - -#define SOCK_OUT_TRYLOCK(pvsk) CommOS_MutexTrylock(&(pvsk)->outLock) -#define SOCK_OUT_LOCK(pvsk) CommOS_MutexLock(&(pvsk)->outLock) -#define SOCK_OUT_LOCK_UNINT(pvsk) \ - CommOS_MutexLockUninterruptible(&(pvsk)->outLock) -#define SOCK_OUT_UNLOCK(pvsk) CommOS_MutexUnlock(&(pvsk)->outLock) - -#define PVTCP_UNLOCK_DISP_DISCARD_VEC() \ - CommSvc_DispatchUnlock(channel); \ - while (vecLen) { \ - PvtcpBufFree(vec[--vecLen].iov_base); \ - } - - -#if defined(PVTCP_BUILDING_SERVER) -#include "pvtcp_off.h" -#else -#include "pvtcp_pv.h" -#endif // defined(PVTCP_BUILDING_SERVER) - - -/* - * Data declarations - */ - -extern const PvtcpIfConf *pvtcpIfUnbound; -extern const PvtcpIfConf *pvtcpIfDeathRow; -extern const PvtcpIfConf *pvtcpIfLoopbackInet4; - -extern CommImpl pvtcpImpl; -extern CommOperationFunc pvtcpOperations[]; - -extern CommChannel pvtcpClientChannel; - - -/* - * Common state manipulation functions. - */ - -void *PvtcpStateAlloc(CommChannel channel); -void PvtcpStateFree(void *arg); - -int PvtcpStateAddIf(CommChannel channel, const PvtcpIfConf *conf); -void PvtcpStateRemoveIf(CommChannel channel, const PvtcpIfConf *conf); -PvtcpIf *PvtcpStateFindIf(PvtcpState *state, const PvtcpIfConf *conf); - -int -PvtcpStateAddSocket(CommChannel channel, - const PvtcpIfConf *conf, - PvtcpSock *sock); -int PvtcpStateRemoveSocket(CommChannel channel, PvtcpSock *sock); - - -/* - * Common Pvtcp functions. - */ - -int PvtcpCheckArgs(CommTranspInitArgs *transpArgs); - -void -PvtcpCloseNtf(void *ntfData, - const CommTranspInitArgs *transpArgs, - int inBH); - -void *PvtcpBufAlloc(unsigned int size); -void PvtcpBufFree(void *buf); - -void PvtcpReleaseSocket(PvtcpSock *pvsk); -int PvtcpSockInit(PvtcpSock *pvsk, CommChannel channel); - -void PvtcpProcessAIO(CommOSWork *work); - - -/** - * @brief Packs an IPV6 address stored in an array of four 32-bit elements, - * into two 64-bit variables. - * @param addr IPV6 address as an array of 32-bit elements. - * @param[out] d64_0 pointer to 64-bit variable. - * @param[out] d64_1 pointer to 64-bit variable. - */ - -static inline void -PvtcpI6AddrPack(const unsigned int addr[4], - unsigned long long *d64_0, - unsigned long long *d64_1) -{ - *d64_0 = *(unsigned long long *)&addr[0]; - *d64_1 = *(unsigned long long *)&addr[2]; -} - - -/** - * @brief Unpacks two 64-bit values into an IPV6 address-storing array of - * four 32-bit elements, - * @param[out] addr IPV6 address as an array of 32-bit elements. - * @param d64_0 64-bit value. - * @param d64_1 64-bit value. - */ - -static inline void -PvtcpI6AddrUnpack(unsigned int addr[4], - unsigned long long d64_0, - unsigned long long d64_1) -{ - *(unsigned long long *)&addr[0] = d64_0; - *(unsigned long long *)&addr[2] = d64_1; -} - - -/** - * @brief Verifies whether the argument is a valid socket. If yes, it returns - * the actual pointer. Otherwise, it returns from the calling function. - * WARNING: This macro must ONLY be used in operation functions, as its - * implementation assumes. - * @param handle socket handle to verify. - * @param container state supposed to contain the socket handle. - * @return 32-bit or 64-bit PvtcpSock*, depending on __LP64__ or __LLP64__. - */ - -#if defined(__LP64__) || defined(__LLP64__) - -#define PvtcpGetPvskOrReturn(handle, container) \ - ({ \ - PvtcpState *__state = (PvtcpState *)(container); \ - PvtcpSock *__pvsk = \ - (PvtcpSock *)((handle) ^ (unsigned long long)__state->mask); \ - \ - if (__pvsk->stateID != __state->id) { \ - PVTCP_UNLOCK_DISP_DISCARD_VEC(); \ - CommSvc_Zombify(__state->channel, 0); \ - return; \ - } \ - (__pvsk); \ - }) - -#else // __LP64__ || __LLP64__ - -#define PvtcpGetPvskOrReturn(handle, container) \ - ({ \ - PvtcpState *__state = (PvtcpState *)(container); \ - PvtcpSock *__pvsk = \ - (PvtcpSock *)((unsigned int)(handle) ^ __state->mask); \ - \ - if (__pvsk->stateID != __state->id) { \ - PVTCP_UNLOCK_DISP_DISCARD_VEC(); \ - CommSvc_Zombify(__state->channel, 0); \ - return; \ - } \ - (__pvsk); \ - }) - -#endif // __LP64__ || __LLP64__ - - -/** - * @brief Masks a socket pointer to be passed to the peer module. - * @param pvsk socket pointer to mask. - * @return 64-bit pvtcp socket handle. - */ - -#if defined(__LP64__) || defined(__LLP64__) - -#define PvtcpGetHandle(pvsk) \ - ((unsigned long long)(pvsk) ^ (unsigned long long)(pvsk)->state->mask) - -#else // __LP64__ || __LLP64__ - -#define PvtcpGetHandle(pvsk) \ - ((unsigned int)(pvsk) ^ (pvsk)->state->mask) - -#endif // __LP64__ || __LLP64__ - -#endif // _PVTCP_H_ diff --git a/arch/arm/mvp/pvtcpkm/pvtcp_off.c b/arch/arm/mvp/pvtcpkm/pvtcp_off.c deleted file mode 100644 index 053d9c2..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp_off.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Server (offload) side code. - */ - -#include "pvtcp.h" - -/** - * @brief Allocates the net buffer. - * @param size buffer size - * @return address of buffer or NULL - */ -void * -PvtcpBufAlloc(unsigned int size) -{ - PvtcpOffBuf *buf; - - /* coverity[alloc_fn] */ - /* coverity[var_assign] */ - buf = CommOS_Kmalloc(size + sizeof *buf - sizeof buf->data); - if (buf) { - CommOS_ListInit(&buf->link); - buf->len = (unsigned short)size; - buf->off = 0; - return PvtcpOffBufFromInternal(buf); - } - return NULL; -} - - -/** - * @brief Deallocates given net buffer. - * @param buf buffer to deallocate - * @sideeffect Frees memory - */ - -void -PvtcpBufFree(void *buf) -{ - CommOS_Kfree(PvtcpOffInternalFromBuf(buf)); -} - - -/** - * @brief Initializes the Pvtcp socket offload common fields. - * @param pvsk pvtcp socket. - * @param channel Comm channel this socket is associated with. - * @return 0 if successful, -1 otherwise. - */ - -int -PvtcpOffSockInit(PvtcpSock *pvsk, - CommChannel channel) -{ - int rc = PvtcpSockInit(pvsk, channel); - - pvsk->opFlags = 0; - pvsk->flags = 0; - return rc; -} diff --git a/arch/arm/mvp/pvtcpkm/pvtcp_off.h b/arch/arm/mvp/pvtcpkm/pvtcp_off.h deleted file mode 100644 index f183968..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp_off.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Offload common definitions. - * This file is meant to only be included via pvtcp.h. - */ - -#ifndef _PVTCP_OFF_H_ -#define _PVTCP_OFF_H_ - - -#define PVTCP_OFF_SOCK_COMMON_FIELDS \ - volatile unsigned int opFlags; /* Saves op codes as bit mask. */ \ - volatile unsigned int flags /* General purpose flags. */ - - -/* General purpose socket flags */ - -enum PvtcpOffPvskFlags { - PVTCP_OFF_PVSKF_IPV6_LOOP = 0, /* Used for IPV6 loopback morphing/reset. */ - PVTCP_OFF_PVSKF_SHUT_RD, /* Set to initiate socket recv shutdown. */ - PVTCP_OFF_PVSKF_SHUT_WR, /* Set to initiate socket send shutdown. */ - PVTCP_OFF_PVSKF_TCP_NODELAY, /* Caches the TCP_NODELAY socket option. */ - PVTCP_OFF_PVSKF_TCP_CORK, /* Caches the TCP_CORK socket option. */ - PVTCP_OFF_PVSKF_DISCONNECT, /* Set do indicate connect()/AF_UNSPEC. */ - PVTCP_OFF_PVSKF_INVALID = 32 -}; - - -/* - * Include OS-dependent PvtcpSock structure and functions. - */ - -#if defined(__linux__) -#include "pvtcp_off_linux.h" -#else -#error "Unsupported OS." -#endif - - -/* - * Offload packet payload data structure. - */ - -typedef struct PvtcpOffBuf { - CommOSList link; // Link in socket queue. - unsigned short len; - unsigned short off; - char data[1]; -} PvtcpOffBuf; - - -/** - * @brief Returns net buffer given private data structure pointer and based - * on the internal offset pointer - * @param arg pointer to PvtcpOffBuf wrapper structure - * @return address of buffer or NULL - */ - -static inline void * -PvtcpOffBufFromInternalOff(PvtcpOffBuf *arg) -{ - return arg ? - &arg->data[arg->off] : - NULL; -} - - -/** - * @brief Returns net buffer given private data structure pointer - * @param arg pointer to PvtcpOffBuf wrapper structure - * @return address of buffer or NULL - */ - -static inline void * -PvtcpOffBufFromInternal(PvtcpOffBuf *arg) -{ - return arg ? - &arg->data[0] : - NULL; -} - - -/** - * @brief Returns internal data structure given net buffer pointer - * @param arg pointer to PvtcpOffBuf wrapper structure - * @return address of internal data structure or NULL - */ - -static inline PvtcpOffBuf * -PvtcpOffInternalFromBuf(void *arg) -{ - return arg ? - (PvtcpOffBuf *)((char *)arg - offsetof(PvtcpOffBuf, data)) : - NULL; -} - - -/** - * @brief Tests operation flag for AIO processing. - * @param pvsk socket to test operation on. - * @param op operation to test if set. - * @return non-zero if operation set, zero otherwise. - * @sideeffect socket processing by AIO threads affected according to operation. - */ - -static inline int -PvskTestOpFlag(struct PvtcpSock *pvsk, - int op) -{ - return pvsk->opFlags & (1 << op); -} - - -/** - * @brief Sets operation flag for AIO processing; acquires the state lock. - * @param[in,out] pvsk socket to set operation on. - * @param op operation to set. - * @sideeffect socket processing by AIO threads affected according to operation. - */ - -static inline void -PvskSetOpFlag(struct PvtcpSock *pvsk, - int op) -{ - unsigned int ops; - - SOCK_STATE_LOCK(pvsk); - ops = pvsk->opFlags | (1 << op); - pvsk->opFlags = ops; - SOCK_STATE_UNLOCK(pvsk); -} - - -/** - * @brief Resets operation flag for AIO processing; acquires the state lock. - * @param[in,out] pvsk socket to reset operation on. - * @param op operation to reset. - * @sideeffect socket processing by AIO threads affected according to operation. - */ - -static inline void -PvskResetOpFlag(struct PvtcpSock *pvsk, - int op) -{ - unsigned int ops; - - SOCK_STATE_LOCK(pvsk); - ops = pvsk->opFlags & ~(1 << op); - pvsk->opFlags = ops; - SOCK_STATE_UNLOCK(pvsk); -} - - -/** - * @brief Tests general purpose socket flags. - * @param pvsk socket. - * @param flag flag to test. - * @return non-zero if flag set, zero otherwise. - */ - -static inline int -PvskTestFlag(struct PvtcpSock *pvsk, - int flag) -{ - return (flag < PVTCP_OFF_PVSKF_INVALID) && (pvsk->flags & (1 << flag)); -} - - -/** - * @brief Sets general purpose socket flags; acquires the state lock. - * @param[in,out] pvsk socket. - * @param flag flag to set or clear. - * @param onOff whether to set or clear the flag. - */ - -static inline void -PvskSetFlag(struct PvtcpSock *pvsk, - int flag, - int onOff) -{ - unsigned int flags; - - SOCK_STATE_LOCK(pvsk); - if (flag < PVTCP_OFF_PVSKF_INVALID) { - if (onOff) { - flags = pvsk->flags | (1 << flag); - } else { - flags = pvsk->flags & ~(1 << flag); - } - pvsk->flags = flags; - } - SOCK_STATE_UNLOCK(pvsk); -} - - -int PvtcpOffSockInit(PvtcpSock *pvsk, CommChannel channel); - -#endif // _PVTCP_OFF_H_ diff --git a/arch/arm/mvp/pvtcpkm/pvtcp_off_io_linux.c b/arch/arm/mvp/pvtcpkm/pvtcp_off_io_linux.c deleted file mode 100644 index 9958c39..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp_off_io_linux.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Server (offload) side Linux-specific socket I/O functions. - */ - -#include "pvtcp.h" - -/* - * Data. - */ - -/* Used to check if OutputAIO()-ing is likely in progress. */ - -CommOSAtomic PvtcpOutputAIOSection; - - -/* - * Large datagram bounce buffer (PVTCP_SOCK_BUF_SIZE < size <= 64K). - * Only one such buffer is available, shared across cpus via get/put. - * A preallocated, smaller buffer is used for most over-size 'allocs'. - * A larger, 64K-buffer may need to be __vmalloc()-ed. - */ - -typedef struct LargeDgramBuf { - unsigned char buf[PVTCP_SOCK_BUF_SIZE << 1]; /* Fast buffer. */ - void *spareBuf; /* Dynamically allocated. */ - CommOSMutex lock; -} LargeDgramBuf; - -static LargeDgramBuf largeDgramBuf; - - -/** - * @brief One time initialization of large datagram buffer. - */ - -void -PvtcpOffLargeDgramBufInit(void) -{ - largeDgramBuf.spareBuf = NULL; - CommOS_MutexInit(&largeDgramBuf.lock); -} - - -/** - * @brief Reserves/holds the large datagram buffer. - * @param size size of buffer. - * @sizeeffect may sleep until the buffer is available. - * @return address of buffer, or NULL if size too large or allocation failed. - */ - -static inline void * -LargeDgramBufGet(int size) -{ - static const unsigned int maxSize = 64 * 1024; - - /* coverity[alloc_fn] */ - /* coverity[var_assign] */ - - CommOS_MutexLockUninterruptible(&largeDgramBuf.lock); - - if (size <= sizeof largeDgramBuf.buf) { - return largeDgramBuf.buf; - } - - if (size <= maxSize) { - if (!largeDgramBuf.spareBuf) { - largeDgramBuf.spareBuf = __vmalloc(maxSize, - (GFP_ATOMIC | __GFP_HIGHMEM), - PAGE_KERNEL); - } - if (largeDgramBuf.spareBuf) { - return largeDgramBuf.spareBuf; - } - } - - CommOS_MutexUnlock(&largeDgramBuf.lock); - return NULL; -} - - -/** - * @brief Releases hold on the large datagram buffer. - * @param buf buffer to put back. - */ - -static inline void -LargeDgramBufPut(void *buf) -{ - static unsigned int spareBufPuts = 0; - - BUG_ON((buf != largeDgramBuf.buf) && (buf != largeDgramBuf.spareBuf)); - - if (largeDgramBuf.spareBuf && (++spareBufPuts % 2) == 0) { - /* Deallocate the spare buffer every now and then. */ - - vfree(largeDgramBuf.spareBuf); - largeDgramBuf.spareBuf = NULL; - } - - CommOS_MutexUnlock(&largeDgramBuf.lock); -} - - -/* - * I/O offload operations. - */ - -/** - * @brief Flow control notification received when more (enough) data was - * consumed from a PV socket. - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled - */ - -void -PvtcpFlowOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - - PvtcpHoldSock(pvsk); - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - CommOS_SubReturnAtomic(&pvsk->rcvdSize, (int)packet->data32); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Outputs bytes to socket. - * @param channel communication channel with offloader. - * @param upperLayerState state associated with this channel. - * @param packet received packet header. - * @param vec payload buffer descriptors. - * @param vecLen payload buffer descriptor count. - * @sideeffect Changes send size/capacity ratio. May schedule AIO processing - * for enqueued bytes, if applicable. - */ - -void -PvtcpIoOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - int rc; - unsigned int vecOff; - PvtcpOffBuf *internalBuf; - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - unsigned int dataLen = packet->len - sizeof *packet; - struct msghdr msg = { - .msg_controllen = 0, - .msg_control = NULL - }; - int tmpSize; - int needSched = 0; - - PvtcpHoldSock(pvsk); - rc = 0; - - if (!pvsk->peerSockSet || PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_WR)) { - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - goto out; - } - - tmpSize = (int)COMM_OPF_GET_VAL(packet->flags); - if (tmpSize) { - /* It was requested that we update deltaAckSize. */ - - tmpSize = 1 << tmpSize; - CommOS_WriteAtomic(&pvsk->deltaAckSize, tmpSize); - } - - if (sk->sk_type == SOCK_STREAM) { - unsigned int queueSize = 0; - - if (!SOCK_OUT_TRYLOCK(pvsk)) { - if (pvsk->peerSockSet && - (sk->sk_state == TCP_ESTABLISHED) && - (CommOS_ReadAtomic(&pvsk->queueSize) == 0)) { - /* Attempt to write directly as many bytes as we can. */ - - msg.msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL; - rc = kernel_sendmsg(sock, &msg, vec, vecLen, dataLen); - - if (rc == -EAGAIN) { - rc = 0; - } - if (rc >= 0) { - dataLen = rc; - for (vecOff = 0; vecOff < vecLen; vecOff++) { - if (rc >= vec[vecOff].iov_len) { - /* Dispose of all fully consumed buffers. */ - - PvtcpBufFree(vec[vecOff].iov_base); - rc -= vec[vecOff].iov_len; - } else { - /* Place partly consumed / unconsumed buffers in queue. */ - - internalBuf = - PvtcpOffInternalFromBuf(vec[vecOff].iov_base); - BUG_ON(internalBuf == NULL); - if (rc > 0) { - internalBuf->len -= rc; - internalBuf->off += rc; - rc = 0; - } - CommOS_ListAddTail(&pvsk->queue, &internalBuf->link); - queueSize += internalBuf->len; - } - } - if (queueSize > 0) { - CommOS_AddReturnAtomic(&pvsk->queueSize, queueSize); - needSched = 1; - } - } else { - /* - * We never close offload sockets unless told by the PV side, - * or when the comm goes down. Getting out of sync with PV - * sockets is a dangerously bad idea. - * This is very likely an EPIPE/ECONNRESET. - */ - - dataLen = 0; - for ( vecOff = 0; vecOff < vecLen; vecOff++) { - PvtcpBufFree(vec[vecOff].iov_base); - } - } - SOCK_OUT_UNLOCK(pvsk); - } else { - SOCK_OUT_UNLOCK(pvsk); - goto enqueueBytes; - } - } else { - /* - * We enqueue the bytes for aio processing. Note that request - * level ordering is preserved since we're still under the dispatch - * lock. However, accessing 'queue' must be protected via - * the state lock to serialize with aio changes. - * Note that the struct socket *sock may have been released, but here - * we only access sk which is held (albeit potentially orphaned). - */ - - CommOSList bufList; - -enqueueBytes: - dataLen = 0; - if (pvsk->peerSockSet && (sk->sk_state == TCP_ESTABLISHED)) { - queueSize = 0; - CommOS_ListInit(&bufList); - for (vecOff = 0; vecOff < vecLen; vecOff++) { - internalBuf = PvtcpOffInternalFromBuf(vec[vecOff].iov_base); - BUG_ON(internalBuf == NULL); - CommOS_ListAddTail(&bufList, &internalBuf->link); - queueSize += internalBuf->len; - } - - if (queueSize > 0) { - SOCK_STATE_LOCK(pvsk); - CommOS_ListSpliceTail(&pvsk->queue, &bufList); - SOCK_STATE_UNLOCK(pvsk); - CommOS_AddReturnAtomic(&pvsk->queueSize, queueSize); - needSched = 1; - } - } else { - for ( vecOff = 0; vecOff < vecLen; vecOff++) { - PvtcpBufFree(vec[vecOff].iov_base); - } - } - } - } else { /* SOCK_DGRAM || SOCK_RAW */ - struct sockaddr *addr; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - int addrLen; - - /* - * Non-stream sockets don't use the send queue, packets are sent - * directly and they must _not_ be merged. - */ - - if (sk->sk_family == AF_INET) { - sin.sin_family = AF_INET; - sin.sin_port = packet->data16; - addr = (struct sockaddr *)&sin; - addrLen = sizeof sin; - sin.sin_addr.s_addr = (unsigned int)packet->data64ex; - PvtcpTestAndBindLoopbackInet4(pvsk, &sin.sin_addr.s_addr, 0); - } else { /* AF_INET6 */ - sin6.sin6_family = AF_INET6; - sin6.sin6_port = packet->data16; - addr = (struct sockaddr *)&sin6; - addrLen = sizeof sin6; - PvtcpTestAndBindLoopbackInet6(pvsk, &packet->data64ex, - &packet->data64ex2, 0); - PvtcpI6AddrUnpack(&sin6.sin6_addr.s6_addr32[0], - packet->data64ex, packet->data64ex2); - } - msg.msg_flags = packet->data32 | MSG_DONTWAIT | MSG_NOSIGNAL; - msg.msg_name = addr; - msg.msg_namelen = addrLen; - - if (pvsk->peerSockSet) { - /* - * Flow-control already done, based on PVTCP_SOCK_SAFE_RCVSIZE, just - * as with stream sockets. Meaning that we block the senders in the - * guest (if applicable). - * - * The send buffer size was set high enough, at socket creation time, - * to avoid dropping datagrams during the (non-blocking) write. - */ - - if (vecLen == 0) { - /* - * Allow zero-sized datagram sending. - */ - - struct kvec dummy = { .iov_base = NULL, .iov_len = 0 }; - - rc = kernel_sendmsg(sock, &msg, &dummy, 0, 0); - if (rc != dummy.iov_len) { -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Dgram [0x%p] sent [%d], expected [%d]\n", - __FUNCTION__, sk, rc, dummy.iov_len)); -#endif - if (rc == -EAGAIN) { /* As if lost on the wire. */ - rc = 0; - } - } - } - - for (vecOff = 0; vecOff < vecLen; vecOff++) { - rc = kernel_sendmsg(sock, &msg, &vec[vecOff], 1, - vec[vecOff].iov_len); - PvtcpBufFree(vec[vecOff].iov_base); - if (rc != vec[vecOff].iov_len) { -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Dgram [0x%p] sent [%d], expected [%d]\n", - __FUNCTION__, sk, rc, vec[vecOff].iov_len)); -#endif - if (rc == -EAGAIN) { /* As if lost on the wire. */ - rc = 0; - } - } - } - - if (COMM_OPF_TEST_ERR(packet->flags)) { - /* PV client wants an automatic bind. */ - - PvskSetOpFlag(pvsk, PVTCP_OP_BIND); - PvtcpSchedSock(pvsk); - } - } else { - for ( vecOff = 0; vecOff < vecLen; vecOff++) { - PvtcpBufFree(vec[vecOff].iov_base); - } - } - } - CommSvc_DispatchUnlock(channel); - -out: - if (rc < 0) { - pvsk->err = -rc; - } - tmpSize = CommOS_AddReturnAtomic(&pvsk->sentSize, dataLen); - if ((tmpSize >= CommOS_ReadAtomic(&pvsk->deltaAckSize)) || - pvsk->err || needSched) { - if (CommOS_AddReturnAtomic(&PvtcpOutputAIOSection, 1) == 1) { - /* OutputAIO() (likely) not running. */ - - PvtcpSchedSock(pvsk); - } - CommOS_SubReturnAtomic(&PvtcpOutputAIOSection, 1); - } - - PvtcpPutSock(pvsk); -} - - -/* - * AI/O functions called from the main AIO processing function. - */ - -/** - * @brief Processes socket flow control acks and error notifications in an - * AIO thread. This function is called with the socket 'in' lock taken. - * @param[in,out] pvsk socket to process. - * @param err non-zero if offload was closed, zero otherwise. - * @sideeffect May resume PV socket sending or raise errors. - */ - -void -PvtcpFlowAIO(PvtcpSock *pvsk, - int err) -{ - CommPacket packet = { .flags = 0 }; - unsigned long long timeout; - int tmpSize; - - COMM_OPF_CLEAR_ERR(packet.flags); - packet.data32 = PVTCP_FLOW_OP_INVALID_SIZE; - if (pvsk->err || err) { - COMM_OPF_SET_ERR(packet.flags); - packet.data32ex = !pvsk->err ? 0 : xchg(&pvsk->err, 0); - if (!packet.data32ex) { - packet.data32ex = -err; - } -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sending socket error [%u] on [0x%p -> 0x%0x].\n", - __FUNCTION__, packet.data32ex, pvsk, - (unsigned)(pvsk->peerSock))); -#endif - } else { - SOCK_STATE_LOCK(pvsk); - tmpSize = CommOS_ReadAtomic(&pvsk->deltaAckSize); - if (CommOS_ReadAtomic(&pvsk->sentSize) >= tmpSize) { - if ((SkFromPvsk(pvsk)->sk_type != SOCK_STREAM) && - !sock_writeable(SkFromPvsk(pvsk))) { - /* Don't send dgram flow op until WriteSpaceCB tells us to do so. */ - - packet.data32 = PVTCP_FLOW_OP_INVALID_SIZE; - } else { - packet.data32 = CommOS_ReadAtomic(&pvsk->sentSize); - CommOS_WriteAtomic(&pvsk->sentSize, 0); - if (tmpSize > (1 << (PVTCP_SOCK_SMALL_ACK_ORDER + 1))) { - tmpSize >>= 1; - CommOS_WriteAtomic(&pvsk->deltaAckSize, tmpSize); - } - } - } - SOCK_STATE_UNLOCK(pvsk); - packet.data32ex = 0; - } - - if (((packet.data32 != PVTCP_FLOW_OP_INVALID_SIZE) || - COMM_OPF_TEST_ERR(packet.flags)) && - pvsk->peerSockSet) { - packet.len = sizeof packet; - packet.opCode = PVTCP_OP_FLOW; - packet.data64 = pvsk->peerSock; - timeout = COMM_MAX_TO; - CommSvc_Write(pvsk->channel, &packet, &timeout); - } -} - - -/** - * @brief Processes queued socket output in an AIO thread. This function is - * called with the socket 'out' lock taken. - * @param[in,out] pvsk socket to process. - * @sideeffect Changes send size/capacity ratio. - */ - -void -PvtcpOutputAIO(PvtcpSock *pvsk) -{ - struct sock *sk; - struct socket *sock; - PvtcpOffBuf *internalBuf; - PvtcpOffBuf *tmp; - CommOSList queue; -#define VEC_SIZE 32 - struct kvec vec[VEC_SIZE]; - unsigned int vecLen; - unsigned int dataLen; - struct msghdr msg = { - .msg_controllen = 0, - .msg_control = NULL, - .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL - }; - int queueDelta = 0; - int done = 0; - int rc; - - sk = SkFromPvsk(pvsk); - if (!sk) { - /* This is an error socket, we don't process it. */ - - return; - } - - sock = sk->sk_socket; - -again: - CommOS_AddReturnAtomic(&PvtcpOutputAIOSection, 1); - while (!done && CommOS_ReadAtomic(&pvsk->queueSize) > 0) { - /* Note: only stream sockets can have a positive send queue size. - * Similar to PvtcpIoOp: we must check if sock (struct socket *) is - * still valid. - */ - - /* Take the current queue private. */ - - SOCK_STATE_LOCK(pvsk); - queue = pvsk->queue; - if (CommOS_ListEmpty(&queue)) { - SOCK_STATE_UNLOCK(pvsk); - return; - } - queue.next->prev = &queue; - queue.prev->next = &queue; - CommOS_ListInit(&pvsk->queue); - SOCK_STATE_UNLOCK(pvsk); - - vecLen = 0; - dataLen = 0; - - if (sk->sk_state == TCP_ESTABLISHED) { - CommOS_ListForEach(&queue, internalBuf, link) { - if (vecLen == VEC_SIZE) { - break; - } - vec[vecLen].iov_base = PvtcpOffBufFromInternalOff(internalBuf); - vec[vecLen].iov_len = internalBuf->len; - dataLen += internalBuf->len; - vecLen++; - } - - rc = kernel_sendmsg(sock, &msg, vec, vecLen, dataLen); - - if (rc == -EAGAIN) { - rc = 0; - } - if (rc >= 0) { - /* If we wrote anything, dispose of the buffers in question. */ - - queueDelta = rc; - if (queueDelta > 0) { - CommOS_ListForEachSafe(&queue, internalBuf, tmp, link) { - if (rc >= internalBuf->len) { - rc -= internalBuf->len; - CommOS_ListDel(&internalBuf->link); - PvtcpBufFree(PvtcpOffBufFromInternal(internalBuf)); - } else { - internalBuf->len -= rc; - internalBuf->off += rc; - break; - } - } - } - if (!CommOS_ListEmpty(&queue)) { - /* Add the remaining bytes to the beginning of the queue. */ - - SOCK_STATE_LOCK(pvsk); - CommOS_ListSplice(&pvsk->queue, &queue); - SOCK_STATE_UNLOCK(pvsk); - } - if (queueDelta == 0) { - /* Bail out if no bytes written, WriteSpaceCB() will resched. */ - - done = 1; - break; - } - CommOS_AddReturnAtomic(&pvsk->sentSize, queueDelta); - CommOS_SubReturnAtomic(&pvsk->queueSize, queueDelta); - } else { - /* - * Very likely, this is due to the socket being closed, so fine. - */ - - goto discardOutput; - } - } else { - /* Dispose of all buffers in the queue and mark it empty. */ - -discardOutput: - if (!CommOS_ListEmpty(&queue)) { - CommOS_ListForEachSafe(&queue, internalBuf, tmp, link) { - CommOS_ListDel(&internalBuf->link); - PvtcpBufFree(PvtcpOffBufFromInternal(internalBuf)); - } - } - CommOS_WriteAtomic(&pvsk->queueSize, 0); - break; - } - } - if (CommOS_SubReturnAtomic(&PvtcpOutputAIOSection, 1) > 0) { - if (!done) { - goto again; - } - } - - if (PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_WR)) { - kernel_sock_shutdown(sock, SHUT_WR); - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_WR, 0); - } -#undef VEC_SIZE -} - - -/** - * @brief Processes socket input in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket to process. - * @param[in,out] perCpuBuf per-cpu socket read buffer. - * @return zero if eof was not detected, non-zero otherwise. - * @sideeffect Changes receive size/capacity ratio. - */ - -int -PvtcpInputAIO(PvtcpSock *pvsk, - void *perCpuBuf) -{ - struct sock *sk; - struct socket *sock; - int err = 0; - CommPacket packet = { - .opCode = PVTCP_OP_IO - }; - unsigned long long timeout; - - sk = SkFromPvsk(pvsk); - if (!sk) { - /* IO processing is skipped on socket create-error sockets. */ - - return -1; - } - if (!perCpuBuf) { - /* No read buffer. */ - - return -1; - } - - sock = sk->sk_socket; - packet.data64 = pvsk->peerSock; - COMM_OPF_CLEAR_ERR(packet.flags); - - if (sk->sk_state == TCP_LISTEN) { - /* Process stream listen 'input'. */ - - packet.len = sizeof packet; - packet.data16 = sk->sk_ack_backlog; - timeout = COMM_MAX_TO; - if (pvsk->peerSockSet) { - CommSvc_Write(pvsk->channel, &packet, &timeout); - CommOS_Debug(("%s: Listen sock [0x%p] 'ack_backlog' [%hu].\n", - __FUNCTION__, sk, packet.data16)); - } - } else { - /* Common path for both stream and datagram sockets. */ - - int rc; - int tmpSize; - struct kvec vec[2]; - void *ioBuf = perCpuBuf; - struct kvec *inVec; - unsigned int inVecLen; - unsigned int iovOffset = 0; - unsigned int inputSize = 0; - unsigned int coalescingSize = PVTCP_SOCK_RCVSIZE >> 2; - struct sockaddr_in sin = { .sin_family = AF_INET }; - struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6 }; - struct msghdr msg = { - .msg_controllen = 0, - .msg_control = NULL, - .msg_flags = MSG_DONTWAIT - }; - int tmpFlags = msg.msg_flags; - PvtcpDgramPseudoHeader dgramHeader; - - tmpSize = CommOS_ReadAtomic(&pvsk->rcvdSize); - while ((tmpSize < PVTCP_SOCK_SAFE_RCVSIZE) && pvsk->peerSockSet) { - if (ioBuf != perCpuBuf) { - LargeDgramBufPut(ioBuf); - ioBuf = perCpuBuf; - } - vec[0].iov_base = (char *)ioBuf; - - if (sk->sk_type == SOCK_STREAM) { - if (PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_RD)) { - break; - } - - msg.msg_name = NULL; - msg.msg_namelen = 0; - vec[0].iov_len = PVTCP_SOCK_STREAM_BUF_SIZE; - } else { /* SOCK_DGRAM || SOCK_RAW */ - if (sk->sk_family == AF_INET) { - msg.msg_name = &sin; - msg.msg_namelen = sizeof sin; - } else { - msg.msg_name = &sin6; - msg.msg_namelen = sizeof sin6; - } - - /* - * Check if datagram larger than the per cpu buffer; if so, - * allocate a large enough buffer. This should happen quite - * rarely, as well-behaved applications don't rely on IP - * fragmentation to accommodate large sizes. - */ - - vec[0].iov_len = 1; - msg.msg_flags |= (MSG_PEEK | MSG_TRUNC); - rc = kernel_recvmsg(sock, &msg, vec, 1, 1, msg.msg_flags); - if (rc < 0) { - break; - } - msg.msg_flags = tmpFlags; - if (rc > PVTCP_SOCK_DGRAM_BUF_SIZE) { - /* - * Track large datagram allocations, whether allocation succeeds - * or not. No need for atomic overhead, approximating is OK. - */ - - pvtcpOffDgramAllocations++; - ioBuf = LargeDgramBufGet(rc); - if (!ioBuf) { - /* - * We reset it to the per-cpu buffer such that we can still - * consume the datagram in the next recvmsg, which will set - * MSG_TRUNC so we won't put it on the channel. - */ - - CommOS_Debug(("%s: Dropping datagram (alloc failure)!\n", - __FUNCTION__)); - ioBuf = perCpuBuf; - vec[0].iov_len = PVTCP_SOCK_DGRAM_BUF_SIZE; - } else { - vec[0].iov_len = rc; - } - } else { - vec[0].iov_len = PVTCP_SOCK_DGRAM_BUF_SIZE; - } - vec[0].iov_base = (char *)ioBuf; - } - - rc = kernel_recvmsg(sock, &msg, vec, 1, vec[0].iov_len, msg.msg_flags); - if (rc < 0) { - break; - } - - if ((rc == 0) && (sk->sk_type == SOCK_STREAM)) { - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_RD, 1); - err = -ECONNRESET; - break; - } - - if (msg.msg_flags & MSG_TRUNC) { - continue; - } - - inputSize += rc; - tmpSize = CommOS_AddReturnAtomic(&pvsk->rcvdSize, rc); - if (tmpSize >= PVTCP_SOCK_LARGE_ACK_WM) { - COMM_OPF_SET_VAL(packet.flags, PVTCP_SOCK_LARGE_ACK_ORDER); - } else { - COMM_OPF_SET_VAL(packet.flags, 0); - } - - if (sk->sk_type == SOCK_STREAM) { - vec[0].iov_base = ioBuf; - vec[0].iov_len = rc; - inVecLen = 1; - packet.len = sizeof packet + rc; - } else { /* SOCK_DGRAM || SOCK_RAW */ - if (sk->sk_family == AF_INET) { - dgramHeader.d0 = (unsigned long long)sin.sin_port; - PvtcpResetLoopbackInet4(pvsk, &sin.sin_addr.s_addr); - dgramHeader.d1 = (unsigned long long)sin.sin_addr.s_addr; - } else { /* AF_INET6 */ - dgramHeader.d0 = (unsigned long long)sin6.sin6_port; - PvtcpResetLoopbackInet6(pvsk, &sin6.sin6_addr); - PvtcpI6AddrPack(&sin6.sin6_addr.s6_addr32[0], - &dgramHeader.d1, &dgramHeader.d2); - } - vec[0].iov_base = &dgramHeader; - vec[0].iov_len = sizeof dgramHeader; - vec[1].iov_base = ioBuf; - vec[1].iov_len = rc; - inVecLen = 2; - packet.len = sizeof packet + sizeof dgramHeader + rc; - } - - inVec = vec; - timeout = COMM_MAX_TO; - rc = CommSvc_WriteVec(pvsk->channel, &packet, - &inVec, &inVecLen, &timeout, &iovOffset); - if (rc != packet.len) { - CommOS_Log(("%s: BOOG -- WROTE INCOMPLETE PACKET [%u->%d]!\n", - __FUNCTION__, packet.len, rc)); - break; - } - - /* - * If the write failed, we could print a warning. But if this - * happened, the comm channel went down. - */ - if (inputSize >= coalescingSize) { - PvtcpSchedSock(pvsk); /* We must schedule ourselves back in. */ - break; - } - } - if (ioBuf != perCpuBuf) { - LargeDgramBufPut(ioBuf); - } - } - return err; -} diff --git a/arch/arm/mvp/pvtcpkm/pvtcp_off_linux.c b/arch/arm/mvp/pvtcpkm/pvtcp_off_linux.c deleted file mode 100644 index 047547f..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp_off_linux.c +++ /dev/null @@ -1,2858 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Server (offload) side Linux-specific functions and callbacks. - */ - - -#include "pvtcp.h" - -#if defined(CONFIG_NET_NS) -#include -#include -#endif - -#include -#include -#include -#include -#include - - -/* The PVSock address (127.238.0.1) in binary form, host byte order. */ -#define PVTCP_PVSOCK_ADDR 0x7fee0001 -#define PVTCP_PVSOCK_NET 0x7fee0000 -#define PVTCP_PVSOCK_MASK 0x000000ff - -/* From mvpkm */ -extern uid_t Mvpkm_vmwareUid; - -/* - * Credentials to back socket file pointer. Used in Android ICS network - * data usage accounting to bill guest data to MVP. - */ -static struct cred _cred; -static struct file _file = { - .f_cred = &_cred, -}; - -/* From pvtcp_off_io_linux.c */ -extern CommOSAtomic PvtcpOutputAIOSection; -extern void PvtcpOffLargeDgramBufInit(void); - -static const unsigned short portRangeBase = 7000; -static const unsigned int portRangeSize = 31; -static int hooksRegistered = 0; - -static inline int PvtcpTestPortIndexBit(unsigned int addr, - unsigned int portIdx); -/** - * @note - * Netfilter hooks: - * - * We decide to drop each packet based on the following criteria: - * 1) Destination address is to a pvsock address AND - * 3) (NOT(uid == 0 OR uid == vmwareUid)) OR - * 4) (type == UDP AND NOT(port-in-pvsock-range))) - */ - -/** - * @brief Netfilter hook. Restricts LOCAL_OUT packets. - * See note above to filter policy. - * @param skb skbuff - * @param inet6 is this socket ipv4 or ipv6? - * @return NF_ACCEPT if the packet is allowed through, NF_DROP otherwise - */ -static inline unsigned int -PvsockNfHook(struct sk_buff *skb, int inet6) -{ - uid_t uid; - unsigned int port; - struct socket *sock; - unsigned int addr = inet6 ? - ntohl(ipv6_hdr(skb)->daddr.s6_addr32[3]) : - ntohl(ip_hdr(skb)->daddr); - - if (likely((addr ^ PVTCP_PVSOCK_NET) & ~PVTCP_PVSOCK_MASK)) { - /* Not a pvsock address. */ - return NF_ACCEPT; - } - - sock = skb->sk->sk_socket; - if (unlikely(!sock)) { - return NF_ACCEPT; - } - - /* - * Guest (kernel) sockets can send to other guest sockets, - * Root can send to whoever it wants, no checks. - */ - uid = (sock->file ? sock->file->f_cred->uid : 0); - if (uid == 0 || (sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM)) { - return NF_ACCEPT; - } - - /* - * Only vmware can send to guest. - */ - if (likely(uid == Mvpkm_vmwareUid)) { - if (sock->type == SOCK_DGRAM) { - /* - * Deny sending to UDP port in pvsock range, if receiving socket was - * not created by the guest with this pvsock address. Drop all other - * UDP packets. - */ - port = ntohs(udp_hdr(skb)->dest) - portRangeBase; - if ((port < portRangeSize) && - PvtcpTestPortIndexBit(htonl(addr), port)) { - return NF_ACCEPT; - } - return NF_DROP; - } - /* - * TCP is all-good. - */ - return NF_ACCEPT; - } - - return NF_DROP; -} - - -/** - * @brief AF_INET4 Netfilter hook. Restricts LOCAL_OUT packets. - * See note above to filter policy. - * @param hooknum netfilter hook number - * @param skb skbuff - * @param in rx net_device - * @param out out net_device - * @param okfn ignored - * @return NF_ACCEPT if the packet is allowed through, NF_DROP otherwise - */ -static unsigned int -Inet4NfHook(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - return PvsockNfHook(skb, 0); -} - -/** - * @brief AF_INET6 Netfilter hook. Restricts LOCAL_OUT packets. - * See note above to filter policy. - * @param hooknum netfilter hook number - * @param skb skbuff - * @param in rx net_device - * @param out out net_device - * @param okfn ignored - * @return NF_ACCEPT if the packet is allowed through, NF_DROP otherwise - */ -static unsigned int -Inet6NfHook(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - if (!ipv6_addr_v4mapped(&ipv6_hdr(skb)->daddr)) { - /* Not ipv4-mapped, so not a pvsock address. */ - return NF_ACCEPT; - } - - return PvsockNfHook(skb, 1); -} - - -static struct nf_hook_ops netfilterHooks[] = { - { - .hook = Inet4NfHook, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_INET_LOCAL_OUT, - .priority = NF_IP_PRI_SECURITY - }, - { - .hook = Inet6NfHook, - .owner = THIS_MODULE, - .pf = PF_INET6, - .hooknum = NF_INET_LOCAL_OUT, - .priority = NF_IP6_PRI_SECURITY - } -}; - - -#if !defined(CONFIG_SYSFS) -#error "The pvTCP offload module requires sysfs!" -#endif - -/* - * State kobject, attributes and type. - */ - -typedef struct PvtcpStateKObj { - struct kobject kobj; - CommTranspInitArgs transpArgs; - unsigned int pvsockAddr; - int useNS; - int haveNS; -} PvtcpStateKObj; - - -typedef struct PvtcpStateKObjAttr { - struct attribute attr; - ssize_t (*show)(PvtcpStateKObj *stateKObj, char *buf); - ssize_t (*store)(PvtcpStateKObj *stateKObj, const char *buf, size_t count); -} PvtcpStateKObjAttr; - - -/** - * @brief Releases state a kobject. - * @param kobj (embedded) state kobject. - */ - -static void -StateKObjRelease(struct kobject *kobj) -{ - kfree(container_of(kobj, PvtcpStateKObj, kobj)); -} - - -/** - * @brief Sysfs show function for all pvtcp attributes. - * @param kobj (embedded) state kobject. - * @param attr pvtcp attribute to show. - * @param buf output buffer. - * @return number of bytes written or negative error code. - */ - -static ssize_t -StateKObjShow(struct kobject *kobj, - struct attribute *attr, - char *buf) -{ - PvtcpStateKObjAttr *stateAttr = container_of(attr, PvtcpStateKObjAttr, attr); - PvtcpStateKObj *stateKObj = container_of(kobj, PvtcpStateKObj, kobj); - - if (stateAttr->show) { - return stateAttr->show(stateKObj, buf); - } - - return -EIO; -} - - -/** - * @brief Sysfs store function for all pvtcp attributes. - * @param kobj (embedded) state kobject. - * @param attr pvtcp attribute to show. - * @param buf input buffer. - * @param count input buffer length. - * @return number of bytes consumed or negative error code. - */ - -static ssize_t -StateKObjStore(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t count) -{ - PvtcpStateKObjAttr *stateAttr = container_of(attr, PvtcpStateKObjAttr, attr); - PvtcpStateKObj *stateKObj = container_of(kobj, PvtcpStateKObj, kobj); - - if (stateAttr->store) { - return stateAttr->store(stateKObj, buf, count); - } - - return -EIO; -} - - -static struct sysfs_ops StateKObjSysfsOps = { - .show = StateKObjShow, - .store = StateKObjStore -}; - - -/** - * @brief Show function for the comm_info pvtcp attribute. - * @param stateKObj state kobject. - * @param buf output buffer. - * @return number of bytes written or negative error code. - */ - -static ssize_t -StateKObjCommInfoShow(PvtcpStateKObj *stateKObj, - char *buf) -{ - unsigned int typeHash; - - /* - * In the offload module, the transport arguments' type field has been - * assigned the matching index in the versions array at probe time. - * Recover and print out the type hash. - */ - - typeHash = CommTransp_GetType(pvtcpVersions[stateKObj->transpArgs.type]); - - return snprintf(buf, PAGE_SIZE, "ID=%u,%u\nCAPACITY=%u\nTYPE=0x%0x\n", - stateKObj->transpArgs.id.d32[0], - stateKObj->transpArgs.id.d32[1], - stateKObj->transpArgs.capacity, - typeHash); -} - - -/** - * @brief Show function for the pvsock_addr pvtcp attribute. - * @param stateKObj state kobject. - * @param buf output buffer. - * @return number of bytes written or negative error code. - */ - -static ssize_t -StateKObjPvsockAddrShow(PvtcpStateKObj *stateKObj, - char *buf) -{ - union { - unsigned int raw; - unsigned char bytes[4]; - } addr; - - addr.raw = stateKObj->pvsockAddr; - return snprintf(buf, PAGE_SIZE, "%u.%u.%u.%u\n", - (unsigned int)addr.bytes[0], (unsigned int)addr.bytes[1], - (unsigned int)addr.bytes[2], (unsigned int)addr.bytes[3]); -} - - -/** - * @brief Show function for the use_ns pvtcp attribute. - * @param stateKObj state kobject. - * @param buf output buffer. - * @return number of bytes written or negative error code. - */ - -static ssize_t -StateKObjUseNSShow(PvtcpStateKObj *stateKObj, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", stateKObj->useNS); -} - - -/** - * @brief Store function for the use_ns pvtcp attribute. - * @param stateKObj state kobject. - * @param buf input buffer. - * @param count input buffer length. - * @return number of bytes consumed or negative error code. - */ - -static ssize_t -StateKObjUseNSStore(PvtcpStateKObj *stateKObj, - const char *buf, - size_t count) -{ - int rc = -EINVAL; - - /* coverity[secure_coding] */ - if (stateKObj->haveNS && (sscanf(buf, "%d", &stateKObj->useNS) == 1)) { - stateKObj->useNS = !!stateKObj->useNS; - rc = count; - } - - return rc; -} - - -static PvtcpStateKObjAttr stateKObjCommInfoAttr = - __ATTR(comm_info, 0444, StateKObjCommInfoShow, NULL); - -static PvtcpStateKObjAttr stateKObjPvsockAddrAttr = - __ATTR(pvsock_addr, 0444, StateKObjPvsockAddrShow, NULL); - -static PvtcpStateKObjAttr stateKObjUseNSAttr = - __ATTR(use_ns, 0644, StateKObjUseNSShow, StateKObjUseNSStore); - - -static struct attribute *stateKObjDefaultAttrs[] = { - &stateKObjCommInfoAttr.attr, - &stateKObjPvsockAddrAttr.attr, - &stateKObjUseNSAttr.attr, - NULL -}; - - -static struct kobj_type stateKType = { - .sysfs_ops = &StateKObjSysfsOps, - .release = StateKObjRelease, - .default_attrs = stateKObjDefaultAttrs -}; - - -/* - * Initialization of module entry and exit callbacks. - */ - -static int Init(void *args); -static void Exit(void); - -COMM_OS_MOD_INIT(Init, Exit); - - -/* - * AIO socket read buffers, stats and other global state. - */ - -static CommOSMutex globalLock; -static char perCpuBuf[NR_CPUS][PVTCP_SOCK_BUF_SIZE]; - -#define PVTCP_OFF_MAX_LB_ADDRS 255 -static unsigned int loopbackAddrs[PVTCP_OFF_MAX_LB_ADDRS] = { - 0xffffffff, // Network address always on, all ports allowed. - 0x7fffffff // Host address not yet on, all ports allowed. - // All the rest zeroed out. -}; - -static const unsigned int loopbackReserved = 0x00000001 << 31; - - -#define PvtcpTestLoopbackBit(entry, mask) \ - ((entry) & (mask)) - -#define PvtcpSetLoopbackBit(entry, mask) \ - ((entry) |= (mask)) - -#define PvtcpResetLoopbackBit(entry, mask) \ - ((entry) &= ~(mask)) - - -static inline int -PvtcpTestPortIndexBit(unsigned int addr, - unsigned int portIdx) -{ - return PvtcpTestLoopbackBit(loopbackAddrs[*((unsigned char *)&addr + 3)], - BIT(portIdx)); -} - - -static inline void -PvtcpSetPortIndexBit(unsigned int addr, - unsigned int portIdx) -{ - PvtcpSetLoopbackBit(loopbackAddrs[*((unsigned char *)&addr + 3)], - BIT(portIdx)); -} - - -static inline void -PvtcpResetPortIndexBit(unsigned int addr, - unsigned int portIdx) -{ - PvtcpResetLoopbackBit(loopbackAddrs[*((unsigned char *)&addr + 3)], - BIT(portIdx)); -} - - -unsigned int pvtcpLoopbackOffAddr; - -unsigned long long pvtcpOffDgramAllocations = 0; - -/* - * Destructor shim addresses and function pointer - */ - -extern void asmDestructorShim(struct sock*); - - -/* - * Functions. - */ - -/** - * @brief Release a socket, NULLing out the fake file field to avoid confusing - * Linux on the release path - * @param sock socket to release - */ -static void -SockReleaseWrapper(struct socket *sock) -{ - sock->file = NULL; - sock_release(sock); -} - -/** - * @brief Gets a new loopback address in the 127.238.0.255 network. - * Note that the first address, 127.238.0.1, is always the host's. - * @return new address or -1U if none is available. - */ - -static unsigned int -GetLoopbackAddr(void) -{ - static unsigned char addrTempl[4] = { 127, 238, 0, 0 }; - unsigned int rc = -1U; - unsigned int idx; - struct socket *sock; - - CommOS_MutexLock(&globalLock); - for (idx = 1; idx < PVTCP_OFF_MAX_LB_ADDRS; idx++) { - if (!PvtcpTestLoopbackBit(loopbackAddrs[idx], loopbackReserved)) { - addrTempl[3] = (unsigned char)idx; - memcpy(&rc, addrTempl, sizeof rc); - - /* Create a dgram socket to configure/bring-up the lo:N interface. */ - - if (!sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock)) { - int err; - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_addr = { .s_addr = rc } - }; - struct ifreq ifr = { - .ifr_flags = IFF_UP - }; - - snprintf(ifr.ifr_name, sizeof ifr.ifr_name, "lo:%u", idx); - memcpy(&ifr.ifr_addr, &sin, sizeof ifr.ifr_addr); - err = kernel_sock_ioctl(sock, SIOCSIFADDR, (unsigned long)&ifr); - sock_release(sock); - if (err) { - CommOS_Log(("%s: Could not set loopback address (ioctl)!\n", - __FUNCTION__)); - rc = -1U; - continue; /* Try next address. */ - } else { - PvtcpSetLoopbackBit(loopbackAddrs[idx], loopbackReserved); - CommOS_Debug(("%s: Allocated loopback address [%u.%u.%u.%u].\n", - __FUNCTION__, - addrTempl[0], addrTempl[1], - addrTempl[2], addrTempl[3])); - break; - } - } else { - CommOS_Log(("%s: Could not set loopback address (create)!\n", - __FUNCTION__)); - rc = -1U; - break; - } - } - } - if (idx == PVTCP_OFF_MAX_LB_ADDRS) { - CommOS_Log(("%s: loopback address range exceeded!\n", __FUNCTION__)); - } - - CommOS_MutexUnlock(&globalLock); - return rc; -} - - -/** - * @brief Puts back a loopback address in the 127.238.0.255 network. - * @param uaddr address to put back. - */ - -static void -PutLoopbackAddr(unsigned int uaddr) -{ - const unsigned char addrTempl[3] = { 127, 238, 0 }; - unsigned char addr[4]; - unsigned int idx; - struct socket *sock; - - memcpy(addr, &uaddr, sizeof uaddr); - if (memcmp(addrTempl, addr, sizeof addrTempl)) { - return; - } - - idx = addr[3]; - if ((idx == 0) || (idx >= PVTCP_OFF_MAX_LB_ADDRS)) { - return; - } - - CommOS_MutexLock(&globalLock); - if (!PvtcpTestLoopbackBit(loopbackAddrs[idx], loopbackReserved)) { - CommOS_Debug(("%s: loopback entry [%u] already freed.\n", - __FUNCTION__, idx)); - goto out; - } - - if (!sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock)) { - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_addr = { .s_addr = uaddr } - }; - struct ifreq ifr = { - .ifr_flags = 0 - }; - - snprintf(ifr.ifr_name, sizeof ifr.ifr_name, "lo:%u", idx); - memcpy(&ifr.ifr_addr, &sin, sizeof ifr.ifr_addr); - kernel_sock_ioctl(sock, SIOCSIFFLAGS, (unsigned long)&ifr); - sock_release(sock); - loopbackAddrs[idx] = 0; // Zero everything out. - CommOS_Debug(("%s: Deallocated loopback address [%u.%u.%u.%u].\n", - __FUNCTION__, addr[0], addr[1], addr[2], addr[3])); - } else { - CommOS_Log(("%s: Could not delete loopback address!\n", - __FUNCTION__)); - } - -out: - CommOS_MutexUnlock(&globalLock); -} - - -/** - * @brief Retrieves and retains the namespace associated with a channel. - * A server must be listening for requests to retrieve the pid of the - * process owning the net namespace for the passed context/vm id. - * Communication takes place over a datagram socket in the AF_UNIX family, - * bound to "/usr/lib/vmware/pvtcp/config/serv_addr". - * @param state channel state for which to retrieve the network namespace. - * @sideeffect If an associated namespace is found, it is retained and saved - * in the state object. - */ - -static void -GetNetNamespace(PvtcpState *state) -{ -#if defined(CONFIG_NET_NS) && !defined(PVTCP_NET_NS_DISABLE) - CommTranspInitArgs args; - pid_t pidn; - struct pid *pid; - struct task_struct *tsk; - struct nsproxy *nsproxy; - struct net *ns; - struct socket *sock; - struct sockaddr_un addr = { - .sun_family = AF_UNIX - }; - struct timeval timeout = { - .tv_sec = 3000, - .tv_usec = 0 - }; - const int passcred = 1; - char buf[64]; - struct kvec vec; - const char *sockname = "pvtcp-vpn"; /* abstract namespace for AF_UNIX/LOCAL sockets */ - const size_t socknamelen = strlen(sockname); - - struct msghdr msg = { - .msg_name = (struct sockaddr *)&addr, - .msg_namelen = 1 + offsetof(struct sockaddr_un, sun_path) + socknamelen - }; - - - if (!state) { - return; - } - - args = CommSvc_GetTranspInitArgs(state->channel); - ns = NULL; - pidn = 0; - - if (sock_create_kern(AF_UNIX, SOCK_DGRAM, 0, &sock)) { - CommOS_Debug(("%s: Can't create config socket!\n", __FUNCTION__)); - goto out; - } - if (kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, - (char *)&timeout, sizeof timeout)) { - sock_release(sock); - CommOS_Debug(("%s: Can't set timeout on config socket!\n", __FUNCTION__)); - goto out; - } - if (kernel_setsockopt(sock, SOL_SOCKET, SO_PASSCRED, - (char *)&passcred, sizeof passcred)) { - sock_release(sock); - CommOS_Debug(("%s: Can't set passcred on config socket!\n", - __FUNCTION__)); - goto out; - } - - /* - * Send the configuration request and receive the reply: - * - the request carries the VM/guest ID as used in the transport - * arguments used to create the channel. - * - the reply is expected to contain the pid of the namespace owner. - */ - - memset(buf, 0, sizeof buf); - snprintf(buf, sizeof buf, "%u\n", args.id.d32[0]); - buf[sizeof buf - 1] = '\0'; - vec.iov_base = buf; - vec.iov_len = strlen(buf); - - /* use anonymous name */ - addr.sun_path[0] = 0; - memcpy(addr.sun_path+1, sockname, socknamelen); - - if (kernel_sendmsg(sock, &msg, &vec, 1, vec.iov_len) <= 0) { - sock_release(sock); - CommOS_Debug(("%s: Could not send config request for vm [%u]!\n", - __FUNCTION__, args.id.d32[0])); - goto out; - } - - memset(buf, 0, sizeof buf); - vec.iov_base = buf; - vec.iov_len = sizeof buf; - if (kernel_recvmsg(sock, &msg, &vec, 1, vec.iov_len, 0) <= 0) { - CommOS_Debug(("%s: Could not receive config reply for vm [%u]!\n", - __FUNCTION__, args.id.d32[0])); - } else { - buf[sizeof buf - 1] = '\0'; - /* coverity[secure_coding] */ - sscanf(buf, "%d", &pidn); - } - sock_release(sock); - - if (!pidn) { - goto out; - } - - pid = find_get_pid(pidn); - if (pid) { - tsk = pid_task(pid, PIDTYPE_PID); - if (tsk) { - rcu_read_lock(); - nsproxy = task_nsproxy(tsk); - if (nsproxy && nsproxy->net_ns) { - ns = maybe_get_net(nsproxy->net_ns); - } - rcu_read_unlock(); - } - put_pid(pid); - } - -out: - if (!ns) { - CommOS_Debug(("%s: Not using a namespace for vm [%u].\n", - __FUNCTION__, args.id.d32[0])); - ns = &init_net; - } else { - CommOS_Debug(("%s: Found the net namespace for vm [%u].\n", - __FUNCTION__, args.id.d32[0])); - } -#else - void *ns = NULL; -#endif - - state->namespace = ns; -} - - -/** - * @brief Releases the network namespace associated with a channel state. - * @param namespace namespace to be released. - * @sideeffect If the namespace is not the initial one, it is released. - */ - -static void -PutNetNamespace(void *namespace) -{ -#if defined(CONFIG_NET_NS) && !defined(PVTCP_NET_NS_DISABLE) - if (namespace && (namespace != &init_net)) { - put_net((struct net *)namespace); - } -#endif -} - - -/** - * @brief Offload state constructor called when a channel is created. - * The function first calls the default state allocator; it then retrieves - * the n/w namespace associated with this client, retains it and stores it - * in the state object. Finally, it creates a sysfs node. - * @param[in,out] channel channel to initialize. - * @return pointer to a new state structure or NULL. - * @sideeffect Allocates memory. - */ - -static void * -StateAlloc(CommChannel channel) -{ - extern struct kset *Mvpkm_FindVMNamedKSet(int, const char *); - PvtcpState *state = NULL; - PvtcpIf *loopbackNetif = NULL; - PvtcpStateKObj *stateKObj = NULL; - struct kset *kset = NULL; - int rc; - CommTranspInitArgs transpArgs; - - transpArgs = CommSvc_GetTranspInitArgs(channel); - - /* - * The transport ID is assigned in an implementation-dependent way. - * (see lib/comm/comm_transp.h for transport type definitions.) - * However, the first 32 bits are expected to denote the guest/VM ID, - * while the last 32 bits are a resource handle within that VM. On MVP, - * transports map to queue pairs, which follow this convention. - */ - - kset = Mvpkm_FindVMNamedKSet((int)transpArgs.id.d32[0], "devices"); - if (!kset) { - CommOS_Debug(("%s: Could not find sysfs '.../vm/N/devices' kset!\n", - __FUNCTION__)); - goto error; - } - - state = PvtcpStateAlloc(channel); - if (!state) { - CommOS_Debug(("%s: Could not allocate state!\n", __FUNCTION__)); - goto error; - } - - /* coverity[leaked_storage] */ - stateKObj = kzalloc(sizeof *stateKObj, GFP_KERNEL); - if (!stateKObj) { - CommOS_Debug(("%s: Could not allocate state kobject!\n", __FUNCTION__)); - goto error; - } - - stateKObj->kobj.kset = kset; - /* coverity[leaked_storage] */ - rc = kobject_init_and_add(&stateKObj->kobj, &stateKType, NULL, "pvtcp"); - if (rc) { - CommOS_Debug(("%s: Could not add state kobject to parent kset [%d]!\n", - __FUNCTION__, rc)); - goto error; - } - - loopbackNetif = PvtcpStateFindIf(state, pvtcpIfLoopbackInet4); - BUG_ON(loopbackNetif == NULL); - loopbackNetif->conf.addr.in.s_addr = GetLoopbackAddr(); - if (loopbackNetif->conf.addr.in.s_addr == -1U) { - CommOS_Log(("%s: Could not allocate loopback address!\n", __FUNCTION__)); - goto error; - } - - GetNetNamespace(state); - - stateKObj->transpArgs = transpArgs; - stateKObj->pvsockAddr = loopbackNetif->conf.addr.in.s_addr; -#if defined(CONFIG_NET_NS) - stateKObj->haveNS = (state->namespace != &init_net); - stateKObj->useNS = stateKObj->haveNS; -#endif - state->extra = stateKObj; - - _cred.uid = _cred.gid = _cred.suid = _cred.sgid = - _cred.euid = _cred.egid = _cred.fsuid = _cred.fsgid = Mvpkm_vmwareUid; - - -out: - if (kset) { - kset_put(kset); - } - return state; - -error: - if (stateKObj) { - kobject_del(&stateKObj->kobj); - kobject_put(&stateKObj->kobj); - } - if (loopbackNetif && (loopbackNetif->conf.addr.in.s_addr != -1U)) { - PutLoopbackAddr(loopbackNetif->conf.addr.in.s_addr); - } - if (state) { - PvtcpStateFree(state); - state = NULL; - } - goto out; -} - - -/** - * @brief Offload state destructor called when a channel is closed. - * The function releases this client's n/w namespace and then calls the - * default state deallocator. - * @param arg pointer to state structure. - * @sideeffect Destroys all netifs and their sockets, deallocates memory. - */ - -static void -StateFree(void *arg) -{ - PvtcpState *state = arg; - PvtcpIf *loopbackNetif; - void *namespace; - - if (!state) { - return; - } - - if (state->extra) { - PvtcpStateKObj *stateKObj = state->extra; - - kobject_del(&stateKObj->kobj); - kobject_put(&stateKObj->kobj); - } - - namespace = state->namespace; - loopbackNetif = PvtcpStateFindIf(state, pvtcpIfLoopbackInet4); - BUG_ON(loopbackNetif == NULL); - PutLoopbackAddr(loopbackNetif->conf.addr.in.s_addr); - PvtcpStateFree(state); - PutNetNamespace(namespace); -} - - -/** - * @brief Releases socket. This function is called when the channel state - * owning the socket is closed. - * @param[in,out] pvsk PV socket to release. - * @sideeffect the socket eventually gets deallocated. - */ - -void -PvtcpReleaseSocket(PvtcpSock *pvsk) -{ - struct socket *sock = SkFromPvsk(pvsk)->sk_socket; - - SOCK_IN_LOCK(pvsk); - SOCK_OUT_LOCK(pvsk); - pvsk->peerSockSet = 0; - SockReleaseWrapper(sock); - SOCK_OUT_UNLOCK(pvsk); - SOCK_IN_UNLOCK(pvsk); - CommOS_Debug(("%s: [0x%p].\n", __FUNCTION__, pvsk)); -} - - -/** - * @brief Tests if the passed address is 127.238.0.1 or 127.0.0.1. - * @param pvsk socket to test. - * @param addr inet4 address to test. - * @return > 1: morph and propagate new address to caller, 1: just morph, - * 0: don't morph, < 0 (-EADDRNOTAVAIL): bad loopback. - */ - -static inline int -TestLoopbackInet4(PvtcpSock *pvsk, - unsigned int addr) -{ - if (!ipv4_is_loopback(addr)) { - return 0; - } - - if (addr != htonl(PVTCP_PVSOCK_ADDR)) { - if (addr != htonl(INADDR_LOOPBACK)) { - return -EADDRNOTAVAIL; - } - if (PvtcpHasSockNamespace(pvsk)) { - /* We don't morph normal 127.0.0.1 when NS present. */ - - return 0; - } - return 2; - } - - return 1; -} - - -/** - * @brief Tests if the passed address is 127.238.0.1 or 127.0.0.1 and the - * socket has a namespace. If yes, the address will be morphed into - * the actual loopback address, then a bind() is performed. - * Note that the function returns EADDRNOTAVAIL for any other loopbacks. - * @param pvsk socket to test. - * @param[in,out] addr inet4 address to test. - * @param port port to bind, or zero for any port. - * @return 1 if bind should be performed by caller, bind return code otherwise. - */ - -int -PvtcpTestAndBindLoopbackInet4(PvtcpSock *pvsk, - unsigned int *addr, - unsigned short port) -{ - int rc; - struct sockaddr_in sin; - unsigned int morphedAddr; - int propagate = 0; - - rc = TestLoopbackInet4(pvsk, *addr); - switch (rc) { - case 2: - propagate = 1; // Fall through. - case 1: - break; // Proceed with morphing. - case 0: - return 1; // Don't morph, let bind() be done by caller. - default: - return rc; - } - - if (pvsk->netif->conf.family == PVTCP_PF_LOOPBACK_INET4) { - /* The socket has already been morphed/bound. */ - - morphedAddr = pvsk->netif->conf.addr.in.s_addr; - rc = 0; - goto out; - } - - /* - * Move the socket to the initial namespace before binding it - * such that the loopback address is accessible to the host. - */ - - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_INITIAL); - PvtcpStateAddSocket(pvsk->channel, pvtcpIfLoopbackInet4, pvsk); - morphedAddr = pvsk->netif->conf.addr.in.s_addr; - memset(&sin, 0, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_port = port; - sin.sin_addr.s_addr = morphedAddr; - - /* Bind to the channel loopback address. */ - - rc = kernel_bind(SkFromPvsk(pvsk)->sk_socket, - (struct sockaddr *)&sin, sizeof sin); - if (rc) { - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_CHANNEL); - PvtcpStateAddSocket(pvsk->channel, pvtcpIfUnbound, pvsk); - } else { - /* - * Bind succeeded on pvsock address. - * If this is a pvsock UDP reserved port, record it. - */ - - port = ntohs(port) - portRangeBase; - if ((SkFromPvsk(pvsk)->sk_socket->type == SOCK_DGRAM) && - (port < portRangeSize)) { - CommOS_MutexLock(&globalLock); - PvtcpSetPortIndexBit(pvsk->netif->conf.addr.in.s_addr, port); - CommOS_MutexUnlock(&globalLock); - } - - /* - * pvsock data usage shouldn't be counted as MVP external traffic. - */ - SkFromPvsk(pvsk)->sk_socket->file = NULL; - } - -out: - if (propagate) { - *addr = morphedAddr; - } - return rc; -} - - -/** - * @brief Tests if the passed address is IPV4-mapped 127.238.0.1 or 127.0.0.1, - * clean ::1, and whether the socket has a namespace. - * If needed, the address will be morphed into the actual loopback address, - * then a bind() is performed. - * Note that the function returns EADDRNOTAVAIL for any other loopbacks. - * @param pvsk socket to test. - * @param[in,out] addr0 first 64 bits of inet6 address to test. - * @param[in,out] addr1 last 64 bits of inet6 address to test. - * @param port port to bind, or zero for any port. - * @return 1 if bind should be performed by caller, bind return code otherwise. - */ - -int -PvtcpTestAndBindLoopbackInet6(PvtcpSock *pvsk, - unsigned long long *addr0, - unsigned long long *addr1, - unsigned short port) -{ - int rc; - struct sockaddr_in6 sin6; - union { - unsigned long long halves[2]; - struct in6_addr in6; - } in6Addr = { - .halves = { *addr0, *addr1 } - }; - int propagate = 0; - const int ipv6Only = 0; - - if (ipv6_addr_loopback(&in6Addr.in6)) { - if (PvtcpHasSockNamespace(pvsk)) { - return 1; - } - - /* Remember that we were passed '::1'. */ - - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_IPV6_LOOP, 1); - ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK), &in6Addr.in6); - } - - if (!ipv6_addr_v4mapped(&in6Addr.in6)) { - /* If the address is not ipv4-mapped, stop testing. */ - - return 1; - } - - rc = TestLoopbackInet4(pvsk, in6Addr.in6.s6_addr32[3]); - switch (rc) { - case 2: - propagate = 1; // Fall through. - case 1: - break; // Proceed with morphing. - case 0: - return 1; // Don't morph, let bind() be done by caller. - default: - return rc; - } - - if (pvsk->netif->conf.family == PVTCP_PF_LOOPBACK_INET4) { - /* The socket has already been morphed/bound. */ - - ipv6_addr_set_v4mapped(pvsk->netif->conf.addr.in.s_addr, &in6Addr.in6); - rc = 0; - goto out; - } - - /* - * Move the socket to the initial namespace before binding it - * such that the loopback address is accessible to the host. - */ - - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_INITIAL); - PvtcpStateAddSocket(pvsk->channel, pvtcpIfLoopbackInet4, pvsk); - ipv6_addr_set_v4mapped(pvsk->netif->conf.addr.in.s_addr, &in6Addr.in6); - memset(&sin6, 0, sizeof sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = port; - sin6.sin6_addr = in6Addr.in6; - - /* - * Ensure we can use ipv4 mapped addresses and bind to the channel - * loopback address. - */ - - (void)kernel_setsockopt(SkFromPvsk(pvsk)->sk_socket, IPPROTO_IPV6, - IPV6_V6ONLY, (char *)&ipv6Only, sizeof ipv6Only); - rc = kernel_bind(SkFromPvsk(pvsk)->sk_socket, - (struct sockaddr *)&sin6, sizeof sin6); - if (rc) { - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_CHANNEL); - PvtcpStateAddSocket(pvsk->channel, pvtcpIfUnbound, pvsk); - } else { - /* - * Bind succeeded on pvsock address. - * If this is a pvsock UDP reserved port, record it. - */ - - port = ntohs(port) - portRangeBase; - if ((SkFromPvsk(pvsk)->sk_socket->type == SOCK_DGRAM) && - (port < portRangeSize)) { - CommOS_MutexLock(&globalLock); - PvtcpSetPortIndexBit(pvsk->netif->conf.addr.in.s_addr, port); - CommOS_MutexUnlock(&globalLock); - } - - /* - * pvsock data usage shouldn't be counted as MVP external traffic. - */ - SkFromPvsk(pvsk)->sk_socket->file = NULL; - } - -out: - if (propagate) { - *addr0 = in6Addr.halves[0]; - *addr1 = in6Addr.halves[1]; - } - return rc; -} - - -/** - * @brief Resets a 127.238.0.N address to 127.0.0.1. - * @param pvsk socket whose address needs resetting. - * @param[in,out] addr inet4 address to reset. - */ - -void -PvtcpResetLoopbackInet4(PvtcpSock *pvsk, - unsigned int *addr) -{ - if (!PvtcpHasSockNamespace(pvsk)) { - static const unsigned int pvsockAddr = htonl(PVTCP_PVSOCK_ADDR); - - if (!memcmp(&pvsockAddr, addr, 3) && memcmp(&pvsockAddr, addr, 4)) { - /* If it's a pvsock address but _not_ the host's, overwrite it. */ - - *addr = htonl(INADDR_LOOPBACK); - } - } -} - - -/** - * @brief Resets an IPV4-mapped ::ffff:127.238.0.N IPV6 address to loopback. - * @param pvsk socket whose address needs resetting. - * @param[in,out] in6 inet6 address to reset. - */ - -void -PvtcpResetLoopbackInet6(PvtcpSock *pvsk, - struct in6_addr *in6) -{ - if (!PvtcpHasSockNamespace(pvsk) && ipv6_addr_v4mapped(in6)) { - if (PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_IPV6_LOOP)) { - /* If the original address came in as ::1, we reset as such. */ - - static const struct in6_addr in6Loopback = IN6ADDR_LOOPBACK_INIT; - - *in6 = in6Loopback; - } else { - PvtcpResetLoopbackInet4(pvsk, &in6->s6_addr32[3]); - } - } -} - - -/** - * @brief Called at module load time. It registers with the Comm runtime. - * @param args initialization arguments - * @return zero if successful, -1 otherwise - * @sideeffect Leaves the module loaded - */ - -static int -Init(void *args) -{ - int rc = -1; - -#if !defined(PVTCP_DISABLE_NETFILTER) - rc = nf_register_hooks(netfilterHooks, ARRAY_SIZE(netfilterHooks)); - if (rc) { - CommOS_Log(("%s: Could not register netfilter hooks!\n", __FUNCTION__)); - goto out; - } else { - CommOS_Debug(("%s: Registered netfilter hooks.\n", __FUNCTION__)); - } - hooksRegistered = 1; -#else - CommOS_Log(("%s: Netfilter hooks disabled.\n", __FUNCTION__)); -#endif - - CommOS_MutexInit(&globalLock); - CommOS_WriteAtomic(&PvtcpOutputAIOSection, 0); - PvtcpOffLargeDgramBufInit(); - - pvtcpImpl.owner = CommOS_ModuleSelf(); - pvtcpImpl.stateCtor = StateAlloc; - pvtcpImpl.stateDtor = StateFree; - if (CommSvc_RegisterImpl(&pvtcpImpl) == 0) { - rc = 0; - pvtcpLoopbackOffAddr = GetLoopbackAddr(); - if (pvtcpLoopbackOffAddr == -1U) { - CommOS_Log(("%s: Could not allocate offload loopback address!\n", - __FUNCTION__)); - rc = -1; - CommSvc_UnregisterImpl(&pvtcpImpl); - } - } - -out: - if (rc) { - if (hooksRegistered) { - nf_unregister_hooks(netfilterHooks, ARRAY_SIZE(netfilterHooks)); - } - } - return rc; -} - - -/** - * @brief Called at module unload time. It shuts down pvtcp. - * @sideeffect Total and utter destruction. - */ - -static void -Exit(void) -{ - PutLoopbackAddr(pvtcpLoopbackOffAddr); - CommSvc_UnregisterImpl(&pvtcpImpl); -#if !defined(PVTCP_DISABLE_NETFILTER) - if (hooksRegistered) { - nf_unregister_hooks(netfilterHooks, ARRAY_SIZE(netfilterHooks)); - CommOS_Debug(("%s: Netfilter hooks unregistered.\n", __FUNCTION__)); - } -#endif - CommOS_Log(("%s: Allocations of large datagrams: %llu.\n", - __FUNCTION__, pvtcpOffDgramAllocations)); -} - - -/* - * Socket callback interceptors. - */ - -/** - * @brief Callback called when socket is destroyed. - * @param[in,out] sk socket to cleanup - * @return 0 if socket memory is freed, < 0 otherwise (no-op) - * @sideeffect Send queue buffers are deallocated - */ - -int -DestructCB(struct sock *sk) -{ - PvtcpOffBuf *internalBuf; - PvtcpOffBuf *tmp; - PvtcpSock *pvsk = PvskFromSk(sk); - - if (!pvsk || - (SkFromPvsk(pvsk) != sk) || - (pvsk->destruct == asmDestructorShim)) { - /* Module put _not_ to be performed by asmDestructorShim. */ - - CommOS_Debug(("%s: pvsk / sk inconsistency. Ignored.\n", __FUNCTION__)); - return -1; - } - - CommOS_ListForEachSafe(&pvsk->queue, internalBuf, tmp, link) { - CommOS_ListDel(&internalBuf->link); - PvtcpBufFree(PvtcpOffBufFromInternal(internalBuf)); - } - if (pvsk->destruct) { - pvsk->destruct(sk); - } - - if (pvsk->rpcReply) { - CommOS_Kfree(pvsk->rpcReply); - } - CommOS_Kfree(pvsk); - - /* - * Module put is performed by asmDestructorShim. - */ - - return 0; -} - - -/** - * @brief Callback called when socket state changes occur. - * @param sk socket specified socket which changed state - * @sideeffect A writer task may be scheduled - */ - -static void -StateChangeCB(struct sock *sk) -{ - PvtcpSock *pvsk = PvskFromSk(sk); - - if (!pvsk || - (SkFromPvsk(pvsk) != sk) || - (pvsk->stateChange == StateChangeCB)) { - CommOS_Debug(("%s: pvsk / sk inconsistency. Ignored.\n", __FUNCTION__)); - return; - } - - /* - * The socket (spin) lock is held when this function is called. - */ - - CommOS_Debug(("%s: [0x%p] sk_state [%u] sk_err [%d] sk_err_soft [%d].\n", - __FUNCTION__, pvsk, sk->sk_state, - sk->sk_err, sk->sk_err_soft)); - if (pvsk->stateChange) { - pvsk->stateChange(sk); - } - if (sk->sk_state == TCP_ESTABLISHED) { - PvskSetOpFlag(pvsk, PVTCP_OP_CONNECT); - } - PvtcpSchedSock(pvsk); -} - - -/** - * @brief Callback called when an error is set on the socket. - * @param sk socket the error happened on - * @sideeffect A writer task may be scheduled - */ - -static void -ErrorReportCB(struct sock *sk) -{ - PvtcpSock *pvsk = PvskFromSk(sk); - - if (!pvsk || - (SkFromPvsk(pvsk) != sk) || - (pvsk->errorReport == ErrorReportCB)) { - CommOS_Debug(("%s: pvsk / sk inconsistency. Ignored\n", __FUNCTION__)); - return; - } - - /* - * The socket (spin) lock is held when this function is called. - * Interesting sk_err-s: - * ECONNRESET - tcp_disconnect(), tcp_reset() - * ECONNREFUSED - tcp_reset() - * EPIPE - tcp_reset() - * ETIMEDOUT - tcp_write_error() - * EHOSTUNREACH, etc. - tcp_v4_error()??, icmp errors - * etc. - __udp4_lib_err(), icmp errors - */ - - CommOS_Debug(("%s: [0x%p] sk_err [%d] sk_err_soft [%d].\n", - __FUNCTION__, pvsk, sk->sk_err, sk->sk_err_soft)); - if (pvsk->errorReport) { - pvsk->errorReport(sk); - } - pvsk->err = sk->sk_err; - PvtcpSchedSock(pvsk); -} - - -/** - * @brief Callback called when data is available to be read from a socket. - * @param sk socket in question - * @param bytes number of bytes to read - * @sideeffect A writer task is scheduled _iff_ the peer can safely - * receive. - */ - -static void -DataReadyCB(struct sock *sk, - int bytes) -{ - PvtcpSock *pvsk = PvskFromSk(sk); - - if (!pvsk || - (SkFromPvsk(pvsk) != sk) || - (pvsk->dataReady == DataReadyCB)) { - CommOS_Debug(("%s: pvsk / sk inconsistency. Ignored.\n", __FUNCTION__)); - return; - } - - /* - * The socket (spin) lock is held when this function is called. - */ - - if (pvsk->dataReady) { - pvsk->dataReady(sk, bytes); - } - if (sk->sk_state == TCP_LISTEN) { - CommOS_Debug(("%s: Listen socket ready to accept [0x%p].\n", - __FUNCTION__, pvsk)); - } - PvtcpSchedSock(pvsk); -} - - -/** - * @brief Callback called when writing is possible on a socket. - * @param sk socket in question - * @sideeffect An AIO thread is scheduled. - */ - -static void -WriteSpaceCB(struct sock *sk) -{ - PvtcpSock *pvsk = PvskFromSk(sk); - - if (!pvsk || - (SkFromPvsk(pvsk) != sk) || - (pvsk->writeSpace == WriteSpaceCB)) { - CommOS_Debug(("%s: pvsk / sk inconsistency. Ignored.\n", __FUNCTION__)); - return; - } - - /* - * The socket (spin) lock is held when this function is called. - */ - - if (pvsk->writeSpace) { - pvsk->writeSpace(sk); - } - PvtcpSchedSock(pvsk); -} - - -/** - * @brief Initializes a newly created socket for offload operations. - * @param[in,out] sock socket to initialize - * @param channel channel to update - * @param peerSock peer PV socket of this socket - * @param parentPvsk parent of this socket or NULL - * @return zero on success, error code otherwise - */ - -static int -SockAllocInit(struct socket *sock, - CommChannel channel, - unsigned long long peerSock, - PvtcpSock *parentPvsk) -{ - struct sock *sk; - PvtcpSock *pvsk; - int sndBuf = PVTCP_SOCK_RCVSIZE * 4; - - if (!sock || !channel || !peerSock) { - return -EINVAL; - } - - sk = sock->sk; - sk->sk_user_data = NULL; - - pvsk = CommOS_Kmalloc(sizeof *pvsk); - if (!pvsk) { - return -ENOMEM; - } - - if (PvtcpOffSockInit(pvsk, channel)) { - CommOS_Kfree(pvsk); - return -ENOMEM; - } - - /* - * PVTCP sockets should be billed against the vmware uid. - */ - sk->sk_socket->file = &_file; - - /* Set peer (pv) socket. */ - pvsk->peerSock = peerSock; - pvsk->peerSockSet = 1; - - /* Set up back pointer. */ - pvsk->sk = sk; - - /* Keep track of new socket. */ - if (PvtcpStateAddSocket(channel, pvtcpIfUnbound, pvsk) != 0) { - CommOS_Kfree(pvsk); - return -ENOMEM; - } - - /* - * Keep pvtcp around for at least the lifetime of this socket - */ - CommOS_ModuleGet(pvtcpImpl.owner); - - if (!parentPvsk) { - pvsk->destruct = sk->sk_destruct; - sk->sk_destruct = asmDestructorShim; - pvsk->stateChange = sk->sk_state_change; - sk->sk_state_change = StateChangeCB; - pvsk->errorReport = sk->sk_error_report; - sk->sk_error_report = ErrorReportCB; - pvsk->dataReady = sk->sk_data_ready; - sk->sk_data_ready = DataReadyCB; - pvsk->writeSpace = sk->sk_write_space; - sk->sk_write_space = WriteSpaceCB; - } else { - /* - * Copy the parent's saved callbacks. The parent pvsk is only passed - * when creating/initializing a socket after an 'accept'. - */ - - pvsk->destruct = parentPvsk->destruct; - sk->sk_destruct = asmDestructorShim; - pvsk->stateChange = parentPvsk->stateChange; - sk->sk_state_change = StateChangeCB; - pvsk->errorReport = parentPvsk->errorReport; - sk->sk_error_report = ErrorReportCB; - pvsk->dataReady = parentPvsk->dataReady; - sk->sk_data_ready = DataReadyCB; - pvsk->writeSpace = parentPvsk->writeSpace; - sk->sk_write_space = WriteSpaceCB; - - if (parentPvsk->netif->conf.family == PVTCP_PF_LOOPBACK_INET4) { - /* The parent socket was morphed/bound. */ - - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_INITIAL); - PvtcpStateAddSocket(pvsk->channel, pvtcpIfLoopbackInet4, pvsk); - } - } - - /* Install forward socket reference. */ - sk->sk_user_data = pvsk; - - /* - * Force the send buffer size high enough, such that we don't lose the - * just-a-bit-over-the-limit bytes. This is mainly needed for datagrams. - * Note that we always apply flow control between host and guest modules, - * according to the sizing model; so this is not artificially inflated. - */ - - kernel_setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, - (void *)&sndBuf, sizeof sndBuf); - - return 0; -} - - -/** - * @brief Allocates a pvsk socket for error reporting (create operation). - * @param err error code to report to PV side - * @param channel channel error socket belongs to - * @param peerSock peer PV socket of this socket - * @return error socket on success, NULL otherwise - */ - -static PvtcpSock * -SockAllocErrInit(int err, - CommChannel channel, - unsigned long long peerSock) -{ - PvtcpSock *pvsk; - - if (!channel || !peerSock) { - return NULL; - } - - pvsk = CommOS_Kmalloc(sizeof *pvsk); - if (!pvsk) { - return NULL; - } - - if (PvtcpOffSockInit(pvsk, channel)) { - CommOS_Kfree(pvsk); - return NULL; - } - - /* Set peer (pv) socket and error. */ - pvsk->peerSock = peerSock; - pvsk->peerSockSet = 1; - pvsk->err = err; - - /* Set up back pointer to NULL such that PvtcpPutSock deallocates it. */ - pvsk->sk = NULL; - return pvsk; -} - - -/* - * Offload operations. - */ - -/** - * @brief Creates an offload socket and schedules it for reply. - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back. - */ - -void -PvtcpCreateOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - int rc; - struct socket *sock; - PvtcpSock *pvsk; - PvtcpState *state = (PvtcpState *)upperLayerState; - const int enable = 1; - - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - -#if defined(PVTCP_IPV6_DISABLE) - if (packet->data16 == AF_INET6) { - CommOS_Debug(("%s: AF_INET6 support is disabled.\n", __FUNCTION__)); - rc = -EAFNOSUPPORT; - } else -#endif - { - rc = sock_create_kern(packet->data16, packet->data32, - packet->data32ex, &sock); - } - - if (!rc) { - rc = SockAllocInit(sock, channel, packet->data64, NULL); - if (rc) { - SockReleaseWrapper(sock); - goto fail; - } - kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (void *)&enable, sizeof enable); - pvsk = PvskFromSk(sock->sk); - if (state->extra && - ((PvtcpStateKObj *)(state->extra))->useNS) { - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_CHANNEL); - } else { - PvtcpSwitchSock(pvsk, PVTCP_SOCK_NAMESPACE_INITIAL); - } - PvtcpStateAddSocket(pvsk->channel, pvtcpIfUnbound, pvsk); - PvskSetOpFlag(pvsk, PVTCP_OP_CREATE); - } else { - CommOS_Debug(("%s: Error creating offload socket: %d\n", - __FUNCTION__, rc)); - /* - * Pass -rc so we follow error conventions for other reply ops. - * The error code is fixed by the PV side so error codes are properly - * reported. - */ - pvsk = SockAllocErrInit(-rc, channel, packet->data64); - if (!pvsk) { - goto fail; - } - } - - PvtcpSchedSock(pvsk); - return; - -fail: - CommOS_Log(("%s: BOOG ** FAILED TO CREATE OFFLOAD SOCKET [%d] " - "_AND_ ERROR REPORTING SOCKET!\n" - " PV SIDE MAY BE LOCKED UP UNTIL CREATE RPC TIMES OUT!", - __FUNCTION__, rc)); -} - - -/** - * @brief Schedules an offload socket to be removed. - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back and - * then release the socket. - */ - -void -PvtcpReleaseOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - - /* - * Check if this is a pvsock datagram socket bound on a reserved port. - * If so, reset the bit such that filtering drops rogue packets. - */ - - if ((sk->sk_socket->type == SOCK_DGRAM) && - (pvsk->netif->conf.family == PVTCP_PF_LOOPBACK_INET4)) { - unsigned short port = 0; - - if (sk->sk_family == AF_INET) { - struct sockaddr_in sin = { .sin_family = AF_INET }; - int addrLen = sizeof sin; - - if(!kernel_getsockname(sk->sk_socket, - (struct sockaddr *)&sin, &addrLen)) { - port = sin.sin_port; - } - } else { /* AF_INET6 */ - struct sockaddr_in6 sin = { .sin6_family = AF_INET6 }; - int addrLen = sizeof sin; - - if(!kernel_getsockname(sk->sk_socket, - (struct sockaddr *)&sin, &addrLen)) { - port = sin.sin6_port; - } - } - - port = ntohs(port) - portRangeBase; - if (port < portRangeSize) { - CommOS_MutexLock(&globalLock); - PvtcpResetPortIndexBit(pvsk->netif->conf.addr.in.s_addr, port); - CommOS_MutexUnlock(&globalLock); - } - } - - /* - * - hold the socket before setting the 'release' flag and until after - * the call to PvtcpSchedSock(): if the socket had already been scheduled - * ReleaseAIO may run, find the flag set and release this socket while - * it's being unlocked here. - * - * - hold the dispatch lock until done to ensure that subsequent Ops for - * this socket see peerSockSet == 0. - */ - - PvtcpHoldSock(pvsk); - SOCK_STATE_LOCK(pvsk); - pvsk->peerSockSet = 0; - SOCK_STATE_UNLOCK(pvsk); - PvskSetOpFlag(pvsk, PVTCP_OP_RELEASE); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); - PVTCP_UNLOCK_DISP_DISCARD_VEC(); -} - - -/** - * @brief Binds an offload socket to a given address - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back - */ - -void -PvtcpBindOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - struct sockaddr *addr; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - int reuseAddr; - int addrLen; - int rc; - - PvtcpHoldSock(pvsk); - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - - /* - * The socket-level option SO_REUSEADDR is set in the common socket code, - * meaning that we cannot intercept it in the guest pvtcp implementation. - * In order to respect the setting, the guest would pass the current - * setting in 'bind' requests. - * If the guest requires 'reuse address' setting, the value is incremented - * such that we differentiate between: 0) not requested, 1) 'false' and - * 2) 'true'. - */ - - reuseAddr = COMM_OPF_GET_VAL(packet->flags); - if ((reuseAddr == 1) || (reuseAddr == 2)) { - /* Explicit request, so decrement the value. */ - - reuseAddr--; - kernel_setsockopt(sk->sk_socket, SOL_SOCKET, SO_REUSEADDR, - (void *)&reuseAddr, sizeof reuseAddr); - } - - if (sk->sk_family == AF_INET) { - memset(&sin, 0, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_port = packet->data16; - sin.sin_addr.s_addr = (unsigned int)packet->data64ex; - addr = (struct sockaddr *)&sin; - addrLen = sizeof sin; - - rc = PvtcpTestAndBindLoopbackInet4(pvsk, &sin.sin_addr.s_addr, - sin.sin_port); - if (rc <= 0) { - /* Bind has already happened. */ - - pvsk->err = -rc; - goto out; - } - } else { /* AF_INET6 */ - memset(&sin6, 0, sizeof sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = packet->data16; - addr = (struct sockaddr *)&sin6; - addrLen = sizeof sin6; - - rc = PvtcpTestAndBindLoopbackInet6(pvsk, &packet->data64ex, - &packet->data64ex2, sin6.sin6_port); - if (rc <= 0) { - /* Bind has already happened. */ - - pvsk->err = -rc; - goto out; - } - PvtcpI6AddrUnpack(&sin6.sin6_addr.s6_addr32[0], - packet->data64ex, packet->data64ex2); - } - - /* coverity[check_return] */ - pvsk->err = -kernel_bind(sk->sk_socket, addr, addrLen); - -out: - PvskSetOpFlag(pvsk, PVTCP_OP_BIND); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Sets a socket option. - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back - */ -void -PvtcpSetSockOptOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - unsigned int optlen = packet->len - sizeof *packet; - - PvtcpHoldSock(pvsk); - - if ((vecLen != 1) || (vec[0].iov_len != optlen) || (optlen < sizeof(int))) { - pvsk->rpcStatus = -EINVAL; - goto out; - } - - if (packet->data32 == SOL_TCP) { - /* - * The back-end implementation must always run in 'nodelay' mode. - * Consequently, we ignore, but we cache the TCP_NODELAY and TCP_CORK - * settings such that getsockopt() can return them as they were 'set'. - * Applications use these settings for performance; pvtcp does quite - * well if it's not interfered with. - */ - - int on; - - switch (packet->data32ex) { - case TCP_NODELAY: - memcpy(&on, vec[0].iov_base, sizeof on); - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_TCP_NODELAY, on); - pvsk->rpcStatus = 0; - goto out; - case TCP_CORK: - memcpy(&on, vec[0].iov_base, sizeof on); - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_TCP_CORK, on); - pvsk->rpcStatus = 0; - goto out; - } - } - - pvsk->rpcStatus = kernel_setsockopt(sock, - packet->data32, - packet->data32ex, - vec[0].iov_base, - optlen); - -out: - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - PvskSetOpFlag(pvsk, PVTCP_OP_SETSOCKOPT); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Retrieves a socket option. - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back - */ -void -PvtcpGetSockOptOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - unsigned int optLen = (unsigned int)(packet->data64ex); - char *optBuf; - int rc = 0; - - PvtcpHoldSock(pvsk); - - if ((optLen < sizeof(int)) || (optLen > PVTCP_SOCK_SAFE_RCVSIZE)) { - pvsk->rpcStatus = -EINVAL; - goto out; - } - - optBuf = CommOS_Kmalloc(optLen); - if (!optBuf) { - pvsk->rpcStatus = -EINVAL; - goto out; - } - - if (packet->data32 == SOL_TCP) { - /* - * See comment in PvtcpSetSockOptOp() regarding special treatment for - * the TCP_NODELAY and TCP_CORK settings. - */ - - int on; - - switch (packet->data32ex) { - case TCP_NODELAY: - on = PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_TCP_NODELAY); - optLen = sizeof on; - memcpy(optBuf, &on, optLen); - goto done; - case TCP_CORK: - on = PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_TCP_CORK); - optLen = sizeof on; - memcpy(optBuf, &on, optLen); - goto done; - } - } - - rc = kernel_getsockopt(sock, packet->data32, - packet->data32ex, optBuf, &optLen); - -done: - if (!rc) { - pvsk->rpcReply = optBuf; - CommOS_MemBarrier(); - pvsk->rpcStatus = (int)optLen; - } else { - CommOS_Kfree(optBuf); - pvsk->rpcStatus = rc; - } - -out: - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - PvskSetOpFlag(pvsk, PVTCP_OP_GETSOCKOPT); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Performs ioctl on offload socket. - * @param channel communication channel with offloader - * @param state state associated with this channel - * @param packet packet header received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - */ - -void -PvtcpIoctlOp(CommChannel channel, - void *state, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, state); - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - - PvtcpHoldSock(pvsk); - - /* Not implemented yet. */ - - (void)sock; - pvsk->rpcStatus = -ENOIOCTLCMD; - - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - PvskSetOpFlag(pvsk, PVTCP_OP_IOCTL); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Marks a socket for listening to incoming connections - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back - */ - -void -PvtcpListenOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - int backlog = (int)packet->data32; - - PvtcpHoldSock(pvsk); - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - - pvsk->err = -kernel_listen(sk->sk_socket, backlog); - PvskSetOpFlag(pvsk, PVTCP_OP_LISTEN); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Accepts a connected socket - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back. - */ - -void -PvtcpAcceptOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - int rc; - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - struct socket *newsock = NULL; - - PvtcpHoldSock(pvsk); - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - - rc = kernel_accept(sk->sk_socket, &newsock, O_NONBLOCK); - if (rc == 0) { - rc = SockAllocInit(newsock, channel, packet->data64ex, pvsk); - if (rc) { - SockReleaseWrapper(newsock); - } - } - - if (rc == 0) { - struct sock *newsk = newsock->sk; - PvtcpSock *newpvsk = PvskFromSk(newsk); - - /* We temporarily use the state field to cache parent socket. */ - - newpvsk->state = (PvtcpState *)pvsk; - PvskSetOpFlag(newpvsk, PVTCP_OP_ACCEPT); - PvtcpSchedSock(newpvsk); - } else { - pvsk->err = -rc; - PvskSetOpFlag(pvsk, PVTCP_OP_ACCEPT); - PvtcpSchedSock(pvsk); - } - - PvtcpPutSock(pvsk); -} - - -/** - * @brief Connects an offload socket to given address - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect A writer task is scheduled, which will send reply back - */ - -void -PvtcpConnectOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - struct sock *sk = SkFromPvsk(pvsk); - struct sockaddr *addr; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - int addrLen; - int flags = 0; - int rc = 0; - int disconnect = 0; - - PvtcpHoldSock(pvsk); - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - - if (sk->sk_family == AF_INET) { - addr = (struct sockaddr *)&sin; - addrLen = sizeof sin; - memset(&sin, 0, sizeof sin); - sin.sin_port = packet->data16; - sin.sin_addr.s_addr = (unsigned int)packet->data64ex; - if (COMM_OPF_GET_VAL(packet->flags)) { - sin.sin_family = AF_UNSPEC; - disconnect = 1; - goto connect; - } - sin.sin_family = AF_INET; - PvtcpTestAndBindLoopbackInet4(pvsk, &sin.sin_addr.s_addr, 0); - } else { /* AF_INET6 */ - addr = (struct sockaddr *)&sin6; - addrLen = sizeof sin6; - memset(&sin6, 0, sizeof sin6); - sin6.sin6_port = packet->data16; - if (COMM_OPF_GET_VAL(packet->flags)) { - sin6.sin6_family = AF_UNSPEC; - PvtcpI6AddrUnpack(&sin6.sin6_addr.s6_addr32[0], - packet->data64ex, packet->data64ex2); - disconnect = 1; - goto connect; - } - sin6.sin6_family = AF_INET6; - PvtcpTestAndBindLoopbackInet6(pvsk, &packet->data64ex, - &packet->data64ex2, 0); - PvtcpI6AddrUnpack(&sin6.sin6_addr.s6_addr32[0], - packet->data64ex, packet->data64ex2); - } - -connect: - rc = kernel_connect(sk->sk_socket, addr, addrLen, flags | O_NONBLOCK); - - /* - * For datagram sockets, ErrorReportCB is not called, so we need to - * explicitly set the pvsk error to be returned back to the guest. - * This should not be used on SOCK_STREAM sockets. You have been - * warned. - */ - - if (rc && (sk->sk_socket->type == SOCK_DGRAM)) { - pvsk->err = -rc; - } - - /* - * Quite likely, stream actual connect requests will set err to EINPROGRESS. - * That's fine, error_report will trigger an AIO/flow-op reply. When the - * connection is established, state_change schedules an AIO/connect reply. - * Record whether the request was a disconnect. - */ - - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_DISCONNECT, disconnect); - PvskSetOpFlag(pvsk, PVTCP_OP_CONNECT); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/** - * @brief Initiates socket shutdown on an offload socket - * @param channel communication channel with offloader - * @param upperLayerState state associated with this channel - * @param packet first packet received in reply - * @param vec payload buffer descriptors - * @param vecLen payload buffer descriptor count - * @sideeffect Socket queue will be drained and socket shutdown performed. - */ - -void -PvtcpShutdownOp(CommChannel channel, - void *upperLayerState, - CommPacket *packet, - struct kvec *vec, - unsigned int vecLen) -{ - PvtcpSock *pvsk = PvtcpGetPvskOrReturn(packet->data64, upperLayerState); - int how = (int)packet->data32; - - PvtcpHoldSock(pvsk); - if ((how == SHUT_RD) || (how == SHUT_RDWR)) { - kernel_sock_shutdown(SkFromPvsk(pvsk)->sk_socket, SHUT_RD); - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_RD, 1); - } - if ((how == SHUT_WR) || (how == SHUT_RDWR)) { - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_SHUT_WR, 1); - } - PVTCP_UNLOCK_DISP_DISCARD_VEC(); - PvtcpSchedSock(pvsk); - PvtcpPutSock(pvsk); -} - - -/* - * AIO functions called from the main AIO processing function. - * Most of these functions complete processing initiated by the corresponding - * offload operations above. - */ - -/** - * @brief Processes socket release in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket to release. - * @sideeffect the socket will be released upon return from this function. - */ - -static inline void -ReleaseAIO(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - CommPacket packet = { - .len = sizeof packet, - .flags = 0, - .opCode = PVTCP_OP_RELEASE, - .data64 = pvsk->peerSock, - .data64ex = PvtcpGetHandle(pvsk) - }; - unsigned long long timeout = COMM_MAX_TO; - - SOCK_OUT_LOCK(pvsk); - CommSvc_Write(pvsk->channel, &packet, &timeout); -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sent 'Release' [0x%p] -> 0x%0x] reply.\n", - __FUNCTION__, pvsk, (unsigned)(pvsk->peerSock))); -#endif - /* - * 'sk' goes away in the final ProcessAIO::sock_put() - */ - SockReleaseWrapper(sock); - SOCK_OUT_UNLOCK(pvsk); - - PvtcpStateRemoveSocket(pvsk->channel, pvsk); -} - - -/** - * @brief Processes socket create reply in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk newly created socket to send ack for. - */ - -static inline void -CreateAIO(PvtcpSock *pvsk) -{ - struct sock *sk; - struct socket *sock; - CommPacket packet = { - .len = sizeof packet, - .flags = 0, - .opCode = PVTCP_OP_CREATE, - .data64 = pvsk->peerSock, - }; - unsigned long long timeout = COMM_MAX_TO; - int rc; - - sk = SkFromPvsk(pvsk); - if (!sk) { - /* - * This is a create-error socket. The error reply has been sent out - * already, by PvtcpFlowAIO(). This is a paranoid safety measure, as - * PVTCP_OP_CREATE OpFlag should not have been set. - */ - - return; - } - - sock = sk->sk_socket; - packet.data64ex = PvtcpGetHandle(pvsk); - - rc = CommSvc_Write(pvsk->channel, &packet, &timeout); - if (rc != packet.len) { - /* We mustn't leak it if PV can't get a hold of it. */ - - PvtcpStateRemoveSocket(pvsk->channel, pvsk); - SockReleaseWrapper(sock); - CommOS_Log(("%s: BOOG -- Couldn't send 'Create' reply [0x%p]!\n", - __FUNCTION__, sk)); - } else { -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sent 'Create' [0x%p] reply [%d].\n", - __FUNCTION__, pvsk, rc)); -#endif - } -} - - -/** - * @brief Processes socket bind in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket being bound. - */ - -static inline void -BindAIO(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - CommPacket packet = { - .len = sizeof packet, - .flags = 0, - .opCode = PVTCP_OP_BIND, - .data64 = pvsk->peerSock - }; - unsigned long long timeout = COMM_MAX_TO; - int rc; - - if (pvsk->peerSockSet) { - if (sk->sk_family == AF_INET) { - struct sockaddr_in sin = { .sin_family = AF_INET }; - int addrLen = sizeof sin; - - rc = kernel_getsockname(sock, (struct sockaddr *)&sin, &addrLen); - if (rc == 0) { - packet.data16 = sin.sin_port; - PvtcpResetLoopbackInet4(pvsk, &sin.sin_addr.s_addr); - packet.data64ex = (unsigned long long)sin.sin_addr.s_addr; - } - } else { /* AF_INET6 */ - struct sockaddr_in6 sin = { .sin6_family = AF_INET6 }; - int addrLen = sizeof sin; - - rc = kernel_getsockname(sock, (struct sockaddr *)&sin, &addrLen); - if (rc == 0) { - packet.data16 = sin.sin6_port; - PvtcpResetLoopbackInet6(pvsk, &sin.sin6_addr); - PvtcpI6AddrPack(&sin.sin6_addr.s6_addr32[0], - &packet.data64ex, &packet.data64ex2); - } - } - - if (rc) { - COMM_OPF_SET_ERR(packet.flags); - packet.data32ex = (unsigned int)(-rc); - packet.opCode = PVTCP_OP_FLOW; - } - CommSvc_Write(pvsk->channel, &packet, &timeout); -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sent 'Bind' [0x%p, %d] reply.\n", - __FUNCTION__, pvsk, rc)); -#endif - } -} - - -/** - * @brief Sends result of setsockopt back to guest. - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket that was modified. - */ - -static inline void -SetSockOptAIO(PvtcpSock *pvsk) -{ - CommPacket packet; - unsigned long long timeout; - - packet.len = sizeof packet; - packet.flags = 0; - packet.opCode = PVTCP_OP_SETSOCKOPT; - packet.data64 = pvsk->peerSock; - packet.data32 = (unsigned int)(pvsk->rpcStatus); - timeout = COMM_MAX_TO; - CommSvc_Write(pvsk->channel, &packet, &timeout); - pvsk->rpcStatus = 0; -} - - -/** - * @brief Sends result of getsockopt back to guest. - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket that was modified. - */ - -static inline void -GetSockOptAIO(PvtcpSock *pvsk) -{ - CommPacket packet = { - .opCode = PVTCP_OP_GETSOCKOPT, - .flags = 0 - }; - unsigned long long timeout = COMM_MAX_TO; - - struct kvec vec[1]; - struct kvec *inVec = vec; - unsigned int vecLen = 1; - unsigned int iovOffset = 0; - - if (pvsk->rpcStatus > 0) { - packet.len = sizeof packet + pvsk->rpcStatus; - vec[0].iov_base = pvsk->rpcReply; - vec[0].iov_len = pvsk->rpcStatus; - } else { - vecLen = 0; - } - - packet.data64 = pvsk->peerSock; - packet.data32 = pvsk->rpcStatus; - - CommSvc_WriteVec(pvsk->channel, &packet, &inVec, &vecLen, - &timeout, &iovOffset); - - if (pvsk->rpcReply) { - CommOS_Kfree(pvsk->rpcReply); - pvsk->rpcReply = NULL; - } - pvsk->rpcStatus = 0; -} - - -/** - * @brief Sends result of ioctl back to guest. - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket that was modified. - */ - -static inline void -IoctlAIO(PvtcpSock *pvsk) -{ - CommPacket packet = { - .len = sizeof packet, - .opCode = PVTCP_OP_IOCTL, - .flags = 0 - }; - unsigned long long timeout = COMM_MAX_TO; - - packet.data64 = pvsk->peerSock; - packet.data32 = pvsk->rpcStatus; - CommSvc_Write(pvsk->channel, &packet, &timeout); - pvsk->rpcStatus = 0; -} - - -/** - * @brief Processes socket listen reply in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket being put in listen mode. - */ - -static inline void -ListenAIO(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - CommPacket packet = { - .len = sizeof packet, - .flags = 0, - .opCode = PVTCP_OP_LISTEN, - .data64 = pvsk->peerSock - }; - unsigned long long timeout = COMM_MAX_TO; - - if (pvsk->peerSockSet) { - if (sk->sk_state != TCP_LISTEN) { - COMM_OPF_SET_ERR(packet.flags); - packet.data32ex = (unsigned int)pvsk->err; - packet.opCode = PVTCP_OP_FLOW; - } - - CommSvc_Write(pvsk->channel, &packet, &timeout); -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sent 'Listen' [0x%p] reply.\n", __FUNCTION__, pvsk)); -#endif - } -} - - -/** - * @brief Processes socket accept reply in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk new socket or socket to accept on (see PvtcpAcceptOp). - */ - -static inline void -AcceptAIO(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - CommPacket packet = { - .len = sizeof packet, - .flags = 0, - .opCode = PVTCP_OP_ACCEPT - }; - unsigned long long timeout = COMM_MAX_TO; - const int enable = 1; - int rc; - - if (pvsk->peerSockSet) { - unsigned long long payloadSocks[2] = { 0, 0 }; - struct kvec payloadVec[] = { - { .iov_base = &payloadSocks, .iov_len = sizeof payloadSocks } - }; - struct kvec *payload = payloadVec; - unsigned int payloadLen = 1; - unsigned int iovOffset = 0; - - packet.len = sizeof packet + sizeof payloadSocks; - - /* - * accept() succeeded, so this is the child socket; its state field - * was temporarily changed to hold the parent/accepting socket. - * The newly accepted socket and its peer need to be put in a - * payload since we use up all available header fields with - * addressing information. Finally, the state field is restored. - */ - - packet.data64 = ((PvtcpSock *)pvsk->state)->peerSock; - pvsk->state = CommSvc_GetState(pvsk->channel); - - payloadSocks[0] = pvsk->peerSock; - payloadSocks[1] = PvtcpGetHandle(pvsk); - - rc = 0; - if (sk->sk_family == AF_INET) { - struct sockaddr_in sin = { .sin_family = AF_INET }; - int addrLen = sizeof sin; - - rc = kernel_getpeername(sock, (struct sockaddr *)&sin, &addrLen); - if (rc == 0) { - packet.data16 = sin.sin_port; - PvtcpResetLoopbackInet4(pvsk, &sin.sin_addr.s_addr); - packet.data64ex = (unsigned long long)sin.sin_addr.s_addr; - } - } else { /* AF_INET6 */ - struct sockaddr_in6 sin = { .sin6_family = AF_INET6 }; - int addrLen = sizeof sin; - - rc = kernel_getpeername(sock, (struct sockaddr *)&sin, &addrLen); - if (rc == 0) { - packet.data16 = sin.sin6_port; - PvtcpResetLoopbackInet6(pvsk, &sin.sin6_addr); - PvtcpI6AddrPack(&sin.sin6_addr.s6_addr32[0], - &packet.data64ex, &packet.data64ex2); - } - } - - if (rc == 0) { - kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY, - (void *)&enable, sizeof enable); - kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, - (void *)&enable, sizeof enable); - kernel_setsockopt(sock, SOL_SOCKET, SO_OOBINLINE, - (void *)&enable, sizeof enable); - } else { - PvtcpStateRemoveSocket(pvsk->channel, pvsk); - SockReleaseWrapper(sock); - COMM_OPF_SET_ERR(packet.flags); - packet.data32ex = (unsigned int)ECONNABORTED; - packet.len = sizeof packet; - packet.opCode = PVTCP_OP_FLOW; - } - - rc = CommSvc_WriteVec(pvsk->channel, &packet, - &payload, &payloadLen, &timeout, &iovOffset); - if ((rc != packet.len) && !COMM_OPF_TEST_ERR(packet.flags)) { - /* Mustn't leak the new socket if PV can't get a hold of it. */ - - PvtcpStateRemoveSocket(pvsk->channel, pvsk); - SockReleaseWrapper(sock); - } -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sent 'Accept' [0x%p] reply.\n", __FUNCTION__, pvsk)); -#endif - } -} - - -/** - * @brief Processes socket connect in an AIO thread. This function is - * called with the socket 'in' lock taken. - * @param[in,out] pvsk socket being connected. - */ - -static inline void -ConnectAIO(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - struct socket *sock = sk->sk_socket; - CommPacket packet = { - .len = sizeof packet, - .flags = 0, - .opCode = PVTCP_OP_CONNECT, - .data64 = pvsk->peerSock - }; - unsigned long long timeout = COMM_MAX_TO; - const int enable = 1; - int rc; - - if (!pvsk->peerSockSet || - (!PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_DISCONNECT) && - (sk->sk_state != TCP_ESTABLISHED))) { - return; - } - - if (PvskTestFlag(pvsk, PVTCP_OFF_PVSKF_DISCONNECT)) { - COMM_OPF_SET_VAL(packet.flags, 1); - PvskSetFlag(pvsk, PVTCP_OFF_PVSKF_DISCONNECT, 0); - } else if (sk->sk_state == TCP_ESTABLISHED) { - if (sk->sk_family == AF_INET) { - struct sockaddr_in sin = { .sin_family = AF_INET }; - int addrLen = sizeof sin; - - rc = kernel_getsockname(sock, (struct sockaddr *)&sin, &addrLen); - if (rc == 0) { - packet.data16 = sin.sin_port; - PvtcpResetLoopbackInet4(pvsk, &sin.sin_addr.s_addr); - packet.data64ex = (unsigned long long)sin.sin_addr.s_addr; - } - } else { /* AF_INET6 */ - struct sockaddr_in6 sin = { .sin6_family = AF_INET6 }; - int addrLen = sizeof sin; - - rc = kernel_getsockname(sock, (struct sockaddr *)&sin, &addrLen); - if (rc == 0) { - packet.data16 = sin.sin6_port; - PvtcpResetLoopbackInet6(pvsk, &sin.sin6_addr); - PvtcpI6AddrPack(&sin.sin6_addr.s6_addr32[0], - &packet.data64ex, &packet.data64ex2); - } - } - - if (rc == 0) { - kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY, - (void *)&enable, sizeof enable); - kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, - (void *)&enable, sizeof enable); - kernel_setsockopt(sock, SOL_SOCKET, SO_OOBINLINE, - (void *)&enable, sizeof enable); - } else { - COMM_OPF_SET_ERR(packet.flags); - packet.data32ex = ECONNABORTED; - packet.opCode = PVTCP_OP_FLOW; - } - } - - CommSvc_Write(pvsk->channel, &packet, &timeout); -#if defined(PVTCP_FULL_DEBUG) - CommOS_Debug(("%s: Sent 'Connect' [0x%p] reply.\n", __FUNCTION__, pvsk)); -#endif -} - - -/** - * @brief Server side main asynchronous processing function. It writes to - * socket queued output buffers, it reads from socket and outputs to PV; it - * also completes operation processing and sends applicable replies to PV. - * Finally, processes error reporting and delta size acks. - * @param arg socket work item. - */ - -void -PvtcpProcessAIO(CommOSWork *arg) -{ - PvtcpSock *pvsk = container_of(arg, PvtcpSock, work); - struct sock *sk = SkFromPvsk(pvsk); - - if (!SOCK_OUT_TRYLOCK(pvsk)) { - /* - * Queued output processing. If trylock failed, we don't retry. - * There are only two reasons for not being able to take the lock: - * - IoOp() has it -- when done, it reschedules us if we're not running. - * - OutputAIO() is already running on another core. - */ - - if (sk && sk->sk_socket) { - PvtcpOutputAIO(pvsk); - } - SOCK_OUT_UNLOCK(pvsk); - } - - /* All other processing needs the socket IN lock. */ - - if (!SOCK_IN_TRYLOCK(pvsk)) { - - if (sk && sk->sk_socket) { - int err; - - /* Input processing. */ - - /* - * Workqueue handlers are pinned to a CPU core and therefore not - * migratable. No need to disable preemption. - */ - err = PvtcpInputAIO(pvsk, perCpuBuf[smp_processor_id()]); - - /* Error and ack notifications. */ - - PvtcpFlowAIO(pvsk, err); - - if (!pvsk->opFlags) { - /* No other operations need to be completed. */ - - goto doneInUnlock; - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_RELEASE)) { - PvskResetOpFlag(pvsk, PVTCP_OP_RELEASE); - ReleaseAIO(pvsk); - - /* All possible in-flight operations must be dropped. */ - goto doneInUnlock; - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_CREATE)) { - /* No state locking required. */ - - PvskResetOpFlag(pvsk, PVTCP_OP_CREATE); - CreateAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_BIND)) { - PvskResetOpFlag(pvsk, PVTCP_OP_BIND); - BindAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_SETSOCKOPT)) { - PvskResetOpFlag(pvsk, PVTCP_OP_SETSOCKOPT); - SetSockOptAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_GETSOCKOPT)) { - PvskResetOpFlag(pvsk, PVTCP_OP_GETSOCKOPT); - GetSockOptAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_IOCTL)) { - PvskResetOpFlag(pvsk, PVTCP_OP_IOCTL); - IoctlAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_LISTEN)) { - PvskResetOpFlag(pvsk, PVTCP_OP_LISTEN); - ListenAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_ACCEPT)) { - PvskResetOpFlag(pvsk, PVTCP_OP_ACCEPT); - AcceptAIO(pvsk); - } - - if (PvskTestOpFlag(pvsk, PVTCP_OP_CONNECT)) { - PvskResetOpFlag(pvsk, PVTCP_OP_CONNECT); - ConnectAIO(pvsk); - } - -doneInUnlock: - SOCK_IN_UNLOCK(pvsk); - } else { - /* - * Special case for error sockets which don't have a sk. - * Note that this socket was created by SockAllocErrInit() and so - * no 'real' socket sits atop it and is not present on any state - * netif list. The socket has a refcnt of one and it will get - * deallocated by the PvtcpPutSock() call below, so we don't need - * to unlock it. - */ - - PvtcpFlowAIO(pvsk, -ENETDOWN); - } - } else { - if ((pvsk->peerSockSet || PvskTestOpFlag(pvsk, PVTCP_OP_RELEASE)) && - sk && sk->sk_socket) { - PvtcpSchedSock(pvsk); - } - } - - PvtcpPutSock(pvsk); -} diff --git a/arch/arm/mvp/pvtcpkm/pvtcp_off_linux.h b/arch/arm/mvp/pvtcpkm/pvtcp_off_linux.h deleted file mode 100644 index 34992da..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp_off_linux.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief Linux Offload definitions. - * This file is only meant to be included via pvtcp_off.h. - */ - -#ifndef _PVTCP_OFF_LINUX_H_ -#define _PVTCP_OFF_LINUX_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -typedef struct PvtcpSock { - struct sock *sk; - PVTCP_SOCK_COMMON_FIELDS; - PVTCP_OFF_SOCK_COMMON_FIELDS; - void (*destruct)(struct sock *sk); - void (*stateChange)(struct sock *sk); - void (*dataReady)(struct sock *sk, int bytes); - void (*writeSpace)(struct sock *sk); - void (*errorReport)(struct sock *sk); -} PvtcpSock; - - -typedef enum PvtcpSockNamespace { - PVTCP_SOCK_NAMESPACE_INITIAL, - PVTCP_SOCK_NAMESPACE_CHANNEL -} PvtcpSockNamespace; - - -/* Number of large datagram allocations. */ -extern unsigned long long pvtcpOffDgramAllocations; - -/* Inet4 loopback addresses. */ -extern unsigned int pvtcpLoopbackOffAddr; - -/* Get the 'struct sock' from a PvtcpSock. */ -#define SkFromPvsk(pvsk) ((pvsk)->sk) - -/* Get the PvtcpSock from a 'struct sock'. */ -#define PvskFromSk(sk) ((PvtcpSock *)(sk)->sk_user_data) - -int -PvtcpTestAndBindLoopbackInet4(PvtcpSock *pvsk, - unsigned int *addr, - unsigned short port); -int -PvtcpTestAndBindLoopbackInet6(PvtcpSock *pvsk, - unsigned long long *addr0, - unsigned long long *addr1, - unsigned short port); - -void PvtcpResetLoopbackInet4(PvtcpSock *pvsk, unsigned int *addr); -void PvtcpResetLoopbackInet6(PvtcpSock *pvsk, struct in6_addr *in6); - -void PvtcpFlowAIO(PvtcpSock *pvsk, int eof); -void PvtcpOutputAIO(PvtcpSock *pvsk); -int PvtcpInputAIO(PvtcpSock *pvsk, void *perCpuBuf); - - -/** - * @brief Switches a socket to the channel, or the initial name space. - * @param pvsk socket to switch. - * @param ns which namespace to switch to. - */ - -static inline void -PvtcpSwitchSock(PvtcpSock *pvsk, - PvtcpSockNamespace ns) -{ -#if defined(CONFIG_NET_NS) && !defined(PVTCP_NET_NS_DISABLE) - struct sock *sk; - struct net *prevNet; - - if (!pvsk) { - return; - } - sk = SkFromPvsk(pvsk); - if (!sk) { - /* If this is a phony, create fail reporting pvsk, just return. */ - - return; - } - - prevNet = sock_net(sk); - switch (ns) { - case PVTCP_SOCK_NAMESPACE_INITIAL: - sock_net_set(sk, get_net(&init_net)); - break; - case PVTCP_SOCK_NAMESPACE_CHANNEL: - sock_net_set(sk, get_net(pvsk->state->namespace)); - break; - } - put_net(prevNet); -#endif -} - - -/** - * @brief Tests whether a socket has an explicit namespace. - * @param pvsk socket to test. - * @return 1 if the socket has a namespace, 0 otherwise. - */ - -static inline int -PvtcpHasSockNamespace(PvtcpSock *pvsk) -{ -#if defined(CONFIG_NET_NS) && !defined(PVTCP_NET_NS_DISABLE) - struct sock *sk; - int rc = 0; - - if (!pvsk) { - return rc; - } - sk = SkFromPvsk(pvsk); - if (!sk) { - /* If this is a phony, create fail reporting pvsk, just return 0. */ - - return rc; - } - - rc = (sock_net(sk) != &init_net); - return rc; -#else - return 0; -#endif -} - - -/** - * @brief Retains the pvsock's underlying socket. - * @param pvsk socket to retain. - */ - -static inline void -PvtcpHoldSock(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - - if (likely(sk)) { - sock_hold(sk); - } -} - - -/** - * @brief Releases a hold on the pvsock's underlying socket. If the underlying - * socket is NULL, this is an error socket and we deallocate it. - * @param pvsk socket to release hold on. - */ - -static inline void -PvtcpPutSock(PvtcpSock *pvsk) -{ - struct sock *sk = SkFromPvsk(pvsk); - - if (likely(sk)) { - sock_put(sk); - } else { - /* - * This is an error socket, which does _not_ have an underlying socket. - * We simply need to free it. - */ - - CommOS_Kfree(pvsk); - } -} - - -/** - * @brief Schedules an offload socket for AIO. - * @param pvsk socket to schedule. - * @sideeffect the socket will be processed by AIO threads. - */ - -static inline void -PvtcpSchedSock(PvtcpSock *pvsk) -{ - /* - * We must hold the socket before we enqueue it for AIO, such that it may - * not be released while in the workqueue. If CommSvc_ScheduleAIOWork() - * returned non-zero, it means the socket had already been enqueued. In - * that case, we release the hold. Otherwise, the hold is released by the - * AIO function (PvtcpProcessAIO()). - * Note that error pv sockets may only originate from synchronized RPCs, - * or to be more precise, from PvtcpCreateOp(), and not from IO processing; - * this means that they cannot be attempted to be enqueued more than once. - */ - - PvtcpHoldSock(pvsk); - if (CommSvc_ScheduleAIOWork(&pvsk->work)) { - PvtcpPutSock(pvsk); - } -} - -#endif // _PVTCP_OFF_LINUX_H_ diff --git a/arch/arm/mvp/pvtcpkm/pvtcp_off_linux_shim.S b/arch/arm/mvp/pvtcpkm/pvtcp_off_linux_shim.S deleted file mode 100644 index 824286b..0000000 --- a/arch/arm/mvp/pvtcpkm/pvtcp_off_linux_shim.S +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Linux 2.6.32 and later Kernel module for VMware MVP PVTCP Server - * - * Copyright (C) 2010-2012 VMware, 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ -#line 5 - -/** - * @file - * - * @brief PVTCP socket destructor shim. - * - * The module reference accounting code for socket destruction in the core - * Linux kernel does not know about PVTCP sockets, so it does not properly - * increment/decrement the reference count on pvtcpkm when calling through a - * function pointer into our destructor. If a module unload is requested on - * pvtcpkm while a socket is being destroyed, it is possible for the destructor - * to be preempted after decrementing the module reference count but before - * returning to the core kernel. If the module code is unmapped before the - * function return, it is possible that we will attempt to execute unmapped - * code, resulting in a host crash. - * - * This shim proxies socket destruction requests through to the PVTCP socket - * destructor, then jumps directly to module_put to drop the reference count. - * module_put will return directly to the caller, eliminating the race. - */ - -.text -.p2align 4 - -.global asmDestructorShim - -/** - * @brief Socket destructor callback. Calls into pvtcpkm to destroy a socket - * and then decrements the refcount. - * @param r0 pointer to struct sock - */ - -asmDestructorShim: - push {lr} - ldr r1, targetAddr @ Destroy socket - blx r1 - pop {lr} - cmp r0, #0 - bxne lr @ We shouldn't module_put, just return. - ldr r0, owner - ldr r1, modulePutAddr @ Jump to module_put. module_put - bx r1 @ returns directly to caller - -owner: - .word __this_module - -targetAddr: - .word DestructCB - -modulePutAddr: - .word module_put -- cgit v1.1 From 36fa542e882882638e4359e7ccc6297c2f8adad0 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Fri, 20 May 2016 18:22:16 +1000 Subject: t0lte/i925: disable authentec interceptor module Change-Id: I4937d8d836404f7590aac29a2a01bb987ead882d --- arch/arm/configs/cyanogenmod_i925_defconfig | 2 +- arch/arm/configs/cyanogenmod_t0lte_defconfig | 2 +- arch/arm/configs/cyanogenmod_t0ltecdma_defconfig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i925_defconfig b/arch/arm/configs/cyanogenmod_i925_defconfig index ea42fa6..01d452c 100644 --- a/arch/arm/configs/cyanogenmod_i925_defconfig +++ b/arch/arm/configs/cyanogenmod_i925_defconfig @@ -2894,7 +2894,7 @@ CONFIG_MOBICORE_SUPPORT=y CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set -CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR=m +# CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR is not set # # File systems diff --git a/arch/arm/configs/cyanogenmod_t0lte_defconfig b/arch/arm/configs/cyanogenmod_t0lte_defconfig index 113daa0..d58be9e 100755 --- a/arch/arm/configs/cyanogenmod_t0lte_defconfig +++ b/arch/arm/configs/cyanogenmod_t0lte_defconfig @@ -2889,7 +2889,7 @@ CONFIG_MOBICORE_SUPPORT=y CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set -CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR=m +# CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR is not set # # File systems diff --git a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig index e63b06b..1dd7240 100755 --- a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig +++ b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig @@ -2889,7 +2889,7 @@ CONFIG_MOBICORE_SUPPORT=y CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set -CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR=m +# CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR is not set # # File systems -- cgit v1.1 From f991b824bffa2034b7c5403a22245a97642b6d31 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Fri, 13 May 2016 23:57:48 +1000 Subject: remove pmem Change-Id: I53ceca9c1e0896241513e166de39684d3654f068 --- arch/arm/configs/cyanogenmod_d710_defconfig | 1 - arch/arm/configs/cyanogenmod_i777_defconfig | 1 - arch/arm/configs/cyanogenmod_i9100_defconfig | 1 - arch/arm/configs/cyanogenmod_n7000_defconfig | 1 - arch/arm/mach-exynos/board-trats.c | 72 -- arch/arm/mach-exynos/mach-px.c | 72 -- arch/arm/mach-exynos/mach-smdk5210.c | 15 - arch/arm/mach-exynos/mach-u1.c | 72 -- arch/arm/mach-exynos/reserve_mem-exynos4.c | 22 - drivers/misc/Kconfig | 21 - drivers/misc/Makefile | 1 - drivers/misc/pmem.c | 1386 -------------------------- include/linux/android_pmem.h | 93 -- 13 files changed, 1758 deletions(-) delete mode 100644 drivers/misc/pmem.c delete mode 100644 include/linux/android_pmem.h diff --git a/arch/arm/configs/cyanogenmod_d710_defconfig b/arch/arm/configs/cyanogenmod_d710_defconfig index 37b84c7..bca492d 100644 --- a/arch/arm/configs/cyanogenmod_d710_defconfig +++ b/arch/arm/configs/cyanogenmod_d710_defconfig @@ -1205,7 +1205,6 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_SENSORS_LIS3LV02D is not set CONFIG_MISC_DEVICES=y # CONFIG_AD525X_DPOT is not set -# CONFIG_ANDROID_PMEM is not set # CONFIG_INTEL_MID_PTI is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set diff --git a/arch/arm/configs/cyanogenmod_i777_defconfig b/arch/arm/configs/cyanogenmod_i777_defconfig index fe979ab..2ac674b 100644 --- a/arch/arm/configs/cyanogenmod_i777_defconfig +++ b/arch/arm/configs/cyanogenmod_i777_defconfig @@ -1215,7 +1215,6 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_SENSORS_LIS3LV02D is not set CONFIG_MISC_DEVICES=y # CONFIG_AD525X_DPOT is not set -# CONFIG_ANDROID_PMEM is not set # CONFIG_INTEL_MID_PTI is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index fae21aa..412847a 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -1238,7 +1238,6 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_SENSORS_LIS3LV02D is not set CONFIG_MISC_DEVICES=y # CONFIG_AD525X_DPOT is not set -# CONFIG_ANDROID_PMEM is not set # CONFIG_INTEL_MID_PTI is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set diff --git a/arch/arm/configs/cyanogenmod_n7000_defconfig b/arch/arm/configs/cyanogenmod_n7000_defconfig index 874c7f3..ca51569 100644 --- a/arch/arm/configs/cyanogenmod_n7000_defconfig +++ b/arch/arm/configs/cyanogenmod_n7000_defconfig @@ -1227,7 +1227,6 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 # CONFIG_SENSORS_LIS3LV02D is not set CONFIG_MISC_DEVICES=y # CONFIG_AD525X_DPOT is not set -# CONFIG_ANDROID_PMEM is not set # CONFIG_INTEL_MID_PTI is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set diff --git a/arch/arm/mach-exynos/board-trats.c b/arch/arm/mach-exynos/board-trats.c index cf725ab..ed62092 100644 --- a/arch/arm/mach-exynos/board-trats.c +++ b/arch/arm/mach-exynos/board-trats.c @@ -47,9 +47,6 @@ #ifdef CONFIG_JACK_MON #include #endif -#ifdef CONFIG_ANDROID_PMEM -#include -#endif #include #include @@ -4844,53 +4841,6 @@ static struct platform_device s3c_device_i2c17 = { }; #endif -#ifdef CONFIG_ANDROID_PMEM -static struct android_pmem_platform_data pmem_pdata = { - .name = "pmem", - .no_allocator = 1, - .cached = 0, - .start = 0, - .size = 0 -}; - -static struct android_pmem_platform_data pmem_gpu1_pdata = { - .name = "pmem_gpu1", - .no_allocator = 1, - .cached = 0, - .start = 0, - .size = 0, -}; - -static struct platform_device pmem_device = { - .name = "android_pmem", - .id = 0, - .dev = { - .platform_data = &pmem_pdata}, -}; - -static struct platform_device pmem_gpu1_device = { - .name = "android_pmem", - .id = 1, - .dev = { - .platform_data = &pmem_gpu1_pdata}, -}; - -static void __init android_pmem_set_platdata(void) -{ -#if defined(CONFIG_S5P_MEM_CMA) - pmem_pdata.size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K; - pmem_gpu1_pdata.size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K; -#else - pmem_pdata.start = (u32) s5p_get_media_memory_bank(S5P_MDEV_PMEM, 0); - pmem_pdata.size = (u32) s5p_get_media_memsize_bank(S5P_MDEV_PMEM, 0); - pmem_gpu1_pdata.start = - (u32) s5p_get_media_memory_bank(S5P_MDEV_PMEM_GPU1, 0); - pmem_gpu1_pdata.size = - (u32) s5p_get_media_memsize_bank(S5P_MDEV_PMEM_GPU1, 0); -#endif -} -#endif - /* USB EHCI */ #ifdef CONFIG_USB_EHCI_S5P static struct s5p_ehci_platdata smdkc210_ehci_pdata; @@ -5187,10 +5137,6 @@ static struct platform_device *smdkc210_devices[] __initdata = { &ipc_spi_device, #endif -#ifdef CONFIG_ANDROID_PMEM - &pmem_device, - &pmem_gpu1_device, -#endif #ifdef CONFIG_VIDEO_FIMC &s3c_device_fimc0, &s3c_device_fimc1, @@ -5350,20 +5296,6 @@ static void __init exynos4_reserve_mem(void) .start = 0 }, #endif -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM - { - .name = "pmem", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K, - .start = 0, - }, -#endif -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - { - .name = "pmem_gpu1", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K, - .start = 0, - }, -#endif #ifdef CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMC0 { .name = "fimc0", @@ -5453,7 +5385,6 @@ static void __init exynos4_reserve_mem(void) #ifdef CONFIG_DRM_EXYNOS "exynos-drm=drm;" #endif - "android_pmem.0=pmem;android_pmem.1=pmem_gpu1;" "s3c-fimc.0=fimc0;s3c-fimc.1=fimc1;s3c-fimc.2=fimc2;s3c-fimc.3=fimc3;" "exynos4210-fimc.0=fimc0;exynos4210-fimc.1=fimc1;exynos4210-fimc.2=fimc2;exynos4210-fimc3=fimc3;" #ifdef CONFIG_ION_EXYNOS @@ -5726,9 +5657,6 @@ static void __init trats_machine_init(void) s5p_device_jpeg.dev.parent = &exynos4_device_pd[PD_CAM].dev; #endif #endif -#ifdef CONFIG_ANDROID_PMEM - android_pmem_set_platdata(); -#endif #ifdef CONFIG_VIDEO_FIMC /* fimc */ s3c_fimc0_set_platdata(&fimc_plat); diff --git a/arch/arm/mach-exynos/mach-px.c b/arch/arm/mach-exynos/mach-px.c index eaec80f..cc26a75 100644 --- a/arch/arm/mach-exynos/mach-px.c +++ b/arch/arm/mach-exynos/mach-px.c @@ -57,9 +57,6 @@ #if defined(CONFIG_S5P_MEM_CMA) #include #endif -#ifdef CONFIG_ANDROID_PMEM -#include -#endif #include #include @@ -5914,53 +5911,6 @@ static void __init mipi_fb_init(void) } #endif -#ifdef CONFIG_ANDROID_PMEM -static struct android_pmem_platform_data pmem_pdata = { - .name = "pmem", - .no_allocator = 1, - .cached = 0, - .start = 0, - .size = 0 -}; - -static struct android_pmem_platform_data pmem_gpu1_pdata = { - .name = "pmem_gpu1", - .no_allocator = 1, - .cached = 0, - .start = 0, - .size = 0, -}; - -static struct platform_device pmem_device = { - .name = "android_pmem", - .id = 0, - .dev = { - .platform_data = &pmem_pdata}, -}; - -static struct platform_device pmem_gpu1_device = { - .name = "android_pmem", - .id = 1, - .dev = { - .platform_data = &pmem_gpu1_pdata}, -}; - -static void __init android_pmem_set_platdata(void) -{ -#if defined(CONFIG_S5P_MEM_CMA) - pmem_pdata.size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K; - pmem_gpu1_pdata.size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K; -#else - pmem_pdata.start = (u32) s5p_get_media_memory_bank(S5P_MDEV_PMEM, 0); - pmem_pdata.size = (u32) s5p_get_media_memsize_bank(S5P_MDEV_PMEM, 0); - pmem_gpu1_pdata.start = - (u32) s5p_get_media_memory_bank(S5P_MDEV_PMEM_GPU1, 0); - pmem_gpu1_pdata.size = - (u32) s5p_get_media_memsize_bank(S5P_MDEV_PMEM_GPU1, 0); -#endif -} -#endif - /* USB EHCI */ #ifdef CONFIG_USB_EHCI_S5P static struct s5p_ehci_platdata smdkc210_ehci_pdata; @@ -6753,10 +6703,6 @@ static struct platform_device *smdkc210_devices[] __initdata = { #ifdef CONFIG_BATTERY_SEC_PX &sec_battery_device, #endif -#ifdef CONFIG_ANDROID_PMEM - &pmem_device, - &pmem_gpu1_device, -#endif #ifdef CONFIG_INTERNAL_MODEM_IF &sec_idpram_pm_device, @@ -7132,20 +7078,6 @@ static void __init exynos4_cma_region_reserve(struct cma_region *regions_normal, static void __init exynos4_reserve_mem(void) { static struct cma_region regions[] = { -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM - { - .name = "pmem", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K, - .start = 0, - }, -#endif -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - { - .name = "pmem_gpu1", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K, - .start = 0, - }, -#endif #ifdef CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMD { .name = "fimd", @@ -7245,7 +7177,6 @@ static void __init exynos4_reserve_mem(void) }; static const char map[] __initconst = - "android_pmem.0=pmem;android_pmem.1=pmem_gpu1;" "s3cfb.0=fimd;exynos4-fb.0=fimd;samsung-pd.1=fimd;" "s3c-fimc.0=fimc0;s3c-fimc.1=fimc1;s3c-fimc.2=fimc2;s3c-fimc.3=fimc3;" "exynos4210-fimc.0=fimc0;exynos4210-fimc.1=fimc1;exynos4210-fimc.2=fimc2;exynos4210-fimc.3=fimc3;" @@ -7674,9 +7605,6 @@ static void __init smdkc210_machine_init(void) s3c24xx_ts1_set_platdata(&s3c_ts_platform); #endif #endif -#ifdef CONFIG_ANDROID_PMEM - android_pmem_set_platdata(); -#endif #ifdef CONFIG_VIDEO_FIMC /* fimc */ s3c_fimc0_set_platdata(&fimc_plat); diff --git a/arch/arm/mach-exynos/mach-smdk5210.c b/arch/arm/mach-exynos/mach-smdk5210.c index 16ccba4..9ed28fa 100644 --- a/arch/arm/mach-exynos/mach-smdk5210.c +++ b/arch/arm/mach-exynos/mach-smdk5210.c @@ -947,20 +947,6 @@ static void __init exynos5_cma_region_reserve( static void __init exynos5_reserve_mem(void) { static struct cma_region regions[] = { -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM - { - .name = "pmem", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K, - .start = 0, - }, -#endif -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - { - .name = "pmem_gpu1", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K, - .start = 0, - }, -#endif #ifdef CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMD { .name = "fimd", @@ -1021,7 +1007,6 @@ static void __init exynos5_reserve_mem(void) }, }; static const char map[] __initconst = - "android_pmem.0=pmem;android_pmem.1=pmem_gpu1;" "s3cfb.0=fimd;" "exynos-gsc.0=gsc0;exynos-gsc.1=gsc1;exynos-gsc.2=gsc2;exynos-gsc.3=gsc3;" "ion-exynos=fimd,gsc0,gsc1,gsc2,gsc3;" diff --git a/arch/arm/mach-exynos/mach-u1.c b/arch/arm/mach-exynos/mach-u1.c index 5d5183c..ba440d2 100644 --- a/arch/arm/mach-exynos/mach-u1.c +++ b/arch/arm/mach-exynos/mach-u1.c @@ -55,9 +55,6 @@ #if defined(CONFIG_S5P_MEM_CMA) #include #endif -#ifdef CONFIG_ANDROID_PMEM -#include -#endif #include #include @@ -7132,53 +7129,6 @@ static void __init mipi_fb_init(void) } #endif -#ifdef CONFIG_ANDROID_PMEM -static struct android_pmem_platform_data pmem_pdata = { - .name = "pmem", - .no_allocator = 1, - .cached = 0, - .start = 0, - .size = 0 -}; - -static struct android_pmem_platform_data pmem_gpu1_pdata = { - .name = "pmem_gpu1", - .no_allocator = 1, - .cached = 0, - .start = 0, - .size = 0, -}; - -static struct platform_device pmem_device = { - .name = "android_pmem", - .id = 0, - .dev = { - .platform_data = &pmem_pdata}, -}; - -static struct platform_device pmem_gpu1_device = { - .name = "android_pmem", - .id = 1, - .dev = { - .platform_data = &pmem_gpu1_pdata}, -}; - -static void __init android_pmem_set_platdata(void) -{ -#if defined(CONFIG_S5P_MEM_CMA) - pmem_pdata.size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K; - pmem_gpu1_pdata.size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K; -#else - pmem_pdata.start = (u32) s5p_get_media_memory_bank(S5P_MDEV_PMEM, 0); - pmem_pdata.size = (u32) s5p_get_media_memsize_bank(S5P_MDEV_PMEM, 0); - pmem_gpu1_pdata.start = - (u32) s5p_get_media_memory_bank(S5P_MDEV_PMEM_GPU1, 0); - pmem_gpu1_pdata.size = - (u32) s5p_get_media_memsize_bank(S5P_MDEV_PMEM_GPU1, 0); -#endif -} -#endif - /* USB EHCI */ #ifdef CONFIG_USB_EHCI_S5P static struct s5p_ehci_platdata smdkc210_ehci_pdata; @@ -7483,10 +7433,6 @@ static struct platform_device *smdkc210_devices[] __initdata = { &s5p_device_cec, &s5p_device_hpd, #endif -#ifdef CONFIG_ANDROID_PMEM - &pmem_device, - &pmem_gpu1_device, -#endif #ifdef CONFIG_VIDEO_FIMC &s3c_device_fimc0, &s3c_device_fimc1, @@ -7649,20 +7595,6 @@ static void __init exynos4_cma_region_reserve(struct cma_region *regions_normal, static void __init exynos4_reserve_mem(void) { static struct cma_region regions[] = { -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM - { - .name = "pmem", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K, - .start = 0, - }, -#endif -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - { - .name = "pmem_gpu1", - .size = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K, - .start = 0, - }, -#endif #ifdef CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMD { .name = "fimd", @@ -7770,7 +7702,6 @@ static void __init exynos4_reserve_mem(void) }; static const char map[] __initconst = - "android_pmem.0=pmem;android_pmem.1=pmem_gpu1;" "s3cfb.0=fimd;exynos4-fb.0=fimd;samsung-pd.1=fimd;" "s3c-fimc.0=fimc0;s3c-fimc.1=fimc1;s3c-fimc.2=fimc2;s3c-fimc.3=fimc3;" "exynos4210-fimc.0=fimc0;exynos4210-fimc.1=fimc1;" @@ -8099,9 +8030,6 @@ static void __init smdkc210_machine_init(void) s3c24xx_ts1_set_platdata(&s3c_ts_platform); #endif #endif -#ifdef CONFIG_ANDROID_PMEM - android_pmem_set_platdata(); -#endif #ifdef CONFIG_VIDEO_FIMC /* fimc */ s3c_fimc0_set_platdata(&fimc_plat); diff --git a/arch/arm/mach-exynos/reserve_mem-exynos4.c b/arch/arm/mach-exynos/reserve_mem-exynos4.c index c48c64f..bd0d332 100644 --- a/arch/arm/mach-exynos/reserve_mem-exynos4.c +++ b/arch/arm/mach-exynos/reserve_mem-exynos4.c @@ -49,28 +49,6 @@ struct s5p_media_device media_devs[] = { }, #endif -#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS) -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM - { - .id = S5P_MDEV_PMEM, - .name = "pmem", - .bank = 0, - .memsize = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM * SZ_1K, - .paddr = 0, - }, -#endif - -#ifdef CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - { - .id = S5P_MDEV_PMEM_GPU1, - .name = "pmem_gpu1", - .bank = 0, - .memsize = CONFIG_ANDROID_PMEM_MEMSIZE_PMEM_GPU1 * SZ_1K, - .paddr = 0, - }, -#endif -#endif - #ifdef CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMC0 { .id = S5P_MDEV_FIMC0, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0559caa..1daa392 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -61,27 +61,6 @@ config AD525X_DPOT_SPI To compile this driver as a module, choose M here: the module will be called ad525x_dpot-spi. -config ANDROID_PMEM - bool "Android pmem allocator" - depends on MACH_U1 || MACH_PX || MACH_TRATS - default n - -if ANDROID_PMEM - comment "Reserved memory configurations" - -config ANDROID_PMEM_MEMSIZE_PMEM - int "Memory size in kbytes for android surface using pmem" - default "4096" - -config ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - int "Memory size in kbytes for android surface using pmem_gpu1" - default "10240" - -config ANDROID_PMEM_MEMSIZE_PMEM_CAM - int "Memory size in kbytes for android camera using pmem_camera" - default "4096" -endif - config ATMEL_PWM tristate "Atmel AT32/AT91 PWM support" depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 44e4884..8d3b193 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o -obj-$(CONFIG_ANDROID_PMEM) += pmem.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c deleted file mode 100644 index c7c9179..0000000 --- a/drivers/misc/pmem.c +++ /dev/null @@ -1,1386 +0,0 @@ -/* drivers/android/pmem.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 -#include -#include -#include -#include -#if defined(CONFIG_S5P_MEM_CMA) -#include -#endif - -#define PMEM_MAX_DEVICES 10 -#define PMEM_MAX_ORDER 128 -#define PMEM_MIN_ALLOC PAGE_SIZE - -#define PMEM_DEBUG 1 - -/* indicates that a refernce to this file has been taken via get_pmem_file, - * the file should not be released until put_pmem_file is called */ -#define PMEM_FLAGS_BUSY 0x1 -/* indicates that this is a suballocation of a larger master range */ -#define PMEM_FLAGS_CONNECTED 0x1 << 1 -/* indicates this is a master and not a sub allocation and that it is mmaped */ -#define PMEM_FLAGS_MASTERMAP 0x1 << 2 -/* submap and unsubmap flags indicate: - * 00: subregion has never been mmaped - * 10: subregion has been mmaped, reference to the mm was taken - * 11: subretion has ben released, refernece to the mm still held - * 01: subretion has been released, reference to the mm has been released - */ -#define PMEM_FLAGS_SUBMAP 0x1 << 3 -#define PMEM_FLAGS_UNSUBMAP 0x1 << 4 - - -struct pmem_data { - /* in alloc mode: an index into the bitmap - * in no_alloc mode: the size of the allocation */ - int index; - /* see flags above for descriptions */ - unsigned int flags; - /* protects this data field, if the mm_mmap sem will be held at the - * same time as this sem, the mm sem must be taken first (as this is - * the order for vma_open and vma_close ops */ - struct rw_semaphore sem; - /* info about the mmaping process */ - struct vm_area_struct *vma; - /* task struct of the mapping process */ - struct task_struct *task; - /* process id of teh mapping process */ - pid_t pid; - /* file descriptor of the master */ - int master_fd; - /* file struct of the master */ - struct file *master_file; - /* a list of currently available regions if this is a suballocation */ - struct list_head region_list; - /* a linked list of data so we can access them for debugging */ - struct list_head list; -#if PMEM_DEBUG - int ref; -#endif -}; - -struct pmem_bits { - unsigned allocated:1; /* 1 if allocated, 0 if free */ - unsigned order:7; /* size of the region in pmem space */ -}; - -struct pmem_region_node { - struct pmem_region region; - struct list_head list; -}; - -#define PMEM_DEBUG_MSGS 0 -#if PMEM_DEBUG_MSGS -#define DLOG(fmt,args...) \ - do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ - ##args); } \ - while (0) -#else -#define DLOG(x...) do {} while (0) -#endif - -struct pmem_info { - struct miscdevice dev; - /* physical start address of the remaped pmem space */ - unsigned long base; - /* vitual start address of the remaped pmem space */ - unsigned char __iomem *vbase; - /* total size of the pmem space */ - unsigned long size; - /* number of entries in the pmem space */ - unsigned long num_entries; - /* pfn of the garbage page in memory */ - unsigned long garbage_pfn; - /* index of the garbage page in the pmem space */ - int garbage_index; - /* the bitmap for the region indicating which entries are allocated - * and which are free */ - struct pmem_bits *bitmap; - /* indicates the region should not be managed with an allocator */ - unsigned no_allocator; - /* indicates maps of this region should be cached, if a mix of - * cached and uncached is desired, set this and open the device with - * O_SYNC to get an uncached region */ - unsigned cached; - unsigned buffered; - /* in no_allocator mode the first mapper gets the whole space and sets - * this flag */ - unsigned allocated; - /* for debugging, creates a list of pmem file structs, the - * data_list_lock should be taken before pmem_data->sem if both are - * needed */ - struct mutex data_list_lock; - struct list_head data_list; - /* pmem_sem protects the bitmap array - * a write lock should be held when modifying entries in bitmap - * a read lock should be held when reading data from bits or - * dereferencing a pointer into bitmap - * - * pmem_data->sem protects the pmem data of a particular file - * Many of the function that require the pmem_data->sem have a non- - * locking version for when the caller is already holding that sem. - * - * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER: - * down(pmem_data->sem) => down(bitmap_sem) - */ - struct rw_semaphore bitmap_sem; - - long (*ioctl)(struct file *, unsigned int, unsigned long); - int (*release)(struct inode *, struct file *); -}; - -static struct pmem_info pmem[PMEM_MAX_DEVICES]; -static int id_count; - -#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated) -#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order -#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index))) -#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index))) -#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC) -#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base) -#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC) -#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase) -#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED) -#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) -#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \ - (!(data->flags & PMEM_FLAGS_UNSUBMAP))) - -static int pmem_release(struct inode *, struct file *); -static int pmem_mmap(struct file *, struct vm_area_struct *); -static int pmem_open(struct inode *, struct file *); -static long pmem_ioctl(struct file *, unsigned int, unsigned long); - -struct file_operations pmem_fops = { - .release = pmem_release, - .mmap = pmem_mmap, - .open = pmem_open, - .unlocked_ioctl = pmem_ioctl, -}; - -static int get_id(struct file *file) -{ - return MINOR(file->f_dentry->d_inode->i_rdev); -} - -int is_pmem_file(struct file *file) -{ - int id; - - if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode)) - return 0; - id = get_id(file); - if (unlikely(id >= PMEM_MAX_DEVICES)) - return 0; - if (unlikely(file->f_dentry->d_inode->i_rdev != - MKDEV(MISC_MAJOR, pmem[id].dev.minor))) - return 0; - return 1; -} - -static int has_allocation(struct file *file) -{ - struct pmem_data *data; - /* check is_pmem_file first if not accessed via pmem_file_ops */ - - if (unlikely(!file->private_data)) - return 0; - data = (struct pmem_data *)file->private_data; - if (unlikely(data->index < 0)) - return 0; - return 1; -} - -static int is_master_owner(struct file *file) -{ - struct file *master_file; - struct pmem_data *data; - int put_needed, ret = 0; - - if (!is_pmem_file(file) || !has_allocation(file)) - return 0; - data = (struct pmem_data *)file->private_data; - if (PMEM_FLAGS_MASTERMAP & data->flags) - return 1; - master_file = fget_light(data->master_fd, &put_needed); - if (master_file && data->master_file == master_file) - ret = 1; - fput_light(master_file, put_needed); - return ret; -} - -static int pmem_free(int id, int index) -{ - /* caller should hold the write lock on pmem_sem! */ - int buddy, curr = index; - DLOG("index %d\n", index); - - if (pmem[id].no_allocator) { - pmem[id].allocated = 0; - return 0; - } - /* clean up the bitmap, merging any buddies */ - pmem[id].bitmap[curr].allocated = 0; - /* find a slots buddy Buddy# = Slot# ^ (1 << order) - * if the buddy is also free merge them - * repeat until the buddy is not free or end of the bitmap is reached - */ - do { - buddy = PMEM_BUDDY_INDEX(id, curr); - if (PMEM_IS_FREE(id, buddy) && - PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) { - PMEM_ORDER(id, buddy)++; - PMEM_ORDER(id, curr)++; - curr = min(buddy, curr); - } else { - break; - } - } while (curr < pmem[id].num_entries); - - return 0; -} - -static void pmem_revoke(struct file *file, struct pmem_data *data); - -static int pmem_release(struct inode *inode, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_region_node *region_node; - struct list_head *elt, *elt2; - int id = get_id(file), ret = 0; - - - mutex_lock(&pmem[id].data_list_lock); - /* if this file is a master, revoke all the memory in the connected - * files */ - if (PMEM_FLAGS_MASTERMAP & data->flags) { - struct pmem_data *sub_data; - list_for_each(elt, &pmem[id].data_list) { - sub_data = list_entry(elt, struct pmem_data, list); - down_read(&sub_data->sem); - if (PMEM_IS_SUBMAP(sub_data) && - file == sub_data->master_file) { - up_read(&sub_data->sem); - pmem_revoke(file, sub_data); - } else - up_read(&sub_data->sem); - } - } - list_del(&data->list); - mutex_unlock(&pmem[id].data_list_lock); - - - down_write(&data->sem); - - /* if its not a conencted file and it has an allocation, free it */ - if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) { - down_write(&pmem[id].bitmap_sem); - ret = pmem_free(id, data->index); - up_write(&pmem[id].bitmap_sem); - } - - /* if this file is a submap (mapped, connected file), downref the - * task struct */ - if (PMEM_FLAGS_SUBMAP & data->flags) - if (data->task) { - put_task_struct(data->task); - data->task = NULL; - } - - file->private_data = NULL; - - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, list); - list_del(elt); - kfree(region_node); - } - BUG_ON(!list_empty(&data->region_list)); - - up_write(&data->sem); - kfree(data); - if (pmem[id].release) - ret = pmem[id].release(inode, file); - - return ret; -} - -static int pmem_open(struct inode *inode, struct file *file) -{ - struct pmem_data *data; - int id = get_id(file); - int ret = 0; - - DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file)); - /* setup file->private_data to indicate its unmapped */ - /* you can only open a pmem device one time */ - if (file->private_data != NULL && file_count(file) != 1) - return -1; - data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); - if (!data) { - printk("pmem: unable to allocate memory for pmem metadata."); - return -1; - } - data->flags = 0; - data->index = -1; - data->task = NULL; - data->vma = NULL; - data->pid = 0; - data->master_file = NULL; -#if PMEM_DEBUG - data->ref = 0; -#endif - INIT_LIST_HEAD(&data->region_list); - init_rwsem(&data->sem); - - file->private_data = data; - INIT_LIST_HEAD(&data->list); - - mutex_lock(&pmem[id].data_list_lock); - list_add(&data->list, &pmem[id].data_list); - mutex_unlock(&pmem[id].data_list_lock); - return ret; -} - -static unsigned long pmem_order(unsigned long len) -{ - int i; - - len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC; - len--; - for (i = 0; i < sizeof(len)*8; i++) - if (len >> i == 0) - break; - return i; -} - -static int pmem_allocate(int id, unsigned long len) -{ - /* caller should hold the write lock on pmem_sem! */ - /* return the corresponding pdata[] entry */ - int curr = 0; - int end = pmem[id].num_entries; - int best_fit = -1; - unsigned long order = pmem_order(len); - - if (pmem[id].no_allocator) { - DLOG("no allocator"); - if ((len > pmem[id].size) || pmem[id].allocated) - return -1; - pmem[id].allocated = 1; - return len; - } - - if (order > PMEM_MAX_ORDER) - return -1; - DLOG("order %lx\n", order); - - /* look through the bitmap: - * if you find a free slot of the correct order use it - * otherwise, use the best fit (smallest with size > order) slot - */ - while (curr < end) { - if (PMEM_IS_FREE(id, curr)) { - if (PMEM_ORDER(id, curr) == (unsigned char)order) { - /* set the not free bit and clear others */ - best_fit = curr; - break; - } - if (PMEM_ORDER(id, curr) > (unsigned char)order && - (best_fit < 0 || - PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit))) - best_fit = curr; - } - curr = PMEM_NEXT_INDEX(id, curr); - } - - /* if best_fit < 0, there are no suitable slots, - * return an error - */ - if (best_fit < 0) { - printk("pmem: no space left to allocate!\n"); - return -1; - } - - /* now partition the best fit: - * split the slot into 2 buddies of order - 1 - * repeat until the slot is of the correct order - */ - while (PMEM_ORDER(id, best_fit) > (unsigned char)order) { - int buddy; - PMEM_ORDER(id, best_fit) -= 1; - buddy = PMEM_BUDDY_INDEX(id, best_fit); - PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit); - } - pmem[id].bitmap[best_fit].allocated = 1; - return best_fit; -} - -static pgprot_t pmem_access_prot(struct file *file, pgprot_t vma_prot) -{ - int id = get_id(file); -#ifdef pgprot_noncached - if (pmem[id].cached == 0 || file->f_flags & O_SYNC) - return pgprot_noncached(vma_prot); -#endif -#ifdef pgprot_ext_buffered - else if (pmem[id].buffered) - return pgprot_ext_buffered(vma_prot); -#endif - return vma_prot; -} - -static unsigned long pmem_start_addr(int id, struct pmem_data *data) -{ - if (pmem[id].no_allocator) - return PMEM_START_ADDR(id, 0); - else - return PMEM_START_ADDR(id, data->index); - -} - -static void *pmem_start_vaddr(int id, struct pmem_data *data) -{ - return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase; -} - -static unsigned long pmem_len(int id, struct pmem_data *data) -{ - if (pmem[id].no_allocator) - return data->index; - else - return PMEM_LEN(id, data->index); -} - -static int pmem_map_garbage(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - int i, garbage_pages = len >> PAGE_SHIFT; - - vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE; - for (i = 0; i < garbage_pages; i++) { - if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE), - pmem[id].garbage_pfn)) - return -EAGAIN; - } - return 0; -} - -static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - int garbage_pages; - DLOG("unmap offset %lx len %lx\n", offset, len); - - BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); - - garbage_pages = len >> PAGE_SHIFT; - zap_page_range(vma, vma->vm_start + offset, len, NULL); - pmem_map_garbage(id, vma, data, offset, len); - return 0; -} - -static int pmem_map_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - DLOG("map offset %lx len %lx\n", offset, len); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset)); - - if (io_remap_pfn_range(vma, vma->vm_start + offset, - (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT, - len, vma->vm_page_prot)) { - return -EAGAIN; - } - return 0; -} - -static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - /* hold the mm semp for the vma you are modifying when you call this */ - BUG_ON(!vma); - zap_page_range(vma, vma->vm_start + offset, len, NULL); - return pmem_map_pfn_range(id, vma, data, offset, len); -} - -static void pmem_vma_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct pmem_data *data = file->private_data; - int id = get_id(file); - /* this should never be called as we don't support copying pmem - * ranges via fork */ - BUG_ON(!has_allocation(file)); - down_write(&data->sem); - /* remap the garbage pages, forkers don't get access to the data */ - pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end); - up_write(&data->sem); -} - -static void pmem_vma_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct pmem_data *data = file->private_data; - - DLOG("current %u ppid %u file %p count %d\n", current->pid, - current->parent->pid, file, file_count(file)); - if (unlikely(!is_pmem_file(file) || !has_allocation(file))) { - printk(KERN_WARNING "pmem: something is very wrong, you are " - "closing a vm backing an allocation that doesn't " - "exist!\n"); - return; - } - down_write(&data->sem); - if (data->vma == vma) { - data->vma = NULL; - if ((data->flags & PMEM_FLAGS_CONNECTED) && - (data->flags & PMEM_FLAGS_SUBMAP)) - data->flags |= PMEM_FLAGS_UNSUBMAP; - } - /* the kernel is going to free this vma now anyway */ - up_write(&data->sem); -} - -static struct vm_operations_struct vm_ops = { - .open = pmem_vma_open, - .close = pmem_vma_close, -}; - -static int pmem_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct pmem_data *data; - int index; - unsigned long vma_size = vma->vm_end - vma->vm_start; - int ret = 0, id = get_id(file); - - if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) { -#if PMEM_DEBUG - printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned" - " and a multiple of pages_size.\n"); -#endif - return -EINVAL; - } - - data = (struct pmem_data *)file->private_data; - down_write(&data->sem); - /* check this file isn't already mmaped, for submaps check this file - * has never been mmaped */ - if ((data->flags & PMEM_FLAGS_SUBMAP) || - (data->flags & PMEM_FLAGS_UNSUBMAP)) { -#if PMEM_DEBUG - printk(KERN_ERR "pmem: you can only mmap a pmem file once, " - "this file is already mmaped. %x\n", data->flags); -#endif - ret = -EINVAL; - goto error; - } - /* if file->private_data == unalloced, alloc*/ - if (data && data->index == -1) { - down_write(&pmem[id].bitmap_sem); - index = pmem_allocate(id, vma->vm_end - vma->vm_start); - up_write(&pmem[id].bitmap_sem); - data->index = index; - } - /* either no space was available or an error occured */ - if (!has_allocation(file)) { - ret = -EINVAL; - printk("pmem: could not find allocation for map.\n"); - goto error; - } - - if (pmem_len(id, data) < vma_size) { -#if PMEM_DEBUG - printk(KERN_WARNING "pmem: mmap size [%lu] does not match" - "size of backing region [%lu].\n", vma_size, - pmem_len(id, data)); -#endif - ret = -EINVAL; - goto error; - } - - vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT; - vma->vm_page_prot = pmem_access_prot(file, vma->vm_page_prot); - - if (data->flags & PMEM_FLAGS_CONNECTED) { - struct pmem_region_node *region_node; - struct list_head *elt; - if (pmem_map_garbage(id, vma, data, 0, vma_size)) { - printk("pmem: mmap failed in kernel!\n"); - ret = -EAGAIN; - goto error; - } - list_for_each(elt, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - DLOG("remapping file: %p %lx %lx\n", file, - region_node->region.offset, - region_node->region.len); - if (pmem_remap_pfn_range(id, vma, data, - region_node->region.offset, - region_node->region.len)) { - ret = -EAGAIN; - goto error; - } - } - data->flags |= PMEM_FLAGS_SUBMAP; - get_task_struct(current->group_leader); - data->task = current->group_leader; - data->vma = vma; -#if PMEM_DEBUG - data->pid = current->pid; -#endif - DLOG("submmapped file %p vma %p pid %u\n", file, vma, - current->pid); - } else { - if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) { - printk(KERN_INFO "pmem: mmap failed in kernel!\n"); - ret = -EAGAIN; - goto error; - } - data->flags |= PMEM_FLAGS_MASTERMAP; - data->pid = current->pid; - } - vma->vm_ops = &vm_ops; -error: - up_write(&data->sem); - return ret; -} - -/* the following are the api for accessing pmem regions by other drivers - * from inside the kernel */ -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *len) -{ - struct pmem_data *data; - if (!is_pmem_file(file) || !has_allocation(file)) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from invalid" - "file.\n"); -#endif - return -1; - } - data = (struct pmem_data *)file->private_data; - down_read(&data->sem); - if (data->vma) { - *start = data->vma->vm_start; - *len = data->vma->vm_end - data->vma->vm_start; - } else { - *start = 0; - *len = 0; - } - up_read(&data->sem); - return 0; -} - -int get_pmem_addr(struct file *file, unsigned long *start, - unsigned long *vstart, unsigned long *len) -{ - struct pmem_data *data; - int id; - - if (!is_pmem_file(file) || !has_allocation(file)) { - return -1; - } - - data = (struct pmem_data *)file->private_data; - if (data->index == -1) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from file with no " - "allocation.\n"); - return -1; -#endif - } - id = get_id(file); - - down_read(&data->sem); - *start = pmem_start_addr(id, data); - *len = pmem_len(id, data); - *vstart = (unsigned long)pmem_start_vaddr(id, data); - up_read(&data->sem); -#if PMEM_DEBUG - down_write(&data->sem); - data->ref++; - up_write(&data->sem); -#endif - return 0; -} - -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *len, struct file **filp) -{ - struct file *file; - - file = fget(fd); - if (unlikely(file == NULL)) { - printk(KERN_INFO "pmem: requested data from file descriptor " - "that doesn't exist."); - return -1; - } - - if (get_pmem_addr(file, start, vstart, len)) - goto end; - - if (filp) - *filp = file; - return 0; -end: - fput(file); - return -1; -} - -void put_pmem_file(struct file *file) -{ - struct pmem_data *data; - int id; - - if (!is_pmem_file(file)) - return; - id = get_id(file); - data = (struct pmem_data *)file->private_data; -#if PMEM_DEBUG - down_write(&data->sem); - if (data->ref == 0) { - printk("pmem: pmem_put > pmem_get %s (pid %d)\n", - pmem[id].dev.name, data->pid); - BUG(); - } - data->ref--; - up_write(&data->sem); -#endif - fput(file); -} - -void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) -{ - struct pmem_data *data; - int id; - void *vaddr; - struct pmem_region_node *region_node; - struct list_head *elt; - void *flush_start, *flush_end; - - if (!is_pmem_file(file) || !has_allocation(file)) { - return; - } - - id = get_id(file); - data = (struct pmem_data *)file->private_data; - if (!pmem[id].cached || file->f_flags & O_SYNC) - return; - - down_read(&data->sem); - vaddr = pmem_start_vaddr(id, data); - /* if this isn't a submmapped file, flush the whole thing */ - if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { - if (pmem_len(id, data) >= SZ_1M) { - flush_all_cpu_caches(); - outer_flush_all(); - } else if (pmem_len(id, data) >= SZ_64K) { - flush_all_cpu_caches(); - outer_flush_range(virt_to_phys(vaddr), virt_to_phys(vaddr + pmem_len(id, data))); - } else { - dmac_flush_range(vaddr, vaddr + pmem_len(id, data)); - outer_flush_range(virt_to_phys(vaddr), virt_to_phys(vaddr + pmem_len(id, data))); - } - goto end; - } - /* otherwise, flush the region of the file we are drawing */ - list_for_each(elt, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, list); - if ((offset >= region_node->region.offset) && - ((offset + len) <= (region_node->region.offset + - region_node->region.len))) { - flush_start = vaddr + region_node->region.offset; - flush_end = flush_start + region_node->region.len; - - if (pmem_len(id, data) >= SZ_1M) { - flush_all_cpu_caches(); - outer_flush_all(); - } else if (pmem_len(id, data) >= SZ_64K) { - flush_all_cpu_caches(); - outer_flush_range(virt_to_phys(flush_start), virt_to_phys(flush_end)); - } else { - dmac_flush_range(flush_start, flush_end); - outer_flush_range(virt_to_phys(flush_start), virt_to_phys(flush_end)); - } - break; - } - } -end: - up_read(&data->sem); -} - -static int pmem_connect(unsigned long connect, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_data *src_data; - struct file *src_file; - int ret = 0, put_needed; - - down_write(&data->sem); - /* retrieve the src file and check it is a pmem file with an alloc */ - src_file = fget_light(connect, &put_needed); - DLOG("connect %p to %p\n", file, src_file); - if (!src_file) { - printk("pmem: src file not found!\n"); - ret = -EINVAL; - goto err_no_file; - } - if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) { - printk(KERN_INFO "pmem: src file is not a pmem file or has no " - "alloc!\n"); - ret = -EINVAL; - goto err_bad_file; - } - src_data = (struct pmem_data *)src_file->private_data; - - if (has_allocation(file) && (data->index != src_data->index)) { - printk("pmem: file is already mapped but doesn't match this" - " src_file!\n"); - ret = -EINVAL; - goto err_bad_file; - } - data->index = src_data->index; - data->flags |= PMEM_FLAGS_CONNECTED; - data->master_fd = connect; - data->master_file = src_file; - -err_bad_file: - fput_light(src_file, put_needed); -err_no_file: - up_write(&data->sem); - return ret; -} - -static void pmem_unlock_data_and_mm(struct pmem_data *data, - struct mm_struct *mm) -{ - up_write(&data->sem); - if (mm != NULL) { - up_write(&mm->mmap_sem); - mmput(mm); - } -} - -static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, - struct mm_struct **locked_mm) -{ - int ret = 0; - struct mm_struct *mm = NULL; - *locked_mm = NULL; -lock_mm: - down_read(&data->sem); - if (PMEM_IS_SUBMAP(data)) { - mm = get_task_mm(data->task); - if (!mm) { -#if PMEM_DEBUG - printk("pmem: can't remap task is gone!\n"); -#endif - up_read(&data->sem); - return -1; - } - } - up_read(&data->sem); - - if (mm) - down_write(&mm->mmap_sem); - - down_write(&data->sem); - /* check that the file didn't get mmaped before we could take the - * data sem, this should be safe b/c you can only submap each file - * once */ - if (PMEM_IS_SUBMAP(data) && !mm) { - pmem_unlock_data_and_mm(data, mm); - goto lock_mm; - } - /* now check that vma.mm is still there, it could have been - * deleted by vma_close before we could get the data->sem */ - if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) { - /* might as well release this */ - if (data->flags & PMEM_FLAGS_SUBMAP) { - put_task_struct(data->task); - data->task = NULL; - /* lower the submap flag to show the mm is gone */ - data->flags &= ~(PMEM_FLAGS_SUBMAP); - } - pmem_unlock_data_and_mm(data, mm); - return -1; - } - *locked_mm = mm; - return ret; -} - -int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) -{ - int ret; - struct pmem_region_node *region_node; - struct mm_struct *mm = NULL; - struct list_head *elt, *elt2; - int id = get_id(file); - struct pmem_data *data = (struct pmem_data *)file->private_data; - - /* pmem region must be aligned on a page boundry */ - if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) || - !PMEM_IS_PAGE_ALIGNED(region->len))) { -#if PMEM_DEBUG - printk("pmem: request for unaligned pmem suballocation " - "%lx %lx\n", region->offset, region->len); -#endif - return -EINVAL; - } - - /* if userspace requests a region of len 0, there's nothing to do */ - if (region->len == 0) - return 0; - - /* lock the mm and data */ - ret = pmem_lock_data_and_mm(file, data, &mm); - if (ret) - return 0; - - /* only the owner of the master file can remap the client fds - * that back in it */ - if (!is_master_owner(file)) { -#if PMEM_DEBUG - printk("pmem: remap requested from non-master process\n"); -#endif - ret = -EINVAL; - goto err; - } - - /* check that the requested range is within the src allocation */ - if (unlikely((region->offset > pmem_len(id, data)) || - (region->len > pmem_len(id, data)) || - (region->offset + region->len > pmem_len(id, data)))) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n"); -#endif - ret = -EINVAL; - goto err; - } - - if (operation == PMEM_MAP) { - region_node = kmalloc(sizeof(struct pmem_region_node), - GFP_KERNEL); - if (!region_node) { - ret = -ENOMEM; -#if PMEM_DEBUG - printk(KERN_INFO "No space to allocate metadata!"); -#endif - goto err; - } - region_node->region = *region; - list_add(®ion_node->list, &data->region_list); - } else if (operation == PMEM_UNMAP) { - int found = 0; - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - if (region->len == 0 || - (region_node->region.offset == region->offset && - region_node->region.len == region->len)) { - list_del(elt); - kfree(region_node); - found = 1; - } - } - if (!found) { -#if PMEM_DEBUG - printk("pmem: Unmap region does not map any mapped " - "region!"); -#endif - ret = -EINVAL; - goto err; - } - } - - if (data->vma && PMEM_IS_SUBMAP(data)) { - if (operation == PMEM_MAP) - ret = pmem_remap_pfn_range(id, data->vma, data, - region->offset, region->len); - else if (operation == PMEM_UNMAP) - ret = pmem_unmap_pfn_range(id, data->vma, data, - region->offset, region->len); - } - -err: - pmem_unlock_data_and_mm(data, mm); - return ret; -} - -static void pmem_revoke(struct file *file, struct pmem_data *data) -{ - struct pmem_region_node *region_node; - struct list_head *elt, *elt2; - struct mm_struct *mm = NULL; - int id = get_id(file); - int ret = 0; - - data->master_file = NULL; - ret = pmem_lock_data_and_mm(file, data, &mm); - /* if lock_data_and_mm fails either the task that mapped the fd, or - * the vma that mapped it have already gone away, nothing more - * needs to be done */ - if (ret) - return; - /* unmap everything */ - /* delete the regions and region list nothing is mapped any more */ - if (data->vma) - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - pmem_unmap_pfn_range(id, data->vma, data, - region_node->region.offset, - region_node->region.len); - list_del(elt); - kfree(region_node); - } - /* delete the master file */ - pmem_unlock_data_and_mm(data, mm); -} - -static void pmem_get_size(struct pmem_region *region, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - int id = get_id(file); - - if (!has_allocation(file)) { - region->offset = 0; - region->len = 0; - return; - } else { - region->offset = pmem_start_addr(id, data); - region->len = pmem_len(id, data); - } - DLOG("offset %lx len %lx\n", region->offset, region->len); -} - - -static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct pmem_data *data; - int id = get_id(file); - - switch (cmd) { - case PMEM_GET_PHYS: - { - struct pmem_region region; - DLOG("get_phys\n"); - if (!has_allocation(file)) { - region.offset = 0; - region.len = 0; - } else { - data = (struct pmem_data *)file->private_data; - region.offset = pmem_start_addr(id, data); - region.len = pmem_len(id, data); - } - printk(KERN_DEBUG "pmem: request for physical address of pmem region " - "from process %d.\n", current->pid); - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_MAP: - { - struct pmem_region region; - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - data = (struct pmem_data *)file->private_data; - return pmem_remap(®ion, file, PMEM_MAP); - } - break; - case PMEM_UNMAP: - { - struct pmem_region region; - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - data = (struct pmem_data *)file->private_data; - return pmem_remap(®ion, file, PMEM_UNMAP); - break; - } - case PMEM_GET_SIZE: - { - struct pmem_region region; - DLOG("get_size\n"); - pmem_get_size(®ion, file); - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_GET_TOTAL_SIZE: - { - struct pmem_region region; - DLOG("get total size\n"); - region.offset = 0; - get_id(file); - region.len = pmem[id].size; - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_ALLOCATE: - { - if (has_allocation(file)) - return -EINVAL; - data = (struct pmem_data *)file->private_data; - data->index = pmem_allocate(id, arg); - break; - } - case PMEM_CONNECT: - DLOG("connect\n"); - return pmem_connect(arg, file); - break; - case PMEM_CACHE_FLUSH: - { - struct pmem_region region; - DLOG("flush\n"); - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - flush_pmem_file(file, region.offset, region.len); - break; - } - default: - if (pmem[id].ioctl) - return pmem[id].ioctl(file, cmd, arg); - return -EINVAL; - } - return 0; -} - -#if PMEM_DEBUG -static ssize_t debug_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t debug_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct list_head *elt, *elt2; - struct pmem_data *data; - struct pmem_region_node *region_node; - int id = (int)file->private_data; - const int debug_bufmax = 4096; - static char buffer[4096]; - int n = 0; - - DLOG("debug open\n"); - n = scnprintf(buffer, debug_bufmax, - "pid #: mapped regions (offset, len) (offset,len)...\n"); - - mutex_lock(&pmem[id].data_list_lock); - list_for_each(elt, &pmem[id].data_list) { - data = list_entry(elt, struct pmem_data, list); - down_read(&data->sem); - n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:", - data->pid); - list_for_each(elt2, &data->region_list) { - region_node = list_entry(elt2, struct pmem_region_node, - list); - n += scnprintf(buffer + n, debug_bufmax - n, - "(%lx,%lx) ", - region_node->region.offset, - region_node->region.len); - } - n += scnprintf(buffer + n, debug_bufmax - n, "\n"); - up_read(&data->sem); - } - mutex_unlock(&pmem[id].data_list_lock); - - n++; - buffer[n] = 0; - return simple_read_from_buffer(buf, count, ppos, buffer, n); -} - -static struct file_operations debug_fops = { - .read = debug_read, - .open = debug_open, -}; -#endif - -#if 0 -static struct miscdevice pmem_dev = { - .name = "pmem", - .fops = &pmem_fops, -}; -#endif - -int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) -{ - int err = 0; - int i, index = 0; - int id = id_count; - struct page **pages; - int nr_pages; - int prot; - id_count++; - -#if defined(CONFIG_S5P_MEM_CMA) - pdata->start = cma_alloc_from(pdata->name, pdata->size, 0); -#endif - - pmem[id].no_allocator = pdata->no_allocator; - pmem[id].cached = pdata->cached; - pmem[id].buffered = pdata->buffered; - pmem[id].base = pdata->start; - pmem[id].size = pdata->size; - pmem[id].ioctl = ioctl; - pmem[id].release = release; - init_rwsem(&pmem[id].bitmap_sem); - mutex_init(&pmem[id].data_list_lock); - INIT_LIST_HEAD(&pmem[id].data_list); - pmem[id].dev.name = pdata->name; - pmem[id].dev.minor = id; - pmem[id].dev.fops = &pmem_fops; - printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached); - - err = misc_register(&pmem[id].dev); - if (err) { - printk(KERN_ALERT "Unable to register pmem driver!\n"); - goto err_cant_register_device; - } - pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC; - - pmem[id].bitmap = kmalloc(pmem[id].num_entries * - sizeof(struct pmem_bits), GFP_KERNEL); - if (!pmem[id].bitmap) - goto err_no_mem_for_metadata; - - memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) * - pmem[id].num_entries); - - for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) { - if ((pmem[id].num_entries) & 1<> PAGE_SHIFT; - pages = kmalloc(nr_pages * sizeof(*pages), GFP_KERNEL); - if (!pages) - goto err_no_mem_for_pages; - - for (i = 0; i < nr_pages; i++) - pages[i] = phys_to_page(pmem[id].base + i * PAGE_SIZE); - - if (pmem[id].cached) - prot = PAGE_KERNEL; - else if (pmem[id].buffered) - prot = pgprot_writecombine(PAGE_KERNEL); - else - prot = pgprot_noncached(PAGE_KERNEL); - - pmem[id].vbase = vmap(pages, nr_pages, VM_MAP, prot); - - kfree(pages); - - if (pmem[id].vbase == 0) - goto error_cant_remap; - - pmem[id].garbage_pfn = page_to_pfn(alloc_page(GFP_KERNEL)); - if (pmem[id].no_allocator) - pmem[id].allocated = 0; - -#if PMEM_DEBUG - debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)id, - &debug_fops); -#endif - return 0; -error_cant_remap: -err_no_mem_for_pages: - kfree(pmem[id].bitmap); -err_no_mem_for_metadata: - misc_deregister(&pmem[id].dev); -err_cant_register_device: - return -1; -} - -static int pmem_probe(struct platform_device *pdev) -{ - struct android_pmem_platform_data *pdata; - - if (!pdev || !pdev->dev.platform_data) { - printk(KERN_ALERT "Unable to probe pmem!\n"); - return -1; - } - pdata = pdev->dev.platform_data; - return pmem_setup(pdata, NULL, NULL); -} - - -static int pmem_remove(struct platform_device *pdev) -{ - int id = pdev->id; - __free_page(pfn_to_page(pmem[id].garbage_pfn)); -#if defined(CONFIG_S5P_MEM_CMA) - cma_free(((struct android_pmem_platform_data *)(pdev->dev.platform_data))->start); -#endif - misc_deregister(&pmem[id].dev); - return 0; -} - -static struct platform_driver pmem_driver = { - .probe = pmem_probe, - .remove = pmem_remove, - .driver = { .name = "android_pmem" } -}; - - -static int __init pmem_init(void) -{ - return platform_driver_register(&pmem_driver); -} - -static void __exit pmem_exit(void) -{ - platform_driver_unregister(&pmem_driver); -} - -module_init(pmem_init); -module_exit(pmem_exit); - diff --git a/include/linux/android_pmem.h b/include/linux/android_pmem.h deleted file mode 100644 index f633621..0000000 --- a/include/linux/android_pmem.h +++ /dev/null @@ -1,93 +0,0 @@ -/* include/linux/android_pmem.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 _ANDROID_PMEM_H_ -#define _ANDROID_PMEM_H_ - -#define PMEM_IOCTL_MAGIC 'p' -#define PMEM_GET_PHYS _IOW(PMEM_IOCTL_MAGIC, 1, unsigned int) -#define PMEM_MAP _IOW(PMEM_IOCTL_MAGIC, 2, unsigned int) -#define PMEM_GET_SIZE _IOW(PMEM_IOCTL_MAGIC, 3, unsigned int) -#define PMEM_UNMAP _IOW(PMEM_IOCTL_MAGIC, 4, unsigned int) -/* This ioctl will allocate pmem space, backing the file, it will fail - * if the file already has an allocation, pass it the len as the argument - * to the ioctl */ -#define PMEM_ALLOCATE _IOW(PMEM_IOCTL_MAGIC, 5, unsigned int) -/* This will connect a one pmem file to another, pass the file that is already - * backed in memory as the argument to the ioctl - */ -#define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int) -/* Returns the total size of the pmem region it is sent to as a pmem_region - * struct (with offset set to 0). - */ -#define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int) -#define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) - -struct android_pmem_platform_data -{ - const char* name; - /* starting physical address of memory region */ - unsigned long start; - /* size of memory region */ - unsigned long size; - /* set to indicate the region should not be managed with an allocator */ - unsigned no_allocator; - /* set to indicate maps of this region should be cached, if a mix of - * cached and uncached is desired, set this and open the device with - * O_SYNC to get an uncached region */ - unsigned cached; - /* The MSM7k has bits to enable a write buffer in the bus controller*/ - unsigned buffered; -}; - -struct pmem_region { - unsigned long offset; - unsigned long len; -}; - -#ifdef CONFIG_ANDROID_PMEM -int is_pmem_file(struct file *file); -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *end, struct file **filp); -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end); -void put_pmem_file(struct file* file); -void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); -int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)); -int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation); - -#else -static inline int is_pmem_file(struct file *file) { return 0; } -static inline int get_pmem_file(int fd, unsigned long *start, - unsigned long *vstart, unsigned long *end, - struct file **filp) { return -ENOSYS; } -static inline int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end) { return -ENOSYS; } -static inline void put_pmem_file(struct file* file) { return; } -static inline void flush_pmem_file(struct file *file, unsigned long start, - unsigned long len) { return; } -static inline int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) { return -ENOSYS; } - -static inline int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) { return -ENOSYS; } -#endif - -#endif //_ANDROID_PPP_H_ - -- cgit v1.1 From 4e34f354af093846d1f2a3b5722025ae0b4fb619 Mon Sep 17 00:00:00 2001 From: Arnab Chaudhuri Date: Sun, 8 May 2016 09:24:28 +0530 Subject: i9100: Enable BLN support Change-Id: I71b2e44803457062412c40c887bf0a1c248974c6 --- arch/arm/configs/cyanogenmod_i9100_defconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index 412847a..bf8eac8 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -1490,7 +1490,9 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_SENSORS_HALL is not set -CONFIG_KEYBOARD_CYPRESS_TOUCH=y +# CONFIG_KEYBOARD_CYPRESS_TOUCH is not set +CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN=y +CONFIG_TOUCHKEY_BLN=y # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set -- cgit v1.1 From de8f823c541d4f3b5ce63322f12b8455d4efda0a Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 23 May 2016 12:54:46 +1000 Subject: remove one more pmem reference Change-Id: I5e65c86f91d50b154cce802a0a2179e3b335bdfb --- drivers/misc/tzic.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/tzic.c b/drivers/misc/tzic.c index 6a5e3ba..643e078 100644 --- a/drivers/misc/tzic.c +++ b/drivers/misc/tzic.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include -- cgit v1.1 From 4cf5942b57b250733bc3a00781e2b752f52a0519 Mon Sep 17 00:00:00 2001 From: RGIB Date: Wed, 25 May 2016 00:00:03 +0200 Subject: smdk4412 : update mali driver build info Change-Id: If2c074434d4b511dd1e5b3567aeb61b092392f11 --- drivers/gpu/mali400/r3p2/mali/__malidrv_build_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/mali400/r3p2/mali/__malidrv_build_info.c b/drivers/gpu/mali400/r3p2/mali/__malidrv_build_info.c index af5e808..485ad26 100644 --- a/drivers/gpu/mali400/r3p2/mali/__malidrv_build_info.c +++ b/drivers/gpu/mali400/r3p2/mali/__malidrv_build_info.c @@ -1 +1 @@ -const char *__malidrv_build_info(void) { return "malidrv: API_VERSION=23 REPO_URL=heads/master REVISION=r3p2-01rel3-137c649 CHANGED_REVISION=137c649 CHANGE_DATE=2013-08-22 17:45:49 +0900 BUILD_DATE=Fri Aug 23 20:48:48 KST 2013 BUILD=release TARGET_PLATFORM=pegasus-m400 MALI_PLATFORM=pegasus-m400 KDIR= OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=16 USING_UMP=y USING_PROFILING= USING_INTERNAL_PROFILING= USING_GPU_UTILIZATION=1 MALI_UPPER_HALF_SCHEDULING=1";} +const char *__malidrv_build_info(void) { return "mali: BUILD=RELEASE ARCH=arch_011_udd PLATFORM=default_7a TRACE=0 THREAD= GEOM=mali_gp_geometry_common CORES=MALI400 USING_MALI400=1 TARGET_CORE_REVISION=0x0101 TOPLEVEL_REPO_URL=Linux-r3p2-01rel3 REVISION=Linux-r3p2-01rel3 CHANGED_REVISION=Linux-r3p2-01rel3 REPO_URL=Linux-r3p2-01rel3 BUILD_DATE=Tue Aug 26 17:05:16 KST 2014 CHANGE_DATE=Linux-r3p2-01rel3 TARGET_TOOLCHAIN=arm-linux-gcc HOST_TOOLCHAIN=gcc TARGET_TOOLCHAIN_VERSION=gcc version 4.6.x-google 20120106 (prerelease) (GCC) HOST_TOOLCHAIN_VERSION=gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5.1) TARGET_SYSTEM=gcc-arm-linux HOST_SYSTEM=gcc-x86_64-linux CPPFLAGS= CUSTOMER=internal VARIANT=mali400-r1p1-gles11-gles20-linux-android-max_pp_split_count_4-ump-smp-rgb_is_xrgb-jellybean_mr2-gp_cache_alloc HOSTLIB=direct INSTRUMENTED=FALSE USING_MRI=FALSE MALI_TEST_API= UDD_OS=linux";} -- cgit v1.1 From 540f1d84d4f8fb27e73dfcb6f3b13c39fa667041 Mon Sep 17 00:00:00 2001 From: RGIB Date: Wed, 25 May 2016 18:27:06 +0200 Subject: smdk4412 : modem_if KK driver from N5100ZTCNL4 Change-Id: I903a0f614751f374e1705df5c35f4e1e21190b13 --- drivers/misc/modem_if/Kconfig | 29 +- drivers/misc/modem_if/Makefile | 34 +- drivers/misc/modem_if/modem_boot_device_spi.c | 279 ++ drivers/misc/modem_if/modem_link_device_c2c.c | 2281 +++++++++++++- drivers/misc/modem_if/modem_link_device_c2c.h | 202 +- drivers/misc/modem_if/modem_link_device_dpram.c | 3284 +++++++++++++------- drivers/misc/modem_if/modem_link_device_dpram.h | 455 ++- .../misc/modem_if/modem_link_device_dpram_ext_op.c | 1619 +++++++--- drivers/misc/modem_if/modem_link_device_hsic.c | 31 +- drivers/misc/modem_if/modem_link_device_memory.c | 496 +++ drivers/misc/modem_if/modem_link_device_memory.h | 409 +++ drivers/misc/modem_if/modem_link_device_pld.c | 1464 ++++----- drivers/misc/modem_if/modem_link_device_pld.h | 578 ++-- .../misc/modem_if/modem_link_device_pld_ext_op.c | 277 +- drivers/misc/modem_if/modem_link_device_shmem.h | 700 +++++ drivers/misc/modem_if/modem_link_device_spi.c | 244 +- drivers/misc/modem_if/modem_link_device_spi.h | 22 +- drivers/misc/modem_if/modem_link_device_usb.c | 14 +- drivers/misc/modem_if/modem_link_pm_usb.c | 29 +- .../misc/modem_if/modem_modemctl_device_cbp72.c | 43 +- .../misc/modem_if/modem_modemctl_device_cbp82.c | 288 ++ .../misc/modem_if/modem_modemctl_device_cmc221.c | 105 +- .../misc/modem_if/modem_modemctl_device_esc6270.c | 16 +- .../misc/modem_if/modem_modemctl_device_mdm6600.c | 41 +- .../misc/modem_if/modem_modemctl_device_qsc6085.c | 218 ++ .../misc/modem_if/modem_modemctl_device_sprd8803.c | 58 +- .../misc/modem_if/modem_modemctl_device_ss222.c | 312 ++ .../misc/modem_if/modem_modemctl_device_xmm6260.c | 10 +- .../misc/modem_if/modem_modemctl_device_xmm6262.c | 90 +- drivers/misc/modem_if/modem_prj.h | 514 +-- drivers/misc/modem_if/modem_sim_slot_switch.c | 11 +- drivers/misc/modem_if/modem_utils.c | 669 ++-- drivers/misc/modem_if/modem_utils.h | 56 +- drivers/misc/modem_if/modem_variation.h | 104 +- drivers/misc/modem_if/sipc4_io_device.c | 309 +- drivers/misc/modem_if/sipc4_modem.c | 13 + drivers/misc/modem_if/sipc5_common.c | 239 ++ drivers/misc/modem_if/sipc5_io_device.c | 1559 +++++----- drivers/misc/modem_if/sipc5_modem.c | 85 +- include/linux/platform_data/modem.h | 244 +- 40 files changed, 12190 insertions(+), 5241 deletions(-) create mode 100644 drivers/misc/modem_if/modem_boot_device_spi.c create mode 100644 drivers/misc/modem_if/modem_link_device_memory.c create mode 100644 drivers/misc/modem_if/modem_link_device_memory.h create mode 100644 drivers/misc/modem_if/modem_link_device_shmem.h create mode 100644 drivers/misc/modem_if/modem_modemctl_device_cbp82.c create mode 100644 drivers/misc/modem_if/modem_modemctl_device_qsc6085.c create mode 100644 drivers/misc/modem_if/modem_modemctl_device_ss222.c create mode 100644 drivers/misc/modem_if/sipc5_common.c mode change 100644 => 100755 include/linux/platform_data/modem.h diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig index 6a6eeab..52591dc 100644 --- a/drivers/misc/modem_if/Kconfig +++ b/drivers/misc/modem_if/Kconfig @@ -24,11 +24,21 @@ config CDMA_MODEM_CBP72 depends on SEC_MODEM default n +config CDMA_MODEM_CBP82 + bool "modem chip : VIA CBP8.2" + depends on SEC_MODEM + default n + config LTE_MODEM_CMC221 bool "modem chip : SEC CMC221" depends on SEC_MODEM default n +config UMTS_MODEM_SS222 + bool "modem chip : SEC SS222" + depends on SEC_MODEM + default n + config CDMA_MODEM_MDM6600 bool "modem chip : QC MDM6600" depends on SEC_MODEM @@ -44,6 +54,11 @@ config GSM_MODEM_ESC6270 depends on SEC_MODEM default n +config CDMA_MODEM_QSC6085 + bool "modem chip : Qualcomm QSC6085" + depends on SEC_MODEM + default n + config LINK_DEVICE_MIPI bool "modem driver link device MIPI-HSI" depends on SEC_MODEM @@ -58,6 +73,7 @@ config LINK_DEVICE_PLD bool "modem driver link device PLD" depends on SEC_MODEM default n + config LINK_DEVICE_USB bool "modem driver link device USB" depends on SEC_MODEM @@ -78,18 +94,13 @@ config LINK_DEVICE_SPI depends on SEC_MODEM default n -config WORKQUEUE_FRONT - bool "IPC: SPI workqueue front" - depends on SEC_MODEM - default n - -config IPC_CMC22x_OLD_RFS - bool "IPC: CMC22x ancient RFS" +config BOOT_DEVICE_SPI + bool "modem driver boot device SPI" depends on SEC_MODEM default n -config SIPC_VER_5 - bool "IPC: Samsung IPC 5.0" +config WORKQUEUE_FRONT + bool "IPC: SPI workqueue front" depends on SEC_MODEM default n diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile index 5bd62ea..6aa28fc 100644 --- a/drivers/misc/modem_if/Makefile +++ b/drivers/misc/modem_if/Makefile @@ -2,7 +2,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if -obj-y += sipc5_modem.o sipc5_io_device.o +obj-y += sipc5_modem.o sipc5_io_device.o sipc5_common.o obj-y += sipc4_modem.o sipc4_io_device.o obj-y += modem_net_flowcontrol_device.o modem_utils.o @@ -10,17 +10,43 @@ obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o -obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o +obj-$(CONFIG_CDMA_MODEM_CBP82) += modem_modemctl_device_cbp82.o +obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o +obj-$(CONFIG_UMTS_MODEM_SS222) += modem_modemctl_device_ss222.o obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o +obj-$(CONFIG_CDMA_MODEM_QSC6085) += modem_modemctl_device_qsc6085.o obj-$(CONFIG_TDSCDMA_MODEM_SPRD8803) += modem_modemctl_device_sprd8803.o obj-$(CONFIG_LINK_DEVICE_MIPI) += modem_link_device_mipi.o -obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o -obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o +obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o +obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o obj-$(CONFIG_LINK_DEVICE_C2C) += modem_link_device_c2c.o obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o +obj-$(CONFIG_BOOT_DEVICE_SPI) += modem_boot_device_spi.o + obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o + +# Check whether or not memory-type interface +ifeq ($(CONFIG_LINK_DEVICE_DPRAM),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_PLD),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_C2C),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_SHMEM),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifdef LINK_DEVICE_MEMORY_INTERFACE +obj-y += modem_link_device_memory.o +endif diff --git a/drivers/misc/modem_if/modem_boot_device_spi.c b/drivers/misc/modem_if/modem_boot_device_spi.c new file mode 100644 index 0000000..8cee588 --- /dev/null +++ b/drivers/misc/modem_if/modem_boot_device_spi.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2011 Samsung Electronics. + * + * 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 "modem_prj.h" + +#define SPI_XMIT_DELEY 100 + +static int check_cp_status(unsigned int gpio_cp_status, unsigned int count) +{ + int ret = 0; + int cnt = 0; + + while (1) { + if (gpio_get_value(gpio_cp_status) != 0) { + ret = 0; + break; + } + + cnt++; + if (cnt >= count) { + mif_err("ERR! gpio_cp_status == 0 (cnt %d)\n", cnt); + ret = -EFAULT; + break; + } + + msleep(20); + } + + return ret; +} + +static inline int spi_send(struct modem_boot_spi *loader, const char val) +{ + char buff[1]; + int ret; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .tx_buf = buff, + }; + + buff[0] = val; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(loader->spi_dev, &msg); + if (ret < 0) + mif_err("ERR! spi_sync fail (err %d)\n", ret); + + return ret; +} + +static int spi_boot_write(struct modem_boot_spi *loader, const char *addr, + const long len) +{ + int i; + int ret = 0; + char *buff = NULL; + mif_err("+++\n"); + + buff = kzalloc(len, GFP_KERNEL); + if (!buff) { + mif_err("ERR! kzalloc(%ld) fail\n", len); + ret = -ENOMEM; + goto exit; + } + + ret = copy_from_user(buff, (const void __user *)addr, len); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit; + } + + for (i = 0 ; i < len ; i++) { + ret = spi_send(loader, buff[i]); + if (ret < 0) { + mif_err("ERR! spi_send fail (err %d)\n", ret); + goto exit; + } + } + +exit: + if (buff) + kfree(buff); + + mif_err("---\n"); + return ret; +} + +static int spi_boot_open(struct inode *inode, struct file *filp) +{ + struct modem_boot_spi *loader = to_modem_boot_spi(filp->private_data); + filp->private_data = loader; + return 0; +} + +static long spi_boot_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct modem_firmware img; + struct modem_boot_spi *loader = filp->private_data; + + mutex_lock(&loader->lock); + switch (cmd) { + case IOCTL_MODEM_XMIT_BOOT: + ret = copy_from_user(&img, (const void __user *)arg, + sizeof(struct modem_firmware)); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit_err; + } + mif_info("IOCTL_MODEM_XMIT_BOOT (size %d)\n", img.size); + + ret = spi_boot_write(loader, img.binary, img.size); + if (ret < 0) { + mif_err("ERR! spi_boot_write fail (err %d)\n", ret); + break; + } + + if (!loader->gpio_cp_status) + break; + + ret = check_cp_status(loader->gpio_cp_status, 100); + if (ret < 0) + mif_err("ERR! check_cp_status fail (err %d)\n", ret); + + break; + + default: + mif_err("ioctl cmd error\n"); + ret = -ENOIOCTLCMD; + + break; + } + mutex_unlock(&loader->lock); + +exit_err: + return ret; +} + +static const struct file_operations modem_spi_boot_fops = { + .owner = THIS_MODULE, + .open = spi_boot_open, + .unlocked_ioctl = spi_boot_ioctl, +}; + +static int __devinit modem_spi_boot_probe(struct spi_device *spi) +{ + int ret; + struct modem_boot_spi *loader; + struct modem_boot_spi_platform_data *pdata; + mif_debug("+++\n"); + + loader = kzalloc(sizeof(*loader), GFP_KERNEL); + if (!loader) { + mif_err("failed to allocate for modem_boot_spi\n"); + ret = -ENOMEM; + goto err_alloc; + } + mutex_init(&loader->lock); + + spi->bits_per_word = 8; + + if (spi_setup(spi)) { + mif_err("ERR! spi_setup fail\n"); + ret = -EINVAL; + goto err_setup; + } + loader->spi_dev = spi; + + if (!spi->dev.platform_data) { + mif_err("ERR! no platform_data\n"); + ret = -EINVAL; + goto err_setup; + } + pdata = (struct modem_boot_spi_platform_data *)spi->dev.platform_data; + + loader->gpio_cp_status = pdata->gpio_cp_status; + + spi_set_drvdata(spi, loader); + + loader->dev.minor = MISC_DYNAMIC_MINOR; + loader->dev.name = MODEM_BOOT_DEV_SPI; + loader->dev.fops = &modem_spi_boot_fops; + ret = misc_register(&loader->dev); + if (ret) { + mif_err("ERR! misc_register fail (err %d)\n", ret); + goto err_setup; + } + + mif_debug("---\n"); + return 0; + +err_setup: + mutex_destroy(&loader->lock); + kfree(loader); + +err_alloc: + mif_err("xxx\n"); + return ret; +} + +static int __devexit modem_spi_boot_remove(struct spi_device *spi) +{ + struct modem_boot_spi *loader = spi_get_drvdata(spi); + + misc_deregister(&loader->dev); + mutex_destroy(&loader->lock); + kfree(loader); + + return 0; +} + +static struct spi_driver modem_boot_device_spi_driver = { + .driver = { + .name = MODEM_BOOT_DEV_SPI, + .owner = THIS_MODULE, + }, + .probe = modem_spi_boot_probe, + .remove = __devexit_p(modem_spi_boot_remove), +}; + +static int __init modem_boot_device_spi_init(void) +{ + int err; + + err = spi_register_driver(&modem_boot_device_spi_driver); + if (err) { + mif_err("spi_register_driver fail (err %d)\n", err); + return err; + } + + return 0; +} + +static void __exit modem_boot_device_spi_exit(void) +{ + spi_unregister_driver(&modem_boot_device_spi_driver); +} + +module_init(modem_boot_device_spi_init); +module_exit(modem_boot_device_spi_exit); + +MODULE_DESCRIPTION("SPI Driver for Downloading Modem Bootloader"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/modem_if/modem_link_device_c2c.c b/drivers/misc/modem_if/modem_link_device_c2c.c index acbaadf..15665f7 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.c +++ b/drivers/misc/modem_if/modem_link_device_c2c.c @@ -1,6 +1,4 @@ -/* /linux/drivers/new_modem_if/link_dev_c2c.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -14,9 +12,9 @@ * */ -#include #include #include +#include #include #include #include @@ -24,38 +22,2279 @@ #include #include #include -#include #include #include +#include +#include +#ifdef CONFIG_FB +#include +#endif +#include +#include +#include +#include #include -#include #include "modem_prj.h" +#include "modem_utils.h" #include "modem_link_device_c2c.h" -struct link_device *c2c_create_link_device(struct platform_device *pdev) +static void trigger_forced_cp_crash(struct shmem_link_device *shmd); + +#ifdef DEBUG_MODEM_IF +static void save_mem_dump(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + char *path = shmd->dump_path; + struct file *fp; + struct utc_time t; + + get_utc_time(&t); + snprintf(path, MIF_MAX_PATH_LEN, "%s/%s_%d%02d%02d_%02d%02d%02d.dump", + MIF_LOG_DIR, ld->name, t.year, t.mon, t.day, t.hour, t.min, + t.sec); + + fp = mif_open_file(path); + if (!fp) { + mif_err("%s: ERR! %s open fail\n", ld->name, path); + return; + } + mif_err("%s: %s opened\n", ld->name, path); + + mif_save_file(fp, shmd->base, shmd->size); + + mif_close_file(fp); +} + +/** + * mem_dump_work + * @ws: pointer to an instance of work_struct structure + * + * Performs actual file operation for saving a DPRAM dump. + */ +static void mem_dump_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + + shmd = container_of(ws, struct shmem_link_device, dump_dwork.work); + if (!shmd) { + mif_err("ERR! no shmd\n"); + return; + } + + save_mem_dump(shmd); +} +#endif + +static void print_pm_status(struct shmem_link_device *shmd, int level) +{ +#ifdef DEBUG_MODEM_IF + struct utc_time t; + u32 magic; + int ap_wakeup; + int ap_status; + int cp_wakeup; + int cp_status; + + if (level < 0) + return; + + get_utc_time(&t); + magic = get_magic(shmd); + ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + ap_status = gpio_get_value(shmd->gpio_ap_status); + cp_wakeup = gpio_get_value(shmd->gpio_cp_wakeup); + cp_status = gpio_get_value(shmd->gpio_cp_status); + + /* + ** PM {ap_wakeup:cp_wakeup:cp_status:ap_status:magic:state} CALLER + */ + if (level > 0) { + pr_err(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " + "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, + cp_wakeup, cp_status, ap_status, magic, CALLER); + } else { + pr_info(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " + "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, + cp_wakeup, cp_status, ap_status, magic, CALLER); + } +#endif +} + +static inline void s5p_change_irq_type(int irq, int value) +{ + irq_set_irq_type(irq, value ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH); + /** + * Exynos BSP has a problem when using level-triggering interrupt. + * If the irq type is changed in an interrupt handler, the handler will + * be called again. + * Below is a temporary solution until SYS.LSI resolves this problem. + */ + __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); +} + +/** + * recv_int2ap + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the CP-to-AP interrupt register. + */ +static inline u16 recv_int2ap(struct shmem_link_device *shmd) +{ + if (shmd->type == C2C_SHMEM) + return (u16)c2c_read_interrupt(); + + if (shmd->mbx2ap) + return *shmd->mbx2ap; + + return 0; +} + +/** + * send_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * @mask: value to be written to the AP-to-CP interrupt register + */ +static inline void send_int2cp(struct shmem_link_device *shmd, u16 mask) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode != LINK_MODE_IPC) + mif_info("%s: mask = 0x%04X\n", ld->name, CALLER, mask); + + if (shmd->type == C2C_SHMEM) + c2c_send_interrupt(mask); + + if (shmd->mbx2cp) + *shmd->mbx2cp = mask; +} + +/** + * get_shmem_status + * @shmd: pointer to an instance of shmem_link_device structure + * @dir: direction of communication (TX or RX) + * @mst: pointer to an instance of mem_status structure + * + * Takes a snapshot of the current status of a SHMEM. + */ +static void get_shmem_status(struct shmem_link_device *shmd, + enum circ_dir_type dir, struct mem_status *mst) +{ +#ifdef DEBUG_MODEM_IF + getnstimeofday(&mst->ts); +#endif + + mst->dir = dir; + mst->magic = get_magic(shmd); + mst->access = get_access(shmd); + mst->head[IPC_FMT][TX] = get_txq_head(shmd, IPC_FMT); + mst->tail[IPC_FMT][TX] = get_txq_tail(shmd, IPC_FMT); + mst->head[IPC_FMT][RX] = get_rxq_head(shmd, IPC_FMT); + mst->tail[IPC_FMT][RX] = get_rxq_tail(shmd, IPC_FMT); + mst->head[IPC_RAW][TX] = get_txq_head(shmd, IPC_RAW); + mst->tail[IPC_RAW][TX] = get_txq_tail(shmd, IPC_RAW); + mst->head[IPC_RAW][RX] = get_rxq_head(shmd, IPC_RAW); + mst->tail[IPC_RAW][RX] = get_rxq_tail(shmd, IPC_RAW); +} + +static inline int check_link_status(struct shmem_link_device *shmd) +{ + u32 magic = get_magic(shmd); + int cnt; + + if (gpio_get_value(shmd->gpio_cp_status) != 0 && magic == SHM_IPC_MAGIC) + return 0; + + cnt = 0; + while (gpio_get_value(shmd->gpio_cp_status) == 0) { + if (gpio_get_value(shmd->gpio_ap_status) == 0) { + print_pm_status(shmd, 1); + gpio_set_value(shmd->gpio_ap_status, 1); + } + + cnt++; + if (cnt >= 100) { + mif_err("ERR! cp_status != 1 (cnt %d)\n", cnt); + return -EACCES; + } + + if (in_interrupt()) + udelay(100); + else + usleep_range(100, 200); + } + + cnt = 0; + while (1) { + magic = get_magic(shmd); + if (magic == SHM_IPC_MAGIC) + break; + + cnt++; + if (cnt >= 100) { + mif_err("ERR! magic 0x%X != SHM_IPC_MAGIC (cnt %d)\n", + magic, cnt); + return -EACCES; + } + + if (in_interrupt()) + udelay(100); + else + usleep_range(100, 200); + } + + return 0; +} + +static void release_cp_wakeup(struct work_struct *ws) { - struct c2c_link_device *dpld; + struct shmem_link_device *shmd; struct link_device *ld; - struct modem_data *pdata; + int i; + unsigned long flags; - pdata = pdev->dev.platform_data; + shmd = container_of(ws, struct shmem_link_device, cp_sleep_dwork.work); - dpld = kzalloc(sizeof(struct c2c_link_device), GFP_KERNEL); - if (!dpld) { - mif_err("dpld == NULL\n"); - return NULL; + spin_lock_irqsave(&shmd->pm_lock, flags); + i = atomic_read(&shmd->ref_cnt); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + if (i > 0) + goto reschedule; + + /* + * If there is any IPC message remained in a TXQ, AP must prevent CP + * from going to sleep. + */ + ld = &shmd->ld; + for (i = 0; i < ld->max_ipc_dev; i++) { + if (ld->skb_txq[i]->qlen > 0) + goto reschedule; } - wake_lock_init(&dpld->c2c_wake_lock, WAKE_LOCK_SUSPEND, "c2c_wakelock"); - wake_lock(&dpld->c2c_wake_lock); + if (gpio_get_value(shmd->gpio_ap_wakeup)) + goto reschedule; - ld = &dpld->ld; - dpld->pdata = pdata; + gpio_set_value(shmd->gpio_cp_wakeup, 0); + print_pm_status(shmd, 1); + return; - ld->name = "c2c"; +reschedule: + if (!work_pending(&shmd->cp_sleep_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } +} + +static void release_ap_status(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + int i; + unsigned long flags; - mif_info("%s is created!!!\n", dpld->ld.name); + shmd = container_of(ws, struct shmem_link_device, link_off_dwork.work); - return ld; + spin_lock_irqsave(&shmd->pm_lock, flags); + i = atomic_read(&shmd->ref_cnt); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + if (i > 0) + goto reschedule; + + if (gpio_get_value(shmd->gpio_cp_status)) + goto reschedule; + + gpio_set_value(shmd->gpio_ap_status, 0); + print_pm_status(shmd, 1); + + if (wake_lock_active(&shmd->cp_wlock)) + wake_unlock(&shmd->cp_wlock); + + return; + +reschedule: + if (!work_pending(&shmd->link_off_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, + msecs_to_jiffies(100)); + } +} + +/** + * forbid_cp_sleep + * @shmd: pointer to an instance of shmem_link_device structure + * + * Wakes up a CP if it can sleep and increases the "ref_cnt" counter in the + * shmem_link_device instance. + * + * CAUTION!!! permit_cp_sleep() MUST be invoked after forbid_cp_sleep() success + * to decrease the "ref_cnt" counter. + */ +static int forbid_cp_sleep(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + int err = 0; + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + atomic_inc(&shmd->ref_cnt); + if (gpio_get_value(shmd->gpio_ap_status) == 0) { + gpio_set_value(shmd->gpio_ap_status, 1); + print_pm_status(shmd, 1); + } + spin_unlock_irqrestore(&shmd->pm_lock, flags); + + if (work_pending(&shmd->cp_sleep_dwork.work)) + cancel_delayed_work(&shmd->cp_sleep_dwork); + + if (work_pending(&shmd->link_off_dwork.work)) + cancel_delayed_work(&shmd->link_off_dwork); + + if (gpio_get_value(shmd->gpio_cp_wakeup) == 0) { + gpio_set_value(shmd->gpio_cp_wakeup, 1); + print_pm_status(shmd, 1); + } + + if (check_link_status(shmd) < 0) { + print_pm_status(shmd, 1); + mif_err("%s: ERR! check_link_status fail\n", ld->name); + err = -EACCES; + goto exit; + } + +exit: + return err; +} + +/** + * permit_cp_sleep + * @shmd: pointer to an instance of shmem_link_device structure + * + * Decreases the "ref_cnt" counter in the shmem_link_device instance if it can + * sleep and allows a CP to sleep only if the value of "ref_cnt" counter is + * less than or equal to 0. + * + * MUST be invoked after forbid_cp_sleep() success to decrease the "ref_cnt" + * counter. + */ +static void permit_cp_sleep(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + + if (atomic_dec_return(&shmd->ref_cnt) > 0) { + spin_unlock_irqrestore(&shmd->pm_lock, flags); + return; + } + + atomic_set(&shmd->ref_cnt, 0); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + + /* Hold gpio_cp_wakeup for CP_WAKEUP_HOLD_TIME until CP finishes RX */ + if (!work_pending(&shmd->cp_sleep_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } +} + +/** + * ap_wakeup_handler: interrupt handler for a wakeup interrupt + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ap_wakeup_handler(int irq, void *data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + int ap_status = gpio_get_value(shmd->gpio_ap_status); + + s5p_change_irq_type(irq, ap_wakeup); + + if (ld->mode != LINK_MODE_IPC) + goto exit; + + if (work_pending(&shmd->cp_sleep_dwork.work)) + __cancel_delayed_work(&shmd->cp_sleep_dwork); + + print_pm_status(shmd, 1); + + if (ap_wakeup) { + if (work_pending(&shmd->link_off_dwork.work)) + __cancel_delayed_work(&shmd->link_off_dwork); + + if (!wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->ap_wlock); + + if (!c2c_suspended() && !ap_status) + gpio_set_value(shmd->gpio_ap_status, 1); + } else { + if (wake_lock_active(&shmd->ap_wlock)) + wake_unlock(&shmd->ap_wlock); + + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } + +exit: + return IRQ_HANDLED; +} + +static irqreturn_t cp_status_handler(int irq, void *data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + int cp_status = gpio_get_value(shmd->gpio_cp_status); + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + + s5p_change_irq_type(irq, cp_status); + + if (ld->mode != LINK_MODE_IPC) + goto exit; + + if (work_pending(&shmd->link_off_dwork.work)) + __cancel_delayed_work(&shmd->link_off_dwork); + + print_pm_status(shmd, 1); + + if (cp_status) { + if (!wake_lock_active(&shmd->cp_wlock)) + wake_lock(&shmd->cp_wlock); + } else { + if (atomic_read(&shmd->ref_cnt) > 0) { + queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, + msecs_to_jiffies(10)); + } else { + gpio_set_value(shmd->gpio_ap_status, 0); + if (wake_lock_active(&shmd->cp_wlock)) + wake_unlock(&shmd->cp_wlock); + } + } + +exit: + spin_unlock_irqrestore(&shmd->pm_lock, flags); + return IRQ_HANDLED; +} + +#if 1 +/* Functions for IPC/BOOT/DUMP RX */ +#endif + +/** + * handle_cp_crash + * @shmd: pointer to an instance of shmem_link_device structure + * + * Actual handler for the CRASH_EXIT command from a CP. + */ +static void handle_cp_crash(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod; + int i; + + if (shmd->forced_cp_crash) + shmd->forced_cp_crash = false; + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < MAX_SIPC5_DEV; i++) + skb_queue_purge(ld->skb_txq[i]); + + /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + /* time margin for taking state changes by rild */ + mdelay(100); + + /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ + iod = link_get_iod_with_format(ld, IPC_BOOT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); +} + +/** + * handle_no_cp_crash_ack + * @arg: pointer to an instance of shmem_link_device structure + * + * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no + * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. + */ +static void handle_no_cp_crash_ack(unsigned long arg) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)arg; + struct link_device *ld = &shmd->ld; + + mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name); + + handle_cp_crash(shmd); +} + +/** + * trigger_forced_cp_crash + * @shmd: pointer to an instance of shmem_link_device structure + * + * Triggers an enforced CP crash. + */ +static void trigger_forced_cp_crash(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct utc_time t; + + if (ld->mode == LINK_MODE_ULOAD) { + mif_err("%s: ALREADY in progress\n", ld->name, CALLER); + return; + } + ld->mode = LINK_MODE_ULOAD; + shmd->forced_cp_crash = true; + + get_utc_time(&t); + mif_err("%s: [%02d:%02d:%02d.%03d] \n", + ld->name, t.hour, t.min, t.sec, t.msec, CALLER); + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + +#ifdef DEBUG_MODEM_IF + if (in_interrupt()) + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); + else + save_mem_dump(shmd); +#endif + + /* Send CRASH_EXIT command to a CP */ + send_int2cp(shmd, INT_CMD(INT_CMD_CRASH_EXIT)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + + /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack() will be executed. */ + mif_add_timer(&shmd->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack, (unsigned long)shmd); + + return; +} + +/** + * cmd_crash_reset_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the CRASH_RESET command from a CP. + */ +static void cmd_crash_reset_handler(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod = NULL; + int i; + struct utc_time t; + + ld->mode = LINK_MODE_ULOAD; + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + + get_utc_time(&t); + mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC7 (CRASH_RESET)\n", + ld->name, t.hour, t.min, t.sec, t.msec); +#ifdef DEBUG_MODEM_IF + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); +#endif + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < MAX_SIPC5_DEV; i++) + skb_queue_purge(ld->skb_txq[i]); + + /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_RESET); + + /* time margin for taking state changes by rild */ + mdelay(100); + + /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ + iod = link_get_iod_with_format(ld, IPC_BOOT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_RESET); +} + +/** + * cmd_crash_exit_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the CRASH_EXIT command from a CP. + */ +static void cmd_crash_exit_handler(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct utc_time t; + + ld->mode = LINK_MODE_ULOAD; + + del_timer(&shmd->crash_ack_timer); + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + + get_utc_time(&t); + mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC9 (CRASH_EXIT)\n", + ld->name, t.hour, t.min, t.sec, t.msec); +#ifdef DEBUG_MODEM_IF + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); +#endif + + handle_cp_crash(shmd); +} + +/** + * cmd_phone_start_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the PHONE_START command from a CP. + */ +static void cmd_phone_start_handler(struct shmem_link_device *shmd) +{ + int err; + struct link_device *ld = &shmd->ld; + struct io_device *iod; + int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + int cp_status = gpio_get_value(shmd->gpio_cp_status); + + mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("%s: ERR! no iod\n", ld->name); + return; + } + + err = init_shmem_ipc(shmd); + if (err) + return; + + if (wake_lock_active(&shmd->wlock)) + wake_unlock(&shmd->wlock); + + s5p_change_irq_type(shmd->irq_ap_wakeup, ap_wakeup); + if (ap_wakeup && !wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->ap_wlock); + + s5p_change_irq_type(shmd->irq_cp_status, cp_status); + if (cp_status && !wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->cp_wlock); + + ld->mode = LINK_MODE_IPC; + iod->modem_state_changed(iod, STATE_ONLINE); +} + +/** + * cmd_handler: processes a SHMEM command from a CP + * @shmd: pointer to an instance of shmem_link_device structure + * @cmd: SHMEM command from a CP + */ +static void cmd_handler(struct shmem_link_device *shmd, u16 cmd) +{ + struct link_device *ld = &shmd->ld; + + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_CRASH_RESET: + cmd_crash_reset_handler(shmd); + break; + + case INT_CMD_CRASH_EXIT: + cmd_crash_exit_handler(shmd); + break; + + case INT_CMD_PHONE_START: + cmd_phone_start_handler(shmd); + complete_all(&ld->init_cmpl); + break; + + default: + mif_err("%s: unknown command 0x%04X\n", ld->name, cmd); + break; + } +} + +/** + * ipc_rx_work + * @ws: pointer to an instance of work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void ipc_rx_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + struct io_device *iod; + struct sk_buff *skb; + int i; + + shmd = container_of(ws, struct shmem_link_device, ipc_rx_dwork.work); + ld = &shmd->ld; + + for (i = 0; i < MAX_SIPC5_DEV; i++) { + iod = shmd->iod[i]; + while (1) { + skb = skb_dequeue(ld->skb_rxq[i]); + if (!skb) + break; + iod->recv_skb(iod, ld, skb); + } + } +} + +/** + * rx_ipc_frames + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_ipc_frames(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *rxq = ld->skb_rxq[dev]; + struct sk_buff *skb; + int ret; + /** + * variables for the status of the circular queue + */ + u8 *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int out; /* index to the start of current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + + src = circ->buff; + qsize = circ->qsize; + out = circ->out; + rcvd = circ->size; + + rest = rcvd; + tot = 0; + while (rest > 0) { + /* Copy the header in the frame to the header buffer */ + circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + mif_err("%s: ERR! %s INVALID config 0x%02X " + "(rcvd %d, rest %d)\n", ld->name, + get_dev_name(dev), hdr[0], rcvd, rest); + ret = -EBADMSG; + goto exit; + } + + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", + ld->name, get_dev_name(dev), tot, rest, rcvd); + ret = -EBADMSG; + goto exit; + } + + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + ret = -ENOMEM; + goto exit; + } + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + circ_read(dst, src, qsize, out, tot); + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(rxq, skb); + + /* Calculate new out value */ + rest -= tot; + out += tot; + if (unlikely(out >= qsize)) + out -= qsize; + } + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(shmd, dev, circ->in); + + return rcvd; + +exit: +#ifdef DEBUG_MODEM_IF + mif_err("%s: ERR! rcvd:%d tot:%d rest:%d\n", ld->name, rcvd, tot, rest); + pr_ipc(1, "c2c: ERR! CP2MIF", (src + out), (rest > 20) ? 20 : rest); +#endif + + return ret; +} + +/** + * msg_handler: receives IPC messages from every RXQ + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every IPC RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void msg_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ + struct link_device *ld = &shmd->ld; + struct circ_status circ; + int i = 0; + int ret = 0; + + if (!ipc_active(shmd)) + return; + + /* Read data from every RXQ */ + for (i = 0; i < MAX_SIPC5_DEV; i++) { + /* Invoke an RX function only when there is data in the RXQ */ + if (likely(mst->head[i][RX] != mst->tail[i][RX])) { + ret = get_rxq_rcvd(shmd, i, mst, &circ); + if (unlikely(ret < 0)) { + mif_err("%s: ERR! get_rxq_rcvd fail (err %d)\n", + ld->name, ret); +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + return; + } + + ret = rx_ipc_frames(shmd, i, &circ); + if (ret < 0) { +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + reset_rxq_circ(shmd, i); + } + } + } + + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &shmd->ipc_rx_dwork, 0); } + +static void msg_rx_task(unsigned long data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = &shmd->ld; + struct mem_status mst; + u16 mask = 0; + + get_shmem_status(shmd, RX, &mst); + + if ((mst.head[IPC_FMT][RX] != mst.tail[IPC_FMT][RX]) + || (mst.head[IPC_RAW][RX] != mst.tail[IPC_RAW][RX])) { +#if 0 + print_mem_status(ld, &mst); +#endif + msg_handler(shmd, &mst); + } + + /* + ** Check and process REQ_ACK (at this time, in == out) + */ + if (unlikely(shmd->dev[IPC_FMT]->req_ack_rcvd)) { + mask |= get_mask_res_ack(shmd, IPC_FMT); + shmd->dev[IPC_FMT]->req_ack_rcvd = 0; + } + + if (unlikely(shmd->dev[IPC_RAW]->req_ack_rcvd)) { + mask |= get_mask_res_ack(shmd, IPC_RAW); + shmd->dev[IPC_RAW]->req_ack_rcvd = 0; + } + + if (unlikely(mask)) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: send RES_ACK 0x%04X\n", ld->name, mask); +#endif + send_int2cp(shmd, INT_NON_CMD(mask)); + } +} + +/** + * ipc_handler: processes a SHMEM command or receives IPC messages + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * Invokes cmd_handler for a SHMEM command or msg_handler for IPC messages. + */ +static void ipc_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ +#ifdef DEBUG_MODEM_IF + struct link_device *ld = &shmd->ld; +#endif + u16 intr = mst->int2ap; + + if (unlikely(INT_CMD_VALID(intr))) { + cmd_handler(shmd, intr); + return; + } + + /* + ** Check REQ_ACK from CP -> REQ_ACK will be processed in the RX tasklet + */ + if (unlikely(intr & get_mask_req_ack(shmd, IPC_FMT))) + shmd->dev[IPC_FMT]->req_ack_rcvd = 1; + + if (unlikely(intr & get_mask_req_ack(shmd, IPC_RAW))) + shmd->dev[IPC_RAW]->req_ack_rcvd = 1; + + /* + ** Check and process RES_ACK from CP + */ + if (unlikely(intr & get_mask_res_ack(shmd, IPC_FMT))) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv FMT RES_ACK\n", ld->name); +#endif + complete(&shmd->req_ack_cmpl[IPC_FMT]); + } + + if (unlikely(intr & get_mask_res_ack(shmd, IPC_RAW))) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv RAW RES_ACK\n", ld->name); +#endif + complete(&shmd->req_ack_cmpl[IPC_RAW]); + } + + /* + ** Schedule RX tasklet + */ + tasklet_hi_schedule(&shmd->rx_tsk); +} + +/** + * rx_udl_frames + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_udl_frames(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod; + struct sk_buff *skb; + int ret; + /** + * variables for the status of the circular queue + */ + u8 *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int out; /* index to the start of current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + + src = circ->buff; + qsize = circ->qsize; + out = circ->out; + rcvd = circ->size; + + rest = rcvd; + tot = 0; + while (rest > 0) { + /* Copy the header in the frame to the header buffer */ + circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + mif_err("%s: ERR! %s INVALID config 0x%02X " + "(rest %d, rcvd %d)\n", ld->name, + get_dev_name(dev), hdr[0], rest, rcvd); + pr_ipc(1, "UDL", (src + out), (rest > 20) ? 20 : rest); + ret = -EBADMSG; + goto exit; + } + + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", + ld->name, get_dev_name(dev), tot, rest, rcvd); + ret = -ENODATA; + goto exit; + } + + /* Allocate an skb */ + skb = alloc_skb(tot + NET_SKB_PAD, GFP_KERNEL); + if (!skb) { + mif_err("%s: ERR! %s alloc_skb fail\n", + ld->name, get_dev_name(dev)); + ret = -ENOMEM; + goto exit; + } + skb_reserve(skb, NET_SKB_PAD); + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + circ_read(dst, src, qsize, out, tot); + + /* Pass the skb to an iod */ + iod = link_get_iod_with_channel(ld, sipc5_get_ch_id(skb->data)); + if (!iod) { + mif_err("%s: ERR! no IPC_BOOT iod\n", ld->name); + break; + } + +#ifdef DEBUG_MODEM_IF + if (!std_udl_with_payload(std_udl_get_cmd(skb->data))) { + if (ld->mode == LINK_MODE_DLOAD) { + pr_ipc(0, "[CP->AP] DL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } else { + pr_ipc(0, "[CP->AP] UL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } + } +#endif + + iod->recv_skb(iod, ld, skb); + + /* Calculate new out value */ + rest -= tot; + out += tot; + if (unlikely(out >= qsize)) + out -= qsize; + } + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(shmd, dev, circ->in); + + return rcvd; + +exit: + return ret; +} + +/** + * udl_rx_work + * @ws: pointer to an instance of the work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void udl_rx_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + struct sk_buff_head *rxq; + struct mem_status mst; + struct circ_status circ; + int dev = IPC_RAW; + + shmd = container_of(ws, struct shmem_link_device, udl_rx_dwork.work); + ld = &shmd->ld; + rxq = ld->skb_rxq[dev]; + + while (1) { + get_shmem_status(shmd, RX, &mst); + if (mst.head[dev][RX] == mst.tail[dev][RX]) + break; + + /* Invoke an RX function only when there is data in the RXQ */ + if (get_rxq_rcvd(shmd, dev, &mst, &circ) < 0) { + mif_err("%s: ERR! get_rxq_rcvd fail\n", ld->name); +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + break; + } + + if (rx_udl_frames(shmd, dev, &circ) < 0) { + skb_queue_purge(rxq); + break; + } + } +} + +/** + * udl_handler: receives BOOT/DUMP IPC messages from every RXQ + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every IPC RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void udl_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ + u16 intr = mst->int2ap; + + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &shmd->udl_rx_dwork, 0); + + /* Check and process RES_ACK */ + if (intr & INT_MASK_RES_ACK_SET) { + if (intr & get_mask_res_ack(shmd, IPC_RAW)) { +#ifdef DEBUG_MODEM_IF + struct link_device *ld = &shmd->ld; + mif_info("%s: recv RAW RES_ACK\n", ld->name); + print_circ_status(ld, IPC_RAW, mst); +#endif + complete(&shmd->req_ack_cmpl[IPC_RAW]); + } + } +} + +/** + * c2c_irq_handler: interrupt handler for a C2C interrupt + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + * + * Flow for normal interrupt handling: + * c2c_irq_handler -> udl_handler + * c2c_irq_handler -> ipc_handler -> cmd_handler -> cmd_xxx_handler + * c2c_irq_handler -> ipc_handler -> msg_handler -> rx_ipc_frames -> ... + */ +static void c2c_irq_handler(void *data, u32 intr) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + struct mem_status *mst = msq_get_free_slot(&shmd->stat_list); + + get_shmem_status(shmd, RX, mst); + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { + mif_info("%s: ld->mode == LINK_MODE_OFFLINE\n", ld->name); + return; + } + + if (unlikely(!INT_VALID(intr))) { + mif_info("%s: ERR! invalid intr 0x%X\n", ld->name, intr); + return; + } + + mst->int2ap = intr; + + if (ld->mode == LINK_MODE_DLOAD || ld->mode == LINK_MODE_ULOAD) + udl_handler(shmd, mst); + else + ipc_handler(shmd, mst); +} + +#if 1 +/* Functions for IPC/BOOT/DUMP TX */ +#endif + +/** + * write_ipc_to_txq + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @circ: pointer to an instance of circ_status structure + * @skb: pointer to an instance of sk_buff structure + * + * Must be invoked only when there is enough space in the TXQ. + */ +static void write_ipc_to_txq(struct shmem_link_device *shmd, int dev, + struct circ_status *circ, struct sk_buff *skb) +{ + u32 qsize = circ->qsize; + u32 in = circ->in; + u8 *buff = circ->buff; + u8 *src = skb->data; + u32 len = skb->len; +#ifdef DEBUG_MODEM_IF + struct io_device *iod = skbpriv(skb)->iod; + struct modem_ctl *mc = shmd->ld.mc; + char tag[MIF_MAX_STR_LEN]; + + snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", iod->name, mc->name); + + if (dev == IPC_FMT) + pr_ipc(1, tag, src, (len > 20 ? 20 : len)); +#if 0 + if (dev == IPC_RAW) + pr_ipc(0, tag, src, (len > 20 ? 20 : len)); +#endif +#endif + + /* Write data to the TXQ */ + circ_write(buff, src, qsize, in, len); + + /* Update new head (in) pointer */ + set_txq_head(shmd, dev, circ_new_pointer(qsize, in, len)); +} + +/** + * xmit_ipc_msg + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. + * + * Returns total length of IPC messages transmitted or an error code. + */ +static int xmit_ipc_msg(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long flags; + struct circ_status circ; + int space; + int copied = 0; + + /* Acquire the spin lock for a TXQ */ + spin_lock_irqsave(&shmd->tx_lock[dev], flags); + + while (1) { + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (unlikely(space < 0)) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(shmd); +#endif + /* Empty out the TXQ */ + reset_txq_circ(shmd, dev); + copied = -EIO; + break; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + /* Check the free space size comparing with skb->len */ + if (unlikely(space < skb->len)) { +#ifdef DEBUG_MODEM_IF + struct mem_status mst; +#endif + /* Set res_required flag for the "dev" */ + atomic_set(&shmd->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_err("%s: NOSPC in %s_TXQ" + "{qsize:%u in:%u out:%u} free:%u < len:%u\n", + ld->name, CALLER, get_dev_name(dev), + circ.qsize, circ.in, circ.out, space, skb->len); +#ifdef DEBUG_MODEM_IF + get_shmem_status(shmd, TX, &mst); + print_circ_status(ld, dev, &mst); +#endif + copied = -ENOSPC; + break; + } + +#ifdef DEBUG_MODEM_IF + if (!ipc_active(shmd)) { + if (get_magic(shmd) == SHM_PM_MAGIC) { + mif_err("%s: Going to SLEEP\n", ld->name); + copied = -EBUSY; + } else { + mif_err("%s: IPC is NOT active\n", ld->name); + copied = -EIO; + } + break; + } +#endif + + /* TX only when there is enough space in the TXQ */ + write_ipc_to_txq(shmd, dev, &circ, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + /* Release the spin lock */ + spin_unlock_irqrestore(&shmd->tx_lock[dev], flags); + + return copied; +} + +/** + * wait_for_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Sends an REQ_ACK interrupt for @dev to CP. + * 2) Waits for the corresponding RES_ACK for @dev from CP. + * + * Returns the return value from wait_for_completion_interruptible_timeout(). + */ +static int wait_for_res_ack(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct completion *cmpl = &shmd->req_ack_cmpl[dev]; + unsigned long timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); + int ret; + u16 mask; + +#ifdef DEBUG_MODEM_IF + mif_info("%s: send %s REQ_ACK\n", ld->name, get_dev_name(dev)); +#endif + + mask = get_mask_req_ack(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + + /* ret < 0 if interrupted, ret == 0 on timeout */ + ret = wait_for_completion_interruptible_timeout(cmpl, timeout); + if (ret < 0) { + mif_err("%s: %s: wait_for_completion interrupted! (ret %d)\n", + ld->name, get_dev_name(dev), ret); + goto exit; + } + + if (ret == 0) { + struct mem_status mst; + get_shmem_status(shmd, TX, &mst); + + mif_err("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", + ld->name, get_dev_name(dev)); + + /* + ** The TXQ must be checked whether or not it is empty, because + ** an interrupt mask can be overwritten by the next interrupt. + */ + if (mst.head[dev][TX] == mst.tail[dev][TX]) { + ret = get_txq_buff_size(shmd, dev); +#ifdef DEBUG_MODEM_IF + mif_err("%s: %s_TXQ has been emptied\n", + ld->name, get_dev_name(dev)); + print_circ_status(ld, dev, &mst); +#endif + } + } + +exit: + return ret; +} + +/** + * process_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Restarts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + * + * Returns the return value from xmit_ipc_msg(). + */ +static int process_res_ack(struct shmem_link_device *shmd, int dev) +{ + int ret; + u16 mask; + + ret = xmit_ipc_msg(shmd, dev); + if (ret > 0) { + mask = get_mask_send(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + } + + if (ret >= 0) + atomic_set(&shmd->res_required[dev], 0); + + return ret; +} + +/** + * fmt_tx_work: performs TX for FMT IPC device under SHMEM flow control + * @ws: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of FMT IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts SHMEM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. + */ +static void fmt_tx_work(struct work_struct *ws) +{ + struct link_device *ld; + struct shmem_link_device *shmd; + int ret; + + ld = container_of(ws, struct link_device, fmt_tx_dwork.work); + shmd = to_shmem_link_device(ld); + + ret = wait_for_res_ack(shmd, IPC_FMT); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); + return; + } + + ret = process_res_ack(shmd, IPC_FMT); + if (ret >= 0) { + permit_cp_sleep(shmd); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], + msecs_to_jiffies(1)); + } +} + +/** + * raw_tx_work: performs TX for RAW IPC device under SHMEM flow control. + * @ws: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RAW IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts SHMEM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. + */ +static void raw_tx_work(struct work_struct *ws) +{ + struct link_device *ld; + struct shmem_link_device *shmd; + int ret; + + ld = container_of(ws, struct link_device, raw_tx_dwork.work); + shmd = to_shmem_link_device(ld); + + ret = wait_for_res_ack(shmd, IPC_RAW); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); + return; + } + + ret = process_res_ack(shmd, IPC_RAW); + if (ret >= 0) { + permit_cp_sleep(shmd); + mif_netif_wake(ld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], + msecs_to_jiffies(1)); + } +} + +/** + * c2c_send_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @skb: pointer to an skb that will be transmitted + * + * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static int c2c_send_ipc(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + int ret; + u16 mask; + + if (atomic_read(&shmd->res_required[dev]) > 0) { + mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + return 0; + } + + ret = xmit_ipc_msg(shmd, dev); + if (likely(ret > 0)) { + mask = get_mask_send(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + goto exit; + } + + /* If there was no TX, just exit */ + if (ret == 0) + goto exit; + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + /* Prohibit CP from sleeping until the TXQ buffer is empty */ + if (forbid_cp_sleep(shmd) < 0) { + trigger_forced_cp_crash(shmd); + goto exit; + } + + /*----------------------------------------------------*/ + /* shmd->res_required[dev] was set in xmit_ipc_msg(). */ + /*----------------------------------------------------*/ + + if (dev == IPC_RAW) + mif_netif_stop(ld); + + queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], + msecs_to_jiffies(1)); + } + +exit: + return ret; +} + +/** + * c2c_try_send_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages with c2c_send_ipc(). + */ +static void c2c_try_send_ipc(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + int ret; + + if (forbid_cp_sleep(shmd) < 0) { + trigger_forced_cp_crash(shmd); + goto exit; + } + + if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { + mif_err("%s: %s txq->qlen %d >= %d\n", ld->name, + get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); + dev_kfree_skb_any(skb); + goto exit; + } + + skb_queue_tail(txq, skb); + + ret = c2c_send_ipc(shmd, dev); + if (ret < 0) { + mif_err("%s->%s: ERR! c2c_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } + +exit: + permit_cp_sleep(shmd); +} + +static int c2c_send_udl_cmd(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + u8 *buff; + u8 *src; + u32 qsize; + u32 in; + int space; + int tx_bytes; + struct circ_status circ; + + if (iod->format == IPC_BOOT) { + pr_ipc(0, "[AP->CP] DL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } else { + pr_ipc(0, "[AP->CP] UL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } + + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (space < 0) { + reset_txq_circ(shmd, dev); + tx_bytes = -EIO; + goto exit; + } + + /* Get the size of data to be sent */ + tx_bytes = skb->len; + + /* Check the size of free space */ + if (space < tx_bytes) { + mif_err("%s: NOSPC in %s_TXQ {qsize:%u in:%u out:%u}, " + "free:%u < tx_bytes:%u\n", ld->name, get_dev_name(dev), + circ.qsize, circ.in, circ.out, space, tx_bytes); + tx_bytes = -ENOSPC; + goto exit; + } + + /* Write data to the TXQ */ + buff = circ.buff; + src = skb->data; + qsize = circ.qsize; + in = circ.in; + circ_write(buff, src, qsize, in, tx_bytes); + + /* Update new head (in) pointer */ + set_txq_head(shmd, dev, circ_new_pointer(qsize, circ.in, tx_bytes)); + +exit: + dev_kfree_skb_any(skb); + return tx_bytes; +} + +static int c2c_send_udl_data(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + u8 *src; + int tx_bytes; + int copied; + u8 *buff; + u32 qsize; + u32 in; + u32 out; + int space; + struct circ_status circ; + + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (space < 0) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(shmd); +#endif + /* Empty out the TXQ */ + reset_txq_circ(shmd, dev); + return -EFAULT; + } + + buff = circ.buff; + qsize = circ.qsize; + in = circ.in; + out = circ.out; + space = circ.size; + + copied = 0; + while (1) { + skb = skb_dequeue(txq); + if (!skb) + break; + + /* Get the size of data to be sent */ + src = skb->data; + tx_bytes = skb->len; + + /* Check the free space size comparing with skb->len */ + if (space < tx_bytes) { + /* Set res_required flag for the "dev" */ + atomic_set(&shmd->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_info("NOSPC in RAW_TXQ {qsize:%u in:%u out:%u}, " + "space:%u < tx_bytes:%u\n", + qsize, in, out, space, tx_bytes); + break; + } + + /* + ** TX only when there is enough space in the TXQ + */ + circ_write(buff, src, qsize, in, tx_bytes); + + copied += tx_bytes; + in = circ_new_pointer(qsize, in, tx_bytes); + space -= tx_bytes; + + dev_kfree_skb_any(skb); + } + + /* Update new head (in) pointer */ + if (copied > 0) { + in = circ_new_pointer(qsize, circ.in, copied); + set_txq_head(shmd, dev, in); + } + + return copied; +} + +/** + * c2c_send_udl + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 4) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static void c2c_send_udl(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct completion *cmpl = &shmd->req_ack_cmpl[dev]; + struct std_dload_info *dl_info = &shmd->dl_info; + struct mem_status mst; + u32 timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); + u32 udl_cmd; + int ret; + u16 mask = get_mask_send(shmd, dev); + + udl_cmd = std_udl_get_cmd(skb->data); + if (iod->format == IPC_RAMDUMP || !std_udl_with_payload(udl_cmd)) { + ret = c2c_send_udl_cmd(shmd, dev, iod, skb); + if (ret > 0) + send_int2cp(shmd, INT_NON_CMD(mask)); + else + mif_err("ERR! c2c_send_udl_cmd fail (err %d)\n", ret); + goto exit; + } + + skb_queue_tail(txq, skb); + if (txq->qlen < dl_info->num_frames) + goto exit; + + mask |= get_mask_req_ack(shmd, dev); + while (1) { + ret = c2c_send_udl_data(shmd, dev); + if (ret < 0) { + mif_err("ERR! c2c_send_udl_data fail (err %d)\n", ret); + skb_queue_purge(txq); + break; + } + + if (skb_queue_empty(txq)) { + send_int2cp(shmd, INT_NON_CMD(mask)); + break; + } + + send_int2cp(shmd, INT_NON_CMD(mask)); + + do { + ret = wait_for_completion_timeout(cmpl, timeout); + get_shmem_status(shmd, TX, &mst); + } while (mst.head[dev][TX] != mst.tail[dev][TX]); + } + +exit: + return; +} + +/** + * c2c_send + * @ld: pointer to an instance of the link_device structure + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * Returns the length of data transmitted or an error code. + * + * Normal call flow for an IPC message: + * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on congestion in a IPC TXQ: + * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work + * => xxx_tx_work -> wait_for_res_ack + * => msg_handler + * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) + */ +static int c2c_send(struct link_device *ld, struct io_device *iod, + struct sk_buff *skb) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + int dev = iod->format; + int len = skb->len; + + switch (dev) { + case IPC_FMT: + case IPC_RAW: + if (likely(ld->mode == LINK_MODE_IPC)) { + c2c_try_send_ipc(shmd, dev, iod, skb); + } else { + mif_err("%s->%s: ERR! ld->mode != LINK_MODE_IPC\n", + iod->name, ld->name); + dev_kfree_skb_any(skb); + } + return len; + + case IPC_BOOT: + case IPC_RAMDUMP: + c2c_send_udl(shmd, IPC_RAW, iod, skb); + return len; + + default: + mif_err("%s: ERR! no TXQ for %s\n", ld->name, iod->name); + dev_kfree_skb_any(skb); + return -ENODEV; + } +} + +#if 1 +/* Functions for BOOT/DUMP and INIT */ +#endif + +static int c2c_dload_start(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + u32 magic; + + ld->mode = LINK_MODE_DLOAD; + + clear_shmem_map(shmd); + + set_magic(shmd, SHM_BOOT_MAGIC); + magic = get_magic(shmd); + if (magic != SHM_BOOT_MAGIC) { + mif_err("%s: ERR! magic 0x%08X != SHM_BOOT_MAGIC 0x%08X\n", + ld->name, magic, SHM_BOOT_MAGIC); + return -EFAULT; + } + + return 0; +} + +/** + * c2c_set_dload_info + * @ld: pointer to an instance of link_device structure + * @iod: pointer to an instance of io_device structure + * @arg: pointer to an instance of std_dload_info structure in "user" memory + * + */ +static int c2c_set_dload_info(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + struct std_dload_info *dl_info = &shmd->dl_info; + int ret; + + ret = copy_from_user(dl_info, (void __user *)arg, + sizeof(struct std_dload_info)); + if (ret) { + mif_err("ERR! copy_from_user fail!\n"); + return -EFAULT; + } + + return 0; +} + +static int c2c_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + mif_err("+++\n"); + trigger_forced_cp_crash(shmd); + mif_err("---\n"); + return 0; +} + +static int c2c_dump_start(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + + ld->mode = LINK_MODE_ULOAD; + + clear_shmem_map(shmd); + + mif_err("%s: magic = 0x%08X\n", ld->name, SHM_DUMP_MAGIC); + set_magic(shmd, SHM_DUMP_MAGIC); + + return 0; +} + +static void c2c_remap_4mb_ipc_region(struct shmem_link_device *shmd) +{ + struct shmem_4mb_phys_map *map; + struct shmem_ipc_device *dev; + + map = (struct shmem_4mb_phys_map *)shmd->base; + + /* Magic code and access enable fields */ + shmd->ipc_map.magic = (u32 __iomem *)&map->magic; + shmd->ipc_map.access = (u32 __iomem *)&map->access; + + /* FMT */ + dev = &shmd->ipc_map.dev[IPC_FMT]; + + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; + + dev->txq.head = (u32 __iomem *)&map->fmt_tx_head; + dev->txq.tail = (u32 __iomem *)&map->fmt_tx_tail; + dev->txq.buff = (u8 __iomem *)&map->fmt_tx_buff[0]; + dev->txq.size = SHM_4M_FMT_TX_BUFF_SZ; + + dev->rxq.head = (u32 __iomem *)&map->fmt_rx_head; + dev->rxq.tail = (u32 __iomem *)&map->fmt_rx_tail; + dev->rxq.buff = (u8 __iomem *)&map->fmt_rx_buff[0]; + dev->rxq.size = SHM_4M_FMT_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_F; + dev->mask_res_ack = INT_MASK_RES_ACK_F; + dev->mask_send = INT_MASK_SEND_F; + + /* RAW */ + dev = &shmd->ipc_map.dev[IPC_RAW]; + + strcpy(dev->name, "RAW"); + dev->id = IPC_RAW; + + dev->txq.head = (u32 __iomem *)&map->raw_tx_head; + dev->txq.tail = (u32 __iomem *)&map->raw_tx_tail; + dev->txq.buff = (u8 __iomem *)&map->raw_tx_buff[0]; + dev->txq.size = SHM_4M_RAW_TX_BUFF_SZ; + + dev->rxq.head = (u32 __iomem *)&map->raw_rx_head; + dev->rxq.tail = (u32 __iomem *)&map->raw_rx_tail; + dev->rxq.buff = (u8 __iomem *)&map->raw_rx_buff[0]; + dev->rxq.size = SHM_4M_RAW_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_R; + dev->mask_res_ack = INT_MASK_RES_ACK_R; + dev->mask_send = INT_MASK_SEND_R; + + /* interrupt ports */ + shmd->ipc_map.mbx2ap = NULL; + shmd->ipc_map.mbx2cp = NULL; +} + +static int c2c_init_ipc_map(struct shmem_link_device *shmd) +{ + if (shmd->size >= SHMEM_SIZE_4MB) + c2c_remap_4mb_ipc_region(shmd); + else + return -EINVAL; + + memset(shmd->base, 0, shmd->size); + + shmd->magic = shmd->ipc_map.magic; + shmd->access = shmd->ipc_map.access; + + shmd->dev[IPC_FMT] = &shmd->ipc_map.dev[IPC_FMT]; + shmd->dev[IPC_RAW] = &shmd->ipc_map.dev[IPC_RAW]; + + shmd->mbx2ap = shmd->ipc_map.mbx2ap; + shmd->mbx2cp = shmd->ipc_map.mbx2cp; + + return 0; +} +static int c2c_init_communication(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + struct io_device *check_iod; + + if (iod->format == IPC_BOOT) + return 0; + + /* send 0xC2 */ + switch(iod->format) { + case IPC_FMT: + check_iod = link_get_iod_with_format(ld, IPC_RFS); + if (check_iod ? atomic_read(&check_iod->opened) : true) { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); + } else + mif_err("%s defined but not opened\n", check_iod->name); + break; + case IPC_RFS: + check_iod = link_get_iod_with_format(ld, IPC_FMT); + if (check_iod && atomic_read(&check_iod->opened)) { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); + } else + mif_err("not opened\n"); + break; + default: + break; + } + return 0; +} + +#if 0 +static void c2c_link_terminate(struct link_device *ld, struct io_device *iod) +{ + if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { + if (!atomic_read(&iod->opened)) { + ld->mode = LINK_MODE_OFFLINE; + mif_err("%s: %s: link mode changed: IPC -> OFFLINE\n", + iod->name, ld->name); + } + } + + return; +} +#endif + +struct link_device *c2c_create_link_device(struct platform_device *pdev) +{ + struct modem_data *modem; + struct shmem_link_device *shmd; + struct link_device *ld; + int err; + int i; + u32 phys_base; + u32 offset; + u32 size; + int irq; + unsigned long irq_flags; + char name[MIF_MAX_NAME_LEN]; + + /* + ** Get the modem (platform) data + */ + modem = (struct modem_data *)pdev->dev.platform_data; + if (!modem) { + mif_err("ERR! modem == NULL\n"); + return NULL; + } + mif_err("%s: %s\n", modem->link_name, modem->name); + + if (modem->ipc_version < SIPC_VER_50) { + mif_err("%s: %s: ERR! IPC version %d < SIPC_VER_50\n", + modem->link_name, modem->name, modem->ipc_version); + return NULL; + } + + /* + ** Alloc an instance of shmem_link_device structure + */ + shmd = kzalloc(sizeof(struct shmem_link_device), GFP_KERNEL); + if (!shmd) { + mif_err("%s: ERR! shmd kzalloc fail\n", modem->link_name); + goto error; + } + ld = &shmd->ld; + + /* + ** Retrieve modem data and SHMEM control data from the modem data + */ + ld->mdm_data = modem; + ld->name = modem->link_name; + ld->aligned = 1; + ld->ipc_version = modem->ipc_version; + ld->max_ipc_dev = MAX_SIPC5_DEV; + + /* + ** Set attributes as a link device + */ + ld->init_comm = c2c_init_communication; +#if 0 + ld->terminate_comm = c2c_link_terminate; +#endif + ld->send = c2c_send; + ld->dload_start = c2c_dload_start; + ld->firm_update = c2c_set_dload_info; + ld->force_dump = c2c_force_dump; + ld->dump_start = c2c_dump_start; + + INIT_LIST_HEAD(&ld->list); + + skb_queue_head_init(&ld->sk_fmt_tx_q); + skb_queue_head_init(&ld->sk_raw_tx_q); + ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q; + ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; + + skb_queue_head_init(&ld->sk_fmt_rx_q); + skb_queue_head_init(&ld->sk_raw_rx_q); + ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; + ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; + + init_completion(&ld->init_cmpl); + + /* + ** Retrieve SHMEM resource + */ + if (modem->link_types & LINKTYPE(LINKDEV_C2C)) { + shmd->type = C2C_SHMEM; + mif_debug("%s: shmd->type = C2C_SHMEM\n", ld->name); + } else if (modem->link_types & LINKTYPE(LINKDEV_SHMEM)) { + shmd->type = REAL_SHMEM; + mif_debug("%s: shmd->type = REAL_SHMEM\n", ld->name); + } else { + mif_err("%s: ERR! invalid type\n", ld->name); + goto error; + } + + phys_base = c2c_get_phys_base(); + offset = c2c_get_sh_rgn_offset(); + size = c2c_get_sh_rgn_size(); + mif_debug("%s: phys_base:0x%08X offset:0x%08X size:%d\n", + ld->name, phys_base, offset, size); + + shmd->start = phys_base + offset; + shmd->size = size; + shmd->base = c2c_request_sh_region(shmd->start, shmd->size); + if (!shmd->base) { + mif_err("%s: ERR! c2c_request_sh_region fail\n", ld->name); + goto error; + } + + mif_debug("%s: phys_addr:0x%08X virt_addr:0x%08X size:%d\n", + ld->name, shmd->start, (int)shmd->base, shmd->size); + + /* + ** Initialize SHMEM maps (physical map -> logical map) + */ + err = c2c_init_ipc_map(shmd); + if (err < 0) { + mif_err("%s: ERR! c2c_init_ipc_map fail (err %d)\n", + ld->name, err); + goto error; + } + + /* + ** Initialize locks, completions, and bottom halves + */ + sprintf(shmd->wlock_name, "%s_wlock", ld->name); + wake_lock_init(&shmd->wlock, WAKE_LOCK_SUSPEND, shmd->wlock_name); + + sprintf(shmd->ap_wlock_name, "%s_ap_wlock", ld->name); + wake_lock_init(&shmd->ap_wlock, WAKE_LOCK_SUSPEND, shmd->ap_wlock_name); + + sprintf(shmd->cp_wlock_name, "%s_cp_wlock", ld->name); + wake_lock_init(&shmd->cp_wlock, WAKE_LOCK_SUSPEND, shmd->cp_wlock_name); + + init_completion(&shmd->udl_cmpl); + for (i = 0; i < MAX_SIPC5_DEV; i++) + init_completion(&shmd->req_ack_cmpl[i]); + + tasklet_init(&shmd->rx_tsk, msg_rx_task, (unsigned long)shmd); + INIT_DELAYED_WORK(&shmd->ipc_rx_dwork, ipc_rx_work); + INIT_DELAYED_WORK(&shmd->udl_rx_dwork, udl_rx_work); + + for (i = 0; i < MAX_SIPC5_DEV; i++) { + spin_lock_init(&shmd->tx_lock[i]); + atomic_set(&shmd->res_required[i], 0); + } + + ld->tx_wq = create_singlethread_workqueue("shmem_tx_wq"); + if (!ld->tx_wq) { + mif_err("%s: ERR! fail to create tx_wq\n", ld->name); + goto error; + } + INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); + INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); + ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; + ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; + + spin_lock_init(&shmd->stat_list.lock); + spin_lock_init(&shmd->trace_list.lock); +#ifdef DEBUG_MODEM_IF + INIT_DELAYED_WORK(&shmd->dump_dwork, mem_dump_work); +#endif + + INIT_DELAYED_WORK(&shmd->cp_sleep_dwork, release_cp_wakeup); + INIT_DELAYED_WORK(&shmd->link_off_dwork, release_ap_status); + spin_lock_init(&shmd->pm_lock); + + /* + ** Retrieve SHMEM IRQ GPIO#, IRQ#, and IRQ flags + */ + shmd->gpio_pda_active = modem->gpio_pda_active; + mif_err("PDA_ACTIVE gpio# = %d (value %d)\n", + shmd->gpio_pda_active, gpio_get_value(shmd->gpio_pda_active)); + + shmd->gpio_ap_status = modem->gpio_ap_status; + shmd->gpio_ap_wakeup = modem->gpio_ap_wakeup; + shmd->irq_ap_wakeup = modem->irq_ap_wakeup; + if (!shmd->irq_ap_wakeup) { + mif_err("ERR! no irq_ap_wakeup\n"); + goto error; + } + mif_debug("CP2AP_WAKEUP IRQ# = %d\n", shmd->irq_ap_wakeup); + + shmd->gpio_cp_wakeup = modem->gpio_cp_wakeup; + shmd->gpio_cp_status = modem->gpio_cp_status; + shmd->irq_cp_status = modem->irq_cp_status; + if (!shmd->irq_cp_status) { + mif_err("ERR! no irq_cp_status\n"); + goto error; + } + mif_debug("CP2AP_STATUS IRQ# = %d\n", shmd->irq_cp_status); + + c2c_assign_gpio_ap_wakeup(shmd->gpio_ap_wakeup); + c2c_assign_gpio_ap_status(shmd->gpio_ap_status); + c2c_assign_gpio_cp_wakeup(shmd->gpio_cp_wakeup); + c2c_assign_gpio_cp_status(shmd->gpio_cp_status); + + gpio_set_value(shmd->gpio_pda_active, 1); + gpio_set_value(shmd->gpio_ap_status, 1); + + /* + ** Register interrupt handlers + */ + err = c2c_register_handler(c2c_irq_handler, shmd); + if (err) { + mif_err("%s: ERR! c2c_register_handler fail (err %d)\n", + ld->name, err); + goto error; + } + + snprintf(name, MIF_MAX_NAME_LEN, "%s_ap_wakeup", ld->name); + irq = shmd->irq_ap_wakeup; + irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); + err = mif_register_isr(irq, ap_wakeup_handler, irq_flags, name, shmd); + if (err) + goto error; + + snprintf(name, MIF_MAX_NAME_LEN, "%s_cp_status", ld->name); + irq = shmd->irq_cp_status; + irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); + err = mif_register_isr(irq, cp_status_handler, irq_flags, name, shmd); + if (err) + goto error; + + return ld; + +error: + mif_err("xxx\n"); + kfree(shmd); + return NULL; +} + diff --git a/drivers/misc/modem_if/modem_link_device_c2c.h b/drivers/misc/modem_if/modem_link_device_c2c.h index 7ec9aa6..9dba90b 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.h +++ b/drivers/misc/modem_if/modem_link_device_c2c.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -8,209 +7,18 @@ * * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ -#include #ifndef __MODEM_LINK_DEVICE_C2C_H__ #define __MODEM_LINK_DEVICE_C2C_H__ -#define DPRAM_ERR_MSG_LEN 128 -#define DPRAM_ERR_DEVICE "c2cerr" +#include +#include "modem_link_device_shmem.h" -#define MAX_IDX 2 - -#define DPRAM_BASE_PTR 0x4000000 - -#define DPRAM_START_ADDRESS 0 -#define DPRAM_MAGIC_CODE_ADDRESS DPRAM_START_ADDRESS -#define DPRAM_GOTA_MAGIC_CODE_SIZE 0x4 -#define DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS \ - (DPRAM_START_ADDRESS + DPRAM_GOTA_MAGIC_CODE_SIZE) -#define BSP_DPRAM_BASE_SIZE 0x1ff8 -#define DPRAM_END_OF_ADDRESS (BSP_DPRAM_BASE_SIZE - 1) -#define DPRAM_INTERRUPT_SIZE 0x2 -#define DPRAM_PDA2PHONE_INTERRUPT_ADDRESS \ - (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE - DPRAM_INTERRUPT_SIZE*2) -#define DPRAM_PHONE2PDA_INTERRUPT_ADDRESS \ - (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE) -#define DPRAM_BUFFER_SIZE \ - (DPRAM_PHONE2PDA_INTERRUPT_ADDRESS -\ - DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS) -#define DPRAM_INDEX_SIZE 0x2 - -#define MAGIC_DMDL 0x4445444C -#define MAGIC_UMDL 0x4445444D - -#define DPRAM_PACKET_DATA_SIZE 0x3f00 -#define DPRAM_PACKET_HEADER_SIZE 0x7 - -#define INT_GOTA_MASK_VALID 0xA000 -#define INT_DPRAM_DUMP_MASK_VALID 0xA000 -#define MASK_CMD_RECEIVE_READY_NOTIFICATION 0xA100 -#define MASK_CMD_DOWNLOAD_START_REQUEST 0xA200 -#define MASK_CMD_DOWNLOAD_START_RESPONSE 0xA301 -#define MASK_CMD_IMAGE_SEND_REQUEST 0xA400 -#define MASK_CMD_IMAGE_SEND_RESPONSE 0xA500 -#define MASK_CMD_SEND_DONE_REQUEST 0xA600 -#define MASK_CMD_SEND_DONE_RESPONSE 0xA701 -#define MASK_CMD_STATUS_UPDATE_NOTIFICATION 0xA800 -#define MASK_CMD_UPDATE_DONE_NOTIFICATION 0xA900 -#define MASK_CMD_EFS_CLEAR_RESPONSE 0xAB00 -#define MASK_CMD_ALARM_BOOT_OK 0xAC00 -#define MASK_CMD_ALARM_BOOT_FAIL 0xAD00 - -#define WRITEIMG_HEADER_SIZE 8 -#define WRITEIMG_TAIL_SIZE 4 -#define WRITEIMG_BODY_SIZE \ - (DPRAM_BUFFER_SIZE - WRITEIMG_HEADER_SIZE - WRITEIMG_TAIL_SIZE) - -#define DPDN_DEFAULT_WRITE_LEN WRITEIMG_BODY_SIZE -#define CMD_DL_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 - -#define CMD_UL_START_REQ 0x9200 -#define CMD_UL_START_READY 0x9400 -#define CMD_UL_SEND_RESP 0x9601 -#define CMD_UL_SEND_DONE_RESP 0x9801 -#define CMD_UL_SEND_REQ 0xA500 -#define CMD_UL_START_RESPONSE 0xA301 -#define CMD_UL_SEND_DONE_REQ 0xA700 -#define CMD_RECEIVE_READY_NOTIFICATION 0xA100 - -#define MASK_CMD_RESULT_FAIL 0x0002 -#define MASK_CMD_RESULT_SUCCESS 0x0001 - -#define START_INDEX 0x007F -#define END_INDEX 0x007E - -#define CMD_IMG_SEND_REQ 0x9400 - -#define CRC_TAB_SIZE 256 -#define CRC_16_L_SEED 0xFFFF - -struct c2c_device { - /* DPRAM memory addresses */ - u16 *in_head_addr; - u16 *in_tail_addr; - u8 *in_buff_addr; - unsigned long in_buff_size; - - u16 *out_head_addr; - u16 *out_tail_addr; - u8 *out_buff_addr; - unsigned long out_buff_size; - - unsigned long in_head_saved; - unsigned long in_tail_saved; - unsigned long out_head_saved; - unsigned long out_tail_saved; - - u16 mask_req_ack; - u16 mask_res_ack; - u16 mask_send; -}; - -struct memory_region { - u8 *control; - u8 *fmt_out; - u8 *raw_out; - u8 *fmt_in; - u8 *raw_in; - u8 *mbx; -}; - -struct UldDataHeader { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -}; - -struct c2c_link_device { - struct link_device ld; - - struct modem_data *pdata; - - /*only c2c*/ - struct wake_lock c2c_wake_lock; - atomic_t raw_txq_req_ack_rcvd; - atomic_t fmt_txq_req_ack_rcvd; - u8 net_stop_flag; - int phone_sync; - u8 phone_status; - - struct work_struct xmit_work_struct; - - struct workqueue_struct *gota_wq; - struct work_struct gota_cmd_work; - - struct c2c_device dev_map[MAX_IDX]; - - struct wake_lock dumplock; - - u8 c2c_read_data[131072]; - - int c2c_init_cmd_wait_condition; - wait_queue_head_t c2c_init_cmd_wait_q; - - int modem_pif_init_wait_condition; - wait_queue_head_t modem_pif_init_done_wait_q; - - struct completion gota_download_start_complete; - - int gota_send_done_cmd_wait_condition; - wait_queue_head_t gota_send_done_cmd_wait_q; - - int gota_update_done_cmd_wait_condition; - wait_queue_head_t gota_update_done_cmd_wait_q; - - int upload_send_req_wait_condition; - wait_queue_head_t upload_send_req_wait_q; - - int upload_send_done_wait_condition; - wait_queue_head_t upload_send_done_wait_q; - - int upload_start_req_wait_condition; - wait_queue_head_t upload_start_req_wait_q; - - int upload_packet_start_condition; - wait_queue_head_t upload_packet_start_wait_q; - - u16 gota_irq_handler_cmd; - - u16 c2c_dump_handler_cmd; - - int dump_region_number; - - unsigned int is_c2c_err ; - - int c2c_dump_start; - int gota_start; - - char c2c_err_buf[DPRAM_ERR_MSG_LEN]; - - struct fasync_struct *c2c_err_async_q; - - void (*clear_interrupt)(struct c2c_link_device *); - - struct memory_region m_region; - - unsigned long fmt_out_buff_size; - unsigned long raw_out_buff_size; - unsigned long fmt_in_buff_size; - unsigned long raw_in_buff_size; - - struct delayed_work delayed_tx; - struct sk_buff *delayed_skb; - u8 delayed_count; -}; - -/* converts from struct link_device* to struct xxx_link_device* */ -#define to_c2c_link_device(linkdev) \ - container_of(linkdev, struct c2c_link_device, ld) +#define CP_WAKEUP_HOLD_TIME 500 /* 500 ms */ #endif + diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c index a650ed9..2d08c4e 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.c +++ b/drivers/misc/modem_if/modem_link_device_dpram.c @@ -25,560 +25,385 @@ #include #include #include -#include +#include +#include +#include +#include #include "modem_prj.h" -#include "modem_link_device_dpram.h" #include "modem_utils.h" +#include "modem_link_device_dpram.h" -/* -** Function prototypes for basic DPRAM operations -*/ -static inline void clear_intr(struct dpram_link_device *dpld); -static inline u16 recv_intr(struct dpram_link_device *dpld); -static inline void send_intr(struct dpram_link_device *dpld, u16 mask); - -static inline u16 get_magic(struct dpram_link_device *dpld); -static inline void set_magic(struct dpram_link_device *dpld, u16 val); -static inline u16 get_access(struct dpram_link_device *dpld); -static inline void set_access(struct dpram_link_device *dpld, u16 val); - -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); - -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); - -static void handle_cp_crash(struct dpram_link_device *dpld); -static int trigger_force_cp_crash(struct dpram_link_device *dpld); -static void dpram_dump_memory(struct link_device *ld, char *buff); - -/* -** Functions for debugging -*/ -static inline void log_dpram_status(struct dpram_link_device *dpld) -{ - pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " - "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", - dpld->ld.mc->name, - get_magic(dpld), get_access(dpld), - get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), - get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), - get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), - get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), - recv_intr(dpld)); -} - -static void set_dpram_map(struct dpram_link_device *dpld, - struct mif_irq_map *map) -{ - map->magic = get_magic(dpld); - map->access = get_access(dpld); - - map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); - map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); - map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); - map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); - map->raw_tx_in = get_tx_head(dpld, IPC_RAW); - map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); - map->raw_rx_in = get_rx_head(dpld, IPC_RAW); - map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); - - map->cp2ap = recv_intr(dpld); -} - -/* -** RXB (DPRAM RX buffer) functions -*/ -static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) -{ - struct dpram_rxb *rxb; - u8 *buff; - int i; - - rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); - if (!rxb) { - mif_info("ERR! kzalloc rxb fail\n"); - return NULL; - } - - buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); - if (!buff) { - mif_info("ERR! kzalloc buff fail\n"); - kfree(rxb); - return NULL; - } - - for (i = 0; i < count; i++) { - rxb[i].buff = buff; - rxb[i].size = size; - buff += size; - } - - return rxb; -} - -static inline unsigned rxbq_get_page_size(unsigned len) -{ - return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; -} - -static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) -{ - return (rxbq->in == rxbq->out) ? true : false; -} - -static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in < out) ? (out - in - 1) : (qsize + out - in - 1); -} +static void trigger_forced_cp_crash(struct dpram_link_device *dpld); -static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) +/** + * set_circ_pointer + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @dir: direction of communication (TX or RX) + * @ptr: type of the queue pointer (HEAD or TAIL) + * @addr: address of the queue pointer + * @val: value to be written to the queue pointer + * + * Writes a value to a pointer in a circular queue with verification. + */ +static inline void set_circ_pointer(struct dpram_link_device *dpld, int id, + int dir, int ptr, void __iomem *addr, u16 val) { - struct dpram_rxb *rxb = NULL; + struct link_device *ld = &dpld->ld; + int cnt = 0; + u16 saved = 0; - if (likely(rxbq_free_size(rxbq) > 0)) { - rxb = &rxbq->rxb[rxbq->in]; - rxbq->in++; - if (rxbq->in >= rxbq->size) - rxbq->in -= rxbq->size; - rxb->data = rxb->buff; - } + iowrite16(val, addr); - return rxb; -} + while (1) { + /* Check the value written to the address */ + saved = ioread16(addr); + if (likely(saved == val)) + break; -static inline int rxbq_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in >= out) ? (in - out) : (qsize - out + in); -} + cnt++; + mif_err("%s: ERR! %s_%s.%s saved(%d) != val(%d), count %d\n", + ld->name, get_dev_name(id), circ_dir(dir), + circ_ptr(ptr), saved, val, cnt); + if (cnt >= MAX_RETRY_CNT) { + trigger_forced_cp_crash(dpld); + break; + } -static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; + udelay(100); - if (likely(!rxbq_empty(rxbq))) { - rxb = &rxbq->rxb[rxbq->out]; - rxbq->out++; - if (rxbq->out >= rxbq->size) - rxbq->out -= rxbq->size; + /* Write the value again */ + iowrite16(val, addr); } - - return rxb; -} - -static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) -{ - rxb->len = len; - return rxb->data; } -static inline void rxb_clear(struct dpram_rxb *rxb) +/** + * recv_int2ap + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the CP-to-AP interrupt register in a DPRAM. + */ +static inline u16 recv_int2ap(struct dpram_link_device *dpld) { - rxb->data = NULL; - rxb->len = 0; + return ioread16(dpld->mbx2ap); } -/* -** DPRAM operations -*/ -static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), - unsigned long flag, const char *name, - struct dpram_link_device *dpld) +/** + * send_int2cp + * @dpld: pointer to an instance of dpram_link_device structure + * @mask: value to be written to the AP-to-CP interrupt register in a DPRAM + */ +static inline void send_int2cp(struct dpram_link_device *dpld, u16 mask) { - int ret; + struct idpram_pm_op *pm_op = dpld->pm_op; - ret = request_irq(irq, isr, flag, name, dpld); - if (ret) { - mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); - return ret; + if (pm_op && pm_op->int2cp_possible) { + if (!pm_op->int2cp_possible(dpld)) + return; } - ret = enable_irq_wake(irq); - if (ret) - mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); - - mif_info("%s (#%d) handler registered\n", name, irq); - - return 0; -} - -static inline void clear_intr(struct dpram_link_device *dpld) -{ - if (likely(dpld->dpctl->clear_intr)) - dpld->dpctl->clear_intr(); -} - -static inline u16 recv_intr(struct dpram_link_device *dpld) -{ - if (likely(dpld->dpctl->recv_intr)) - return dpld->dpctl->recv_intr(); - else - return ioread16(dpld->mbx2ap); + iowrite16(mask, dpld->mbx2cp); } -static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +/** + * read_int2cp + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the AP-to-CP interrupt register in a DPRAM. + */ +static inline u16 read_int2cp(struct dpram_link_device *dpld) { - if (likely(dpld->dpctl->send_intr)) - dpld->dpctl->send_intr(mask); - else - iowrite16(mask, dpld->mbx2cp); + return ioread16(dpld->mbx2cp); } +/** + * get_magic + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the "magic code" field in a DPRAM. + */ static inline u16 get_magic(struct dpram_link_device *dpld) { return ioread16(dpld->magic); } +/** + * set_magic + * @dpld: pointer to an instance of dpram_link_device structure + * @val: value to be written to the "magic code" field in a DPRAM + */ static inline void set_magic(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->magic); } +/** + * get_access + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the "access enable" field in a DPRAM. + */ static inline u16 get_access(struct dpram_link_device *dpld) { return ioread16(dpld->access); } +/** + * set_access + * @dpld: pointer to an instance of dpram_link_device structure + * @val: value to be written to the "access enable" field in a DPRAM + */ static inline void set_access(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->access); } -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +/** + * get_txq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_head(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->txq.head); } -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +/** + * get_txq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (out) pointer in a TX queue. + * + * It is useless for an AP to read a tail pointer in a TX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_txq_tail(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->txq.tail); } -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)in, dpld->dev[id]->txq.head); - - do { - /* Check head value written */ - val = ioread16(dpld->dev[id]->txq.head); - if (likely(val == in)) - return; - - mif_err("ERR: %s txq.head(%d) != in(%d)\n", - get_dev_name(id), val, in); - udelay(100); - - /* Write head value again */ - iowrite16((u16)in, dpld->dev[id]->txq.head); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)out, dpld->dev[id]->txq.tail); - - do { - /* Check tail value written */ - val = ioread16(dpld->dev[id]->txq.tail); - if (likely(val == out)) - return; - - mif_err("ERR: %s txq.tail(%d) != out(%d)\n", - get_dev_name(id), val, out); - udelay(100); - - /* Write tail value again */ - iowrite16((u16)out, dpld->dev[id]->txq.tail); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +/** + * get_txq_buff + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_buff(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->txq.buff; } -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +/** + * get_txq_buff_size + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_buff_size(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->txq.size; } -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +/** + * get_rxq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in an RX queue. + * + * It is useless for an AP to read a head pointer in an RX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_rxq_head(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->rxq.head); } -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +/** + * get_rxq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_tail(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->rxq.tail); } -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)in, dpld->dev[id]->rxq.head); - - do { - /* Check head value written */ - val = ioread16(dpld->dev[id]->rxq.head); - if (val == in) - return; - - mif_err("ERR: %s rxq.head(%d) != in(%d)\n", - get_dev_name(id), val, in); - udelay(100); - - /* Write head value again */ - iowrite16((u16)in, dpld->dev[id]->rxq.head); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)out, dpld->dev[id]->rxq.tail); - - do { - /* Check tail value written */ - val = ioread16(dpld->dev[id]->rxq.tail); - if (val == out) - return; - - mif_err("ERR: %s rxq.tail(%d) != out(%d)\n", - get_dev_name(id), val, out); - udelay(100); - - /* Write tail value again */ - iowrite16((u16)out, dpld->dev[id]->rxq.tail); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +/** + * get_rxq_buff + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_buff(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->rxq.buff; } -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +/** + * get_rxq_buff_size + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_buff_size(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->rxq.size; } -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) -{ - return dpld->dev[id]->mask_req_ack; -} - -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +/** + * set_txq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct dpram_link_device *dpld, int id, u32 in) { - return dpld->dev[id]->mask_res_ack; + set_circ_pointer(dpld, id, TX, HEAD, dpld->dev[id]->txq.head, in); } -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +/** + * set_txq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct dpram_link_device *dpld, int id, u32 out) { - return dpld->dev[id]->mask_send; + set_circ_pointer(dpld, id, TX, TAIL, dpld->dev[id]->txq.tail, out); } -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +/** + * set_rxq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct dpram_link_device *dpld, int id, u32 in) { - if (in >= size) - return false; - - if (out >= size) - return false; - - return true; + set_circ_pointer(dpld, id, RX, HEAD, dpld->dev[id]->rxq.head, in); } -/* Get free space in the TXQ as well as in & out pointers */ -static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +/** + * set_rxq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct dpram_link_device *dpld, int id, u32 out) { - struct link_device *ld = &dpld->ld; - int cnt = 3; - u32 head; - u32 tail; - int space; - - do { - head = get_tx_head(dpld, dev); - tail = get_tx_tail(dpld, dev); - - space = (head < tail) ? (tail - head - 1) : - (qsize + tail - head - 1); - mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", - ld->name, get_dev_name(dev), qsize, head, tail, space); - - if (dpram_circ_valid(qsize, head, tail)) { - *in = head; - *out = tail; - return space; - } - - mif_info("%s: CAUTION! <%pf> " - "%s_TXQ invalid (size:%d in:%d out:%d)\n", - ld->name, __builtin_return_address(0), - get_dev_name(dev), qsize, head, tail); - - udelay(100); - } while (cnt--); - - *in = 0; - *out = 0; - return -EINVAL; + set_circ_pointer(dpld, id, RX, TAIL, dpld->dev[id]->rxq.tail, out); } -static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +/** + * get_mask_req_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) { - set_tx_head(dpld, dev, 0); - set_tx_tail(dpld, dev, 0); - if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + return dpld->dev[id]->mask_req_ack; } -/* Get data size in the RXQ as well as in & out pointers */ -static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +/** + * get_mask_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) { - struct link_device *ld = &dpld->ld; - int cnt = 3; - u32 head; - u32 tail; - u32 rcvd; - - do { - head = get_rx_head(dpld, dev); - tail = get_rx_tail(dpld, dev); - if (head == tail) { - *in = head; - *out = tail; - return 0; - } - - rcvd = (head > tail) ? (head - tail) : (qsize - tail + head); - mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", - ld->name, get_dev_name(dev), qsize, head, tail, rcvd); - - if (dpram_circ_valid(qsize, head, tail)) { - *in = head; - *out = tail; - return rcvd; - } - - mif_info("%s: CAUTION! <%pf> " - "%s_RXQ invalid (size:%d in:%d out:%d)\n", - ld->name, __builtin_return_address(0), - get_dev_name(dev), qsize, head, tail); - - udelay(100); - } while (cnt--); - - *in = 0; - *out = 0; - return -EINVAL; + return dpld->dev[id]->mask_res_ack; } -static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +/** + * get_mask_send + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) { - set_rx_head(dpld, dev, 0); - set_rx_tail(dpld, dev, 0); - if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + return dpld->dev[id]->mask_send; } -/* -** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success -*/ -static int dpram_wake_up(struct dpram_link_device *dpld) +/** + * reset_txq_circ + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties a TXQ by resetting the head (in) pointer with the value in the tail + * (out) pointer. + */ +static inline void reset_txq_circ(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + u32 head = get_txq_head(dpld, dev); + u32 tail = get_txq_tail(dpld, dev); - if (!dpld->dpctl->wakeup) - return 0; - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n", - ld->name, __builtin_return_address(0)); - - if (dpld->dpctl->sleep) - dpld->dpctl->sleep(); - - udelay(10); - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail twice\n", - ld->name, __builtin_return_address(0)); - return -EACCES; - } - } + mif_info("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); - atomic_inc(&dpld->accessing); - return 0; + set_txq_head(dpld, dev, tail); } -static void dpram_allow_sleep(struct dpram_link_device *dpld) +/** + * reset_rxq_circ + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties an RXQ by resetting the tail (out) pointer with the value in the head + * (in) pointer. + */ +static inline void reset_rxq_circ(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + u32 head = get_rxq_head(dpld, dev); + u32 tail = get_rxq_tail(dpld, dev); - if (!dpld->dpctl->sleep) - return; + mif_info("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); - if (atomic_dec_return(&dpld->accessing) <= 0) { - dpld->dpctl->sleep(); - atomic_set(&dpld->accessing, 0); - mif_debug("%s: DPRAM sleep possible\n", ld->name); - } + set_rxq_tail(dpld, dev, head); } -static int dpram_check_access(struct dpram_link_device *dpld) +/** + * check_magic_access + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns 0 if the "magic code" and "access enable" values are valid, otherwise + * returns -EACCES. + */ +static int check_magic_access(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; int i; u16 magic = get_magic(dpld); u16 access = get_access(dpld); + /* Returns 0 if the "magic code" and "access enable" are valid */ if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; + /* Retry up to 100 times with 100 us delay per each retry */ for (i = 1; i <= 100; i++) { - mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n", + mif_info("%s: magic:%X access:%X -> retry:%d\n", ld->name, magic, access, i); udelay(100); @@ -592,357 +417,313 @@ static int dpram_check_access(struct dpram_link_device *dpld) return -EACCES; } -static bool dpram_ipc_active(struct dpram_link_device *dpld) +/** + * ipc_active + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns whether or not IPC via the dpram_link_device instance is possible. + */ +static bool ipc_active(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { - mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n", - ld->name, __builtin_return_address(0)); + mif_err("%s: ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); return false; } - if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pf> dpram_check_access fail\n", - ld->name, __builtin_return_address(0)); + /* Check "magic code" and "access enable" values */ + if (check_magic_access(dpld) < 0) { + mif_err("%s: ERR! check_magic_access fail\n", + ld->name, CALLER); return false; } return true; } -static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 in, u32 out, struct sk_buff *skb) -{ - struct link_device *ld = &dpld->ld; - u8 __iomem *buff = get_tx_buff(dpld, dev); - u8 *src = skb->data; - u32 len = skb->len; - u32 inp; - struct mif_irq_map map; - - if (in < out) { - /* +++++++++ in ---------- out ++++++++++ */ - memcpy((buff + in), src, len); - } else { - /* ------ out +++++++++++ in ------------ */ - u32 space = qsize - in; +/** + * get_dpram_status + * @dpld: pointer to an instance of dpram_link_device structure + * @dir: direction of communication (TX or RX) + * @stat: pointer to an instance of mem_status structure + * + * Takes a snapshot of the current status of a DPRAM. + */ +static void get_dpram_status(struct dpram_link_device *dpld, + enum circ_dir_type dir, struct mem_status *stat) +{ +#ifdef DEBUG_MODEM_IF + getnstimeofday(&stat->ts); +#endif + + stat->dir = dir; + stat->magic = get_magic(dpld); + stat->access = get_access(dpld); + stat->head[IPC_FMT][TX] = get_txq_head(dpld, IPC_FMT); + stat->tail[IPC_FMT][TX] = get_txq_tail(dpld, IPC_FMT); + stat->head[IPC_FMT][RX] = get_rxq_head(dpld, IPC_FMT); + stat->tail[IPC_FMT][RX] = get_rxq_tail(dpld, IPC_FMT); + stat->head[IPC_RAW][TX] = get_txq_head(dpld, IPC_RAW); + stat->tail[IPC_RAW][TX] = get_txq_tail(dpld, IPC_RAW); + stat->head[IPC_RAW][RX] = get_rxq_head(dpld, IPC_RAW); + stat->tail[IPC_RAW][RX] = get_rxq_tail(dpld, IPC_RAW); + stat->int2ap = recv_int2ap(dpld); + stat->int2cp = read_int2cp(dpld); +} + +#if 0 +/** + * save_ipc_trace_work + * @work: pointer to an instance of work_struct structure + * + * Performs actual file operation for saving RX IPC trace. + */ +static void save_ipc_trace_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct trace_data_queue *trq; + struct trace_data *trd; + struct circ_status *stat; + struct file *fp; + struct timespec *ts; + int dev; + u8 *dump; + int rcvd; + u8 *buff; + char *path; + struct utc_time utc; - /* 1) in -> buffer end */ - memcpy((buff + in), src, ((len > space) ? space : len)); + dpld = container_of(work, struct dpram_link_device, trace_dwork.work); + ld = &dpld->ld; + trq = &dpld->trace_list; + path = dpld->trace_path; - /* 2) buffer start -> out */ - if (len > space) - memcpy(buff, (src + space), (len - space)); + buff = kzalloc(dpld->size << 3, GFP_KERNEL); + if (!buff) { + while (1) { + trd = trq_get_data_slot(trq); + if (!trd) + break; + + ts = &trd->ts; + dev = trd->dev; + stat = &trd->circ_stat; + dump = trd->data; + rcvd = trd->size; + print_ipc_trace(ld, dev, stat, ts, dump, rcvd); + + kfree(dump); + } + return; } - /* update new in pointer */ - inp = in + len; - if (inp >= qsize) - inp -= qsize; - set_tx_head(dpld, dev, inp); + while (1) { + trd = trq_get_data_slot(trq); + if (!trd) + break; - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); - mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); - } + ts = &trd->ts; + dev = trd->dev; + stat = &trd->circ_stat; + dump = trd->data; + rcvd = trd->size; + + ts2utc(ts, &utc); + snprintf(path, MIF_MAX_PATH_LEN, + "%s/%s_%s_%d%02d%02d-%02d%02d%02d.lst", + MIF_LOG_DIR, ld->name, get_dev_name(dev), + utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec); + + fp = mif_open_file(path); + if (fp) { + int len; + + snprintf(buff, MIF_MAX_PATH_LEN, + "[%d-%02d-%02d %02d:%02d:%02d.%03d] " + "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", + utc.year, utc.mon, utc.day, utc.hour, utc.min, + utc.sec, utc.msec, ld->name, get_dev_name(dev), + stat->in, stat->out, stat->size); + len = strlen(buff); + mif_dump2format4(dump, rcvd, (buff + len), NULL); + strcat(buff, "\n"); + len = strlen(buff); + + mif_save_file(fp, buff, len); + + memset(buff, 0, len); + mif_close_file(fp); + } else { + mif_err("%s: %s open fail\n", ld->name, path); + print_ipc_trace(ld, dev, stat, ts, dump, rcvd); + } - if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { - mif_err("%s: memcmp16_to_io fail\n", ld->name); - trigger_force_cp_crash(dpld); + kfree(dump); } + + kfree(buff); } +#endif -static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) +/** + * set_dpram_map + * @dpld: pointer to an instance of dpram_link_device structure + * @map: pointer to an instance of mif_irq_map structure + * + * Sets variables in an mif_irq_map instance as current DPRAM status for IPC + * logging. + */ +static void set_dpram_map(struct dpram_link_device *dpld, + struct mif_irq_map *map) { - struct link_device *ld = &dpld->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - struct sk_buff *skb; - unsigned long int flags; - u32 qsize = get_tx_buff_size(dpld, dev); - u32 in; - u32 out; - int space; - int copied = 0; - - spin_lock_irqsave(&dpld->tx_lock[dev], flags); - - while (1) { - space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); - if (unlikely(space < 0)) { - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - dpram_reset_tx_circ(dpld, dev); - return space; - } - - skb = skb_dequeue(txq); - if (unlikely(!skb)) - break; - - if (unlikely(space < skb->len)) { - atomic_set(&dpld->res_required[dev], 1); - skb_queue_head(txq, skb); - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - mif_info("%s: %s " - "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", - ld->name, get_dev_name(dev), - qsize, in, out, space, skb->len); - return -ENOSPC; - } - - /* TX if there is enough room in the queue */ - dpram_ipc_write(dpld, dev, qsize, in, out, skb); - copied += skb->len; - dev_kfree_skb_any(skb); - } + map->magic = get_magic(dpld); + map->access = get_access(dpld); - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + map->fmt_tx_in = get_txq_head(dpld, IPC_FMT); + map->fmt_tx_out = get_txq_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rxq_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rxq_tail(dpld, IPC_FMT); + map->raw_tx_in = get_txq_head(dpld, IPC_RAW); + map->raw_tx_out = get_txq_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rxq_head(dpld, IPC_RAW); + map->raw_rx_out = get_rxq_tail(dpld, IPC_RAW); - return copied; + map->cp2ap = recv_int2ap(dpld); } -static void dpram_ipc_rx_task(unsigned long data) +/** + * dpram_wake_up + * @dpld: pointer to an instance of dpram_link_device structure + * + * Wakes up a DPRAM if it can sleep and increases the "accessing" counter in the + * dpram_link_device instance. + * + * CAUTION!!! dpram_allow_sleep() MUST be invoked after dpram_wake_up() success + * to decrease the "accessing" counter. + */ +static int dpram_wake_up(struct dpram_link_device *dpld) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; struct link_device *ld = &dpld->ld; - struct io_device *iod; - struct dpram_rxb *rxb; - unsigned qlen; - int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { - iod = dpld->iod[i]; - qlen = rxbq_size(&dpld->rxbq[i]); - while (qlen > 0) { - rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); - iod->recv(iod, ld, rxb->data, rxb->len); - rxb_clear(rxb); - qlen--; - } - } -} + if (unlikely(!dpld->need_wake_up)) + return 0; -static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, - u8 __iomem *src, u32 out, u32 len, u32 qsize) -{ - if ((out + len) <= qsize) { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - memcpy(dst, (src + out), len); - } else { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - unsigned len1 = qsize - out; + if (dpld->ext_op->wakeup(dpld) < 0) { + mif_err("%s: ERR! wakeup fail\n", + ld->name, CALLER); + return -EACCES; + } - /* 1) out -> buffer end */ - memcpy(dst, (src + out), len1); + atomic_inc(&dpld->accessing); - /* 2) buffer start -> in */ - dst += len1; - memcpy(dst, src, (len - len1)); - } + return 0; } -/* - ret < 0 : error - ret == 0 : no data - ret > 0 : valid data -*/ -static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) +/** + * dpram_allow_sleep + * @dpld: pointer to an instance of dpram_link_device structure + * + * Decreases the "accessing" counter in the dpram_link_device instance if it can + * sleep and allows the DPRAM to sleep only if the value of "accessing" counter + * is less than or equal to 0. + * + * MUST be invoked after dpram_wake_up() success to decrease the "accessing" + * counter. + */ +static void dpram_allow_sleep(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - struct dpram_rxb *rxb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); - u32 in; - u32 out; - u32 rcvd; - struct mif_irq_map map; - - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); - if (rcvd <= 0) - return rcvd; - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } + if (unlikely(!dpld->need_wake_up)) + return; - /* Allocate an rxb */ - rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); - if (!rxb) { - mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", - ld->name, get_dev_name(dev)); - return -ENOMEM; + if (atomic_dec_return(&dpld->accessing) <= 0) { + dpld->ext_op->sleep(dpld); + atomic_set(&dpld->accessing, 0); + mif_debug("%s: DPRAM sleep possible\n", ld->name); } - - /* Read data from each DPRAM buffer */ - dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); - - /* Calculate and set new out */ - out += rcvd; - if (out >= qsize) - out -= qsize; - set_rx_tail(dpld, dev, out); - - return rcvd; } -/* - ret < 0 : error - ret == 0 : no data - ret > 0 : valid data -*/ -static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) +static int capture_dpram_snapshot(struct link_device *ld, struct io_device *iod) { - struct link_device *ld = &dpld->ld; - struct io_device *iod = dpld->iod[dev]; + struct dpram_link_device *dpld = to_dpram_link_device(ld); struct sk_buff *skb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); - u32 in; - u32 out; - u32 rcvd; - int rest; - u8 *frm; - u8 *dst; - unsigned int len; - unsigned int pad; - unsigned int tot; - struct mif_irq_map map; - - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); - if (rcvd <= 0) - return rcvd; - - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } - - rest = rcvd; - while (rest > 0) { - frm = src + out; - if (unlikely(!sipc5_start_valid(frm[0]))) { - mif_err("%s: ERR! %s invalid start 0x%02X\n", - ld->name, get_dev_name(dev), frm[0]); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } + u32 size = dpld->size; + u32 copied = 0; + u8 *dump; - len = sipc5_get_frame_sz16(frm); - if (unlikely(len > rest)) { - mif_err("%s: ERR! %s len %d > rest %d\n", - ld->name, get_dev_name(dev), len, rest); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } + dpram_wake_up(dpld); + dump = capture_mem_dump(ld, dpld->base, dpld->size); + dpram_allow_sleep(dpld); - pad = sipc5_calc_padding_size(len); - tot = len + pad; + if (!dump) + return -ENOMEM; - /* Allocate an skb */ - skb = dev_alloc_skb(tot); + while (copied < size) { + skb = alloc_skb(MAX_DUMP_SKB_SIZE, GFP_ATOMIC); if (!skb) { - mif_err("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); + mif_err("ERR! alloc_skb fail\n"); + kfree(dump); return -ENOMEM; } - /* Read data from each DPRAM buffer */ - dst = skb_put(skb, tot); - dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); - skb_trim(skb, len); - iod->recv_skb(iod, ld, skb); - - /* Calculate and set new out */ - rest -= tot; - out += tot; - if (out >= qsize) - out -= qsize; - } - - set_rx_tail(dpld, dev, out); - - return rcvd; -} - -static void non_command_handler(struct dpram_link_device *dpld, u16 intr) -{ - struct link_device *ld = &dpld->ld; - int i = 0; - int ret = 0; - u16 tx_mask = 0; - - if (!dpram_ipc_active(dpld)) - return; - - /* Read data from DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (dpld->use_skb) - ret = dpram_ipc_recv_data_with_skb(dpld, i); - else - ret = dpram_ipc_recv_data_with_rxb(dpld, i); - if (ret < 0) - dpram_reset_rx_circ(dpld, i); - - /* Check and process REQ_ACK (at this time, in == out) */ - if (intr & get_mask_req_ack(dpld, i)) { - mif_debug("%s: send %s_RES_ACK\n", - ld->name, get_dev_name(i)); - tx_mask |= get_mask_res_ack(dpld, i); - } - } - - if (!dpld->use_skb) { - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); - } + skb_put(skb, MAX_DUMP_SKB_SIZE); + memcpy(skb->data, (dump + copied), MAX_DUMP_SKB_SIZE); + copied += MAX_DUMP_SKB_SIZE; - /* Try TX via DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (atomic_read(&dpld->res_required[i]) > 0) { - ret = dpram_try_ipc_tx(dpld, i); - if (ret > 0) { - atomic_set(&dpld->res_required[i], 0); - tx_mask |= get_mask_send(dpld, i); - } else if (ret == -ENOSPC) { - tx_mask |= get_mask_req_ack(dpld, i); - } - } + skb_queue_tail(&iod->sk_rx_q, skb); + wake_up(&iod->wq); } - if (tx_mask) { - send_intr(dpld, INT_NON_CMD(tx_mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); - } + kfree(dump); + return 0; } +/** + * handle_cp_crash + * @dpld: pointer to an instance of dpram_link_device structure + * + * Actual handler for the CRASH_EXIT command from a CP. + */ static void handle_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { - mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); + if (dpld->forced_cp_crash) + dpld->forced_cp_crash = false; + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < ld->max_ipc_dev; i++) skb_queue_purge(ld->skb_txq[i]); - } + /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); - - iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); if (iod) - iodevs_for_each(iod->msd, iodev_netif_stop, 0); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); } -static void handle_no_crash_ack(unsigned long arg) +/** + * handle_no_cp_crash_ack + * @arg: pointer to an instance of dpram_link_device structure + * + * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no + * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. + */ +static void handle_no_cp_crash_ack(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; struct link_device *ld = &dpld->ld; @@ -955,187 +736,363 @@ static void handle_no_crash_ack(unsigned long arg) handle_cp_crash(dpld); } -static int trigger_force_cp_crash(struct dpram_link_device *dpld) +/** + * trigger_forced_cp_crash + * @dpld: pointer to an instance of dpram_link_device structure + * + * Triggers an enforced CP crash. + */ +static void trigger_forced_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; +#ifdef DEBUG_MODEM_IF + struct trace_data *trd; + u8 *dump; + struct timespec ts; + getnstimeofday(&ts); +#endif if (ld->mode == LINK_MODE_ULOAD) { - mif_err("%s: CP crash is already in progress\n", ld->mc->name); - return 0; + mif_err("%s: ALREADY in progress\n", + ld->name, CALLER); + return; } ld->mode = LINK_MODE_ULOAD; - mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); + dpld->forced_cp_crash = true; + + disable_irq_nosync(dpld->irq); + + dpram_wake_up(dpld); + +#ifdef DEBUG_MODEM_IF + dump = capture_mem_dump(ld, dpld->base, dpld->size); + if (dump) { + trd = trq_get_free_slot(&dpld->trace_list); + memcpy(&trd->ts, &ts, sizeof(struct timespec)); + trd->dev = IPC_DEBUG; + trd->data = dump; + trd->size = dpld->size; + } +#endif + + enable_irq(dpld->irq); - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + mif_err("%s: \n", ld->name, CALLER); - send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + /* Send CRASH_EXIT command to a CP */ + send_int2cp(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack() will be executed. */ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)dpld); + handle_no_cp_crash_ack, (unsigned long)dpld); - return 0; + return; } -static int dpram_init_ipc(struct dpram_link_device *dpld) +/** + * ext_command_handler + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: extended DPRAM command from a CP + * + * Processes an extended command from a CP. + */ +static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) { struct link_device *ld = &dpld->ld; - int i; - - if (ld->mode == LINK_MODE_IPC && - get_magic(dpld) == DPRAM_MAGIC_CODE && - get_access(dpld) == 1) - mif_info("%s: IPC already initialized\n", ld->name); + u16 resp; - /* Clear pointers in every circular queue */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - set_tx_head(dpld, i, 0); - set_tx_tail(dpld, i, 0); - set_rx_head(dpld, i, 0); - set_rx_tail(dpld, i, 0); - } + switch (EXT_CMD_MASK(cmd)) { + case EXT_CMD_SET_SPEED_LOW: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_LOW); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); + send_int2cp(dpld, resp); + } + break; - /* Initialize variables for efficient TX/RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->iod[i] = link_get_iod_with_format(ld, i); - dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + case EXT_CMD_SET_SPEED_MID: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_MID); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); + send_int2cp(dpld, resp); + } + break; - if (dpld->iod[IPC_RAW]->recv_skb) - dpld->use_skb = true; + case EXT_CMD_SET_SPEED_HIGH: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); + send_int2cp(dpld, resp); + } + break; - for (i = 0; i < dpld->max_ipc_dev; i++) { - spin_lock_init(&dpld->tx_lock[i]); - atomic_set(&dpld->res_required[i], 0); - skb_queue_purge(&dpld->skb_rxq[i]); + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + break; } +} - /* Enable IPC */ - atomic_set(&dpld->accessing, 0); - - set_magic(dpld, DPRAM_MAGIC_CODE); - set_access(dpld, 1); - if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) - return -EACCES; +/** + * udl_command_handler + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: DPRAM upload/download command from a CP + * + * Processes a command for upload/download from a CP. + */ +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; - ld->mode = LINK_MODE_IPC; + if (cmd & UDL_RESULT_FAIL) { + mif_err("%s: ERR! command fail (0x%04X)\n", ld->name, cmd); + return; + } - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_err("%s: [CP->AP] CMD_DL_READY (0x%04X)\n", ld->name, cmd); +#ifdef CONFIG_CDMA_MODEM_CBP72 + mif_err("%s: [AP->CP] CMD_DL_START_REQ (0x%04X)\n", + ld->name, CMD_DL_START_REQ); + send_int2cp(dpld, CMD_DL_START_REQ); +#else + complete(&dpld->udl_cmpl); +#endif + break; - return 0; + default: + complete(&dpld->udl_cmpl); + } } +/** + * cmd_req_active_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the REQ_ACTIVE command from a CP. + */ static void cmd_req_active_handler(struct dpram_link_device *dpld) { - send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_int2cp(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); } +/** + * cmd_crash_reset_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the CRASH_RESET command from a CP. + */ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; + int i; ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < ld->max_ipc_dev; i++) + skb_queue_purge(ld->skb_txq[i]); + mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); + /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); iod->modem_state_changed(iod, STATE_CRASH_RESET); + /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_RESET); } +/** + * cmd_crash_exit_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the CRASH_EXIT command from a CP. + */ static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - u32 size = dpld->dpctl->dp_size; - char *dpram_buff = NULL; +#ifdef DEBUG_MODEM_IF + struct trace_data *trd; + u8 *dump; + struct timespec ts; + getnstimeofday(&ts); +#endif ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); - mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + del_timer(&dpld->crash_ack_timer); - dpram_buff = kzalloc(size + (MAX_MIF_SEPA_SIZE * 2), GFP_ATOMIC); - if (!dpram_buff) { - mif_err("DPRAM dump failed!!\n"); - } else { - memset(dpram_buff, 0, size + (MAX_MIF_SEPA_SIZE * 2)); - memcpy(dpram_buff, MIF_SEPARATOR_DPRAM, MAX_MIF_SEPA_SIZE); - memcpy(dpram_buff + MAX_MIF_SEPA_SIZE, &size, sizeof(u32)); - dpram_buff += (MAX_MIF_SEPA_SIZE * 2); - dpram_dump_memory(ld, dpram_buff); + dpram_wake_up(dpld); + +#ifdef DEBUG_MODEM_IF + if (!dpld->forced_cp_crash) { + dump = capture_mem_dump(ld, dpld->base, dpld->size); + if (dump) { + trd = trq_get_free_slot(&dpld->trace_list); + memcpy(&trd->ts, &ts, sizeof(struct timespec)); + trd->dev = IPC_DEBUG; + trd->data = dump; + trd->size = dpld->size; + } } - - del_timer(&dpld->crash_ack_timer); +#endif if (dpld->ext_op && dpld->ext_op->crash_log) dpld->ext_op->crash_log(dpld); + mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); + handle_cp_crash(dpld); } -static void cmd_phone_start_handler(struct dpram_link_device *dpld) +/** + * init_dpram_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * + * Initializes IPC via DPRAM. + */ +static int init_dpram_ipc(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - struct io_device *iod = NULL; - - mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); + int i; - dpram_init_ipc(dpld); + if (ld->mode == LINK_MODE_IPC && + get_magic(dpld) == DPRAM_MAGIC_CODE && + get_access(dpld) == 1) + mif_info("%s: IPC already initialized\n", ld->name); - iod = link_get_iod_with_format(ld, IPC_FMT); - if (!iod) { - mif_info("%s: ERR! no iod\n", ld->name); - return; + /* Clear pointers in every circular queue */ + for (i = 0; i < ld->max_ipc_dev; i++) { + set_txq_head(dpld, i, 0); + set_txq_tail(dpld, i, 0); + set_rxq_head(dpld, i, 0); + set_rxq_tail(dpld, i, 0); } - if (dpld->ext_op && dpld->ext_op->cp_start_handler) - dpld->ext_op->cp_start_handler(dpld); + /* Initialize variables for efficient TX/RX processing */ + for (i = 0; i < ld->max_ipc_dev; i++) + dpld->iod[i] = link_get_iod_with_format(ld, i); + dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - if (ld->mc->phone_state != STATE_ONLINE) { - mif_info("%s: phone_state: %d -> ONLINE\n", - ld->name, ld->mc->phone_state); - iod->modem_state_changed(iod, STATE_ONLINE); - } + /* Initialize variables for TX flow control */ + for (i = 0; i < ld->max_ipc_dev; i++) + atomic_set(&dpld->res_required[i], 0); + + /* Enable IPC */ + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); + + atomic_set(&dpld->accessing, 0); + + set_magic(dpld, DPRAM_MAGIC_CODE); + set_access(dpld, 1); + if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + return -EACCES; + + ld->mode = LINK_MODE_IPC; - mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); + return 0; } -static void command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * reset_dpram_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * + * Reset DPRAM with IPC map. + */ +static void reset_dpram_ipc(struct dpram_link_device *dpld) { + int i; struct link_device *ld = &dpld->ld; - switch (INT_CMD_MASK(cmd)) { - case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(dpld); - break; + dpld->set_access(dpld, 0); + + /* Clear pointers in every circular queue */ + for (i = 0; i < ld->max_ipc_dev; i++) { + dpld->set_txq_head(dpld, i, 0); + dpld->set_txq_tail(dpld, i, 0); + dpld->set_rxq_head(dpld, i, 0); + dpld->set_rxq_tail(dpld, i, 0); + } + + dpld->set_magic(dpld, DPRAM_MAGIC_CODE); + dpld->set_access(dpld, 1); +} + +/** + * cmd_phone_start_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the PHONE_START command from a CP. + */ +static void cmd_phone_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("%s: ERR! no iod\n", ld->name); + return; + } + + init_dpram_ipc(dpld); + + iod->modem_state_changed(iod, STATE_ONLINE); + + if (dpld->ext_op && dpld->ext_op->cp_start_handler) { + dpld->ext_op->cp_start_handler(dpld); + } else { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(dpld, INT_CMD(INT_CMD_INIT_END)); + } +} + +/** + * cmd_handler: processes a DPRAM command from a CP + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: DPRAM command from a CP + */ +static void cmd_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; + + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + cmd_req_active_handler(dpld); + break; case INT_CMD_CRASH_RESET: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + dpld->init_status = DPRAM_INIT_STATE_NONE; cmd_crash_reset_handler(dpld); break; case INT_CMD_CRASH_EXIT: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + dpld->init_status = DPRAM_INIT_STATE_NONE; cmd_crash_exit_handler(dpld); break; case INT_CMD_PHONE_START: - dpld->dpram_init_status = DPRAM_INIT_STATE_READY; + dpld->init_status = DPRAM_INIT_STATE_READY; cmd_phone_start_handler(dpld); - complete_all(&dpld->dpram_init_cmd); + complete_all(&ld->init_cmpl); break; case INT_CMD_NV_REBUILDING: @@ -1143,14 +1100,14 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&dpld->modem_pif_init_done); + complete_all(&ld->pif_cmpl); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_PWR_OFF: + case INT_CMD_NORMAL_POWER_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1165,213 +1122,1143 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) } } -static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * ipc_rx_work + * @work: pointer to an instance of the work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void ipc_rx_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct io_device *iod; + struct sk_buff *skb; + int i; + + dpld = container_of(work, struct dpram_link_device, rx_dwork.work); + ld = &dpld->ld; + + for (i = 0; i < ld->max_ipc_dev; i++) { + iod = dpld->iod[i]; + while (1) { + skb = skb_dequeue(ld->skb_rxq[i]); + if (!skb) + break; + + if (iod->recv_skb) { + iod->recv_skb(iod, ld, skb); + } else { + iod->recv(iod, ld, skb->data, skb->len); + dev_kfree_skb_any(skb); + } + } + } +} + +/** + * get_rxq_rcvd + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @dcst: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a RXQ, size of the buffer, in & out + * pointer values, size of received data} into the 'stat' instance. + * + * Returns an error code. + */ +static int get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + struct mem_status *mst, struct circ_status *dcst) { struct link_device *ld = &dpld->ld; - u16 resp; - switch (EXT_CMD_MASK(cmd)) { - case EXT_CMD_SET_SPEED_LOW: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); - send_intr(dpld, resp); + dcst->buff = get_rxq_buff(dpld, dev); + dcst->qsize = get_rxq_buff_size(dpld, dev); + dcst->in = mst->head[dev][RX]; + dcst->out = mst->tail[dev][RX]; + dcst->size = circ_get_usage(dcst->qsize, dcst->in, dcst->out); + + if (circ_valid(dcst->qsize, dcst->in, dcst->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), dcst->qsize, dcst->in, + dcst->out, dcst->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), dcst->qsize, dcst->in, + dcst->out); + return -EIO; + } +} + +/** + * rx_sipc4_frames + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a bottom half (e.g. ipc_rx_task) that will invoke the recv method in + * the io_device instance. + */ +static int rx_sipc4_frames(struct dpram_link_device *dpld, int dev, + struct mem_status *mst) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff *skb; + u8 *dst; + struct circ_status dcst; + int rcvd; + + rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); + if (unlikely(rcvd < 0)) { +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(dpld); +#endif + goto exit; + } + rcvd = dcst.size; + + /* Allocate an skb */ + skb = dev_alloc_skb(rcvd); + if (!skb) { + mif_info("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + rcvd = -ENOMEM; + goto exit; + } + + /* Read data from the RXQ */ + dst = skb_put(skb, rcvd); + circ_read16_from_io(dst, dcst.buff, dcst.qsize, dcst.out, rcvd); + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + +exit: + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(dpld, dev, dcst.in); + + return rcvd; +} + +/** + * rx_sipc5_frames + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_sipc5_frames(struct dpram_link_device *dpld, int dev, + struct mem_status *mst) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff *skb; + /** + * variables for the status of the circular queue + */ + u8 __iomem *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + struct circ_status dcst; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int idx; /* index to the start of current frame */ + u8 *frm; /* pointer to current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + /** + * variables for debug logging + */ + struct mif_irq_map map; + + /* Get data size in the RXQ and in/out pointer values */ + rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); + if (unlikely(rcvd < 0)) { + mif_err("%s: ERR! rcvd %d < 0\n", ld->name, rcvd); + goto exit; + } + + rcvd = dcst.size; + src = dcst.buff; + qsize = dcst.qsize; + idx = dcst.out; + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + +#if 0 + skb = dev_alloc_skb(rcvd); + + /* + ** If there is enough free space for an skb to store received + ** data at once, + */ + if (skb) { + /* Read all data from the RXQ to the skb */ + dst = skb_put(skb, rcvd); + if (unlikely(dpld->strict_io_access)) + circ_read16_from_io(dst, src, qsize, idx, rcvd); + else + circ_read(dst, src, qsize, idx, rcvd); + +#ifdef DEBUG_MODEM_IF + /* Verify data copied to the skb */ + if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + rcvd = -EIO; + goto exit; } - break; +#endif - case EXT_CMD_SET_SPEED_MID: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_MID); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); - send_intr(dpld, resp); + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + + goto exit; + } + + /* + ** If there was no enough space to store received data at once, + */ +#endif + + rest = rcvd; + while (rest > 0) { + /* Calculate the start of an SIPC5 frame */ + frm = src + idx; + + /* Copy the header in the frame to the header buffer */ + if (unlikely(dpld->strict_io_access)) + memcpy16_from_io(hdr, frm, SIPC5_MIN_HEADER_SIZE); + else + memcpy(hdr, frm, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: BAD CONFIG", + ld->mc->name); + mif_err("%s: ERR! %s INVALID config 0x%02X\n", + ld->name, get_dev_name(dev), hdr[0]); + pr_ipc(1, str, hdr, 4); + rcvd = -EBADMSG; + goto exit; } - break; - case EXT_CMD_SET_SPEED_HIGH: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); - send_intr(dpld, resp); + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: BAD LENGTH", + ld->mc->name); + mif_err("%s: ERR! %s tot %d > rest %d\n", + ld->name, get_dev_name(dev), tot, rest); + pr_ipc(1, str, hdr, 4); + rcvd = -EBADMSG; +#if defined(CONFIG_MACH_C1_KOR_SKT) || defined(CONFIG_MACH_C1_KOR_KT) || defined(CONFIG_MACH_C1_KOR_LGT) + return rcvd; +#else + goto exit; +#endif } - break; - default: - mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); - break; + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + rcvd = -ENOMEM; + goto exit; + } + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + if (unlikely(dpld->strict_io_access)) + circ_read16_from_io(dst, src, qsize, idx, tot); + else + circ_read(dst, src, qsize, idx, tot); + +#ifdef DEBUG_MODEM_IF + /* Take a log for debugging */ + if (unlikely(dev == IPC_FMT)) { + size_t len = (skb->len > 32) ? 32 : skb->len; + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: CP2MIF", + ld->mc->name); + pr_ipc(0, str, skb->data, len); + } +#endif + +#ifdef DEBUG_MODEM_IF + /* Verify data copied to the skb */ + if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + rcvd = -EIO; + goto exit; + } +#endif + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + + /* Calculate new idx value */ + rest -= tot; + idx += tot; + if (unlikely(idx >= qsize)) + idx -= qsize; } + +exit: +#ifdef DEBUG_MODEM_IF + if (rcvd < 0) + trigger_forced_cp_crash(dpld); +#endif + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(dpld, dev, dcst.in); + + return rcvd; } -static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * msg_handler: receives IPC messages from every RXQ + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every DPRAM RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void msg_handler(struct dpram_link_device *dpld, struct mem_status *stat) { struct link_device *ld = &dpld->ld; + int i = 0; + int ret = 0; + u16 mask = 0; + u16 intr = stat->int2ap; - if (cmd & UDL_RESULT_FAIL) { - mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); + if (!ipc_active(dpld)) return; + + /* Read data from DPRAM */ + for (i = 0; i < ld->max_ipc_dev; i++) { + /* Invoke an RX function only when there is data in the RXQ */ + if (unlikely(stat->head[i][RX] == stat->tail[i][RX])) { + mif_debug("%s: %s_RXQ is empty\n", + ld->name, get_dev_name(i)); + } else { + if (unlikely(ld->ipc_version < SIPC_VER_50)) + ret = rx_sipc4_frames(dpld, i, stat); + else + ret = rx_sipc5_frames(dpld, i, stat); + if (ret < 0) + reset_rxq_circ(dpld, i); + } } - switch (UDL_CMD_MASK(cmd)) { - case UDL_CMD_RECV_READY: - mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); - send_intr(dpld, CMD_IMG_START_REQ); - break; - default: - complete_all(&dpld->udl_cmd_complete); + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &dpld->rx_dwork, 0); + + /* Check and process REQ_ACK (at this time, in == out) */ + if (unlikely(intr & INT_MASK_REQ_ACK_SET)) { + for (i = 0; i < ld->max_ipc_dev; i++) { + if (intr & get_mask_req_ack(dpld, i)) { + mif_debug("%s: set %s_RES_ACK\n", + ld->name, get_dev_name(i)); + mask |= get_mask_res_ack(dpld, i); + } + } + + send_int2cp(dpld, INT_NON_CMD(mask)); + } + + /* Check and process RES_ACK */ + if (unlikely(intr & INT_MASK_RES_ACK_SET)) { + for (i = 0; i < ld->max_ipc_dev; i++) { + if (intr & get_mask_res_ack(dpld, i)) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv %s_RES_ACK\n", + ld->name, get_dev_name(i)); + print_circ_status(ld, i, stat); +#endif + complete(&dpld->req_ack_cmpl[i]); + } + } + } +} + +/** + * cmd_msg_handler: processes a DPRAM command or receives IPC messages + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * Invokes cmd_handler for a DPRAM command or msg_handler for IPC messages. + */ +static inline void cmd_msg_handler(struct dpram_link_device *dpld, + struct mem_status *stat) +{ + struct dpram_ext_op *ext_op = dpld->ext_op; + struct mem_status *mst = msq_get_free_slot(&dpld->stat_list); + u16 intr = stat->int2ap; + + memcpy(mst, stat, sizeof(struct mem_status)); + + if (unlikely(INT_CMD_VALID(intr))) { + if (ext_op && ext_op->cmd_handler) + ext_op->cmd_handler(dpld, intr); + else + cmd_handler(dpld, intr); + } else { + msg_handler(dpld, stat); } } -static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr) +/** + * intr_handler: processes an interrupt from a CP + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * Call flow for normal interrupt handling: + * cmd_msg_handler -> cmd_handler -> cmd_xxx_handler + * cmd_msg_handler -> msg_handler -> rx_sipc5_frames -> ... + */ +static inline void intr_handler(struct dpram_link_device *dpld, + struct mem_status *stat) { - if (unlikely(INT_CMD_VALID(intr))) - command_handler(dpld, intr); + char *name = dpld->ld.name; + u16 intr = stat->int2ap; + + if (unlikely(intr == INT_POWERSAFE_FAIL)) { + mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); + return; + } + + if (unlikely(EXT_UDL_CMD(intr))) { + if (likely(EXT_INT_VALID(intr))) { + if (UDL_CMD_VALID(intr)) + udl_command_handler(dpld, intr); + else if (EXT_CMD_VALID(intr)) + ext_command_handler(dpld, intr); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", + name, intr); + } else { + mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); + } + + return; + } + + if (likely(INT_VALID(intr))) + cmd_msg_handler(dpld, stat); + else + mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); +} + +/** + * ap_idpram_irq_handler: interrupt handler for an internal DPRAM in an AP + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ap_idpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + struct modemlink_dpram_data *dpram = dpld->dpram; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; + + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + if (likely(dpram->clear_int2ap)) + dpram->clear_int2ap(); + + return IRQ_HANDLED; +} + +/** + * cp_idpram_irq_handler: interrupt handler for an internal DPRAM in a CP + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Wakes up the DPRAM + * 2) Reads the interrupt value + * 3) Performs interrupt handling + * 4) Clears the interrupt port (port = memory or register) + * 5) Allows the DPRAM to sleep + */ +static irqreturn_t cp_idpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + struct dpram_ext_op *ext_op = dpld->ext_op; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { + mif_err("%s: ERR! ld->mode == LINK_MODE_OFFLINE\n", ld->name); + get_dpram_status(dpld, RX, &stat); +#ifdef DEBUG_MODEM_IF + print_mem_status(ld, &stat); +#endif + return IRQ_HANDLED; + } + + if (dpram_wake_up(dpld) < 0) { + trigger_forced_cp_crash(dpld); + return IRQ_HANDLED; + } + + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + if (likely(ext_op && ext_op->clear_int2ap)) + ext_op->clear_int2ap(dpld); + + dpram_allow_sleep(dpld); + + return IRQ_HANDLED; +} + +/** + * ext_dpram_irq_handler: interrupt handler for a normal external DPRAM + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ext_dpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; + + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + return IRQ_HANDLED; +} + +/** + * get_txq_space + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * OUT @stat: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of free space} into the 'stat' instance. + * + * Returns the size of free space in the buffer or an error code. + */ +static int get_txq_space(struct dpram_link_device *dpld, int dev, + struct circ_status *stat) +{ + struct link_device *ld = &dpld->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int space; + + while (1) { + qsize = get_txq_buff_size(dpld, dev); + head = get_txq_head(dpld, dev); + tail = get_txq_tail(dpld, dev); + space = circ_get_space(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + stat->buff = get_txq_buff(dpld, dev); + stat->qsize = qsize; + stat->in = head; + stat->out = tail; + stat->size = space; + + return space; +} + +/** + * write_ipc_to_txq + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @stat: pointer to an instance of circ_status structure + * @skb: pointer to an instance of sk_buff structure + * + * Must be invoked only when there is enough space in the TXQ. + */ +static void write_ipc_to_txq(struct dpram_link_device *dpld, int dev, + struct circ_status *stat, struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *buff = stat->buff; + u32 qsize = stat->qsize; + u32 in = stat->in; + u8 *src = skb->data; + u32 len = skb->len; + struct mif_irq_map map; + + /* Write data to the TXQ */ + if (unlikely(dpld->strict_io_access)) + circ_write16_to_io(buff, src, qsize, in, len); else - non_command_handler(dpld, intr); + circ_write(buff, src, qsize, in, len); + + /* Update new head (in) pointer */ + set_txq_head(dpld, dev, circ_new_pointer(qsize, in, len)); + + /* Take a log for debugging */ + if (dev == IPC_FMT) { +#ifdef DEBUG_MODEM_IF + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2CP", ld->mc->name); + pr_ipc(0, tag, src, (len > 32 ? 32 : len)); +#endif + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); + mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); + } + +#ifdef DEBUG_MODEM_IF + /* Verify data written to the TXQ */ + if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + trigger_forced_cp_crash(dpld); + } +#endif +} + +/** + * xmit_ipc_msg + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. + * + * Returns total length of IPC messages transmitted or an error code. + */ +static int xmit_ipc_msg(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long flags; + struct circ_status stat; + int space; + int copied = 0; + + /* Acquire the spin lock for a TXQ */ + spin_lock_irqsave(&dpld->tx_lock[dev], flags); + + while (1) { + /* Get the size of free space in the TXQ */ + space = get_txq_space(dpld, dev, &stat); + if (unlikely(space < 0)) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(dpld); +#endif + /* Empty out the TXQ */ + reset_txq_circ(dpld, dev); + copied = -EIO; + break; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + /* Check the free space size comparing with skb->len */ + if (unlikely(space < skb->len)) { +#ifdef DEBUG_MODEM_IF + struct mem_status mst; +#endif + /* Set res_required flag for the "dev" */ + atomic_set(&dpld->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_info("%s: NOSPC in %s_TXQ" + "{qsize:%u in:%u out:%u}, free:%u < len:%u\n", + ld->name, CALLER, get_dev_name(dev), + stat.qsize, stat.in, stat.out, space, skb->len); +#ifdef DEBUG_MODEM_IF + get_dpram_status(dpld, TX, &mst); + print_circ_status(ld, dev, &mst); +#endif + copied = -ENOSPC; + break; + } + + /* TX only when there is enough space in the TXQ */ + write_ipc_to_txq(dpld, dev, &stat, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + /* Release the spin lock */ + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + + return copied; +} + +/** + * wait_for_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Sends an REQ_ACK interrupt for @dev to CP. + * 2) Waits for the corresponding RES_ACK for @dev from CP. + * + * Returns the return value from wait_for_completion_interruptible_timeout(). + */ +static int wait_for_res_ack(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct completion *cmpl = &dpld->req_ack_cmpl[dev]; + unsigned long timeout = msecs_to_jiffies(dpld->res_ack_wait_timeout); + int ret; + u16 mask; + +#ifdef DEBUG_MODEM_IF + mif_info("%s: send %s_REQ_ACK\n", ld->name, get_dev_name(dev)); +#endif + + mask = get_mask_req_ack(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + + ret = wait_for_completion_interruptible_timeout(cmpl, timeout); + /* ret == 0 on timeout, ret < 0 if interrupted */ + if (ret < 0) { + mif_info("%s: %s: wait_for_completion interrupted! (ret %d)\n", + ld->name, get_dev_name(dev), ret); + goto exit; + } + + if (ret == 0) { + struct mem_status mst; + get_dpram_status(dpld, TX, &mst); + + mif_info("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", + ld->name, get_dev_name(dev)); + + /* + ** The TXQ must be checked whether or not it is empty, because + ** an interrupt mask can be overwritten by the next interrupt. + */ + if (mst.head[dev][TX] == mst.tail[dev][TX]) { + ret = get_txq_buff_size(dpld, dev); +#ifdef DEBUG_MODEM_IF + mif_info("%s: %s_TXQ has been emptied\n", + ld->name, get_dev_name(dev)); + print_circ_status(ld, dev, &mst); +#endif + } + } + +exit: + return ret; +} + +/** + * process_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Restarts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + * + * Returns the return value from xmit_ipc_msg(). + */ +static int process_res_ack(struct dpram_link_device *dpld, int dev) +{ + int ret; + u16 mask; + + ret = xmit_ipc_msg(dpld, dev); + if (ret > 0) { + mask = get_mask_send(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + } + + if (ret >= 0) + atomic_set(&dpld->res_required[dev], 0); + + return ret; } -static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr) -{ - char *name = dpld->ld.name; +/** + * fmt_tx_work: performs TX for FMT IPC device under DPRAM flow control + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of FMT IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. + */ +static void fmt_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, fmt_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_FMT); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); + return; + } + + ret = process_res_ack(dpld, IPC_FMT); + if (ret >= 0) { + dpram_allow_sleep(dpld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], delay); +} + +/** + * raw_tx_work: performs TX for RAW IPC device under DPRAM flow control. + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RAW IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. + */ +static void raw_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, raw_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_RAW); + /* ret < 0 if interrupted */ + if (ret < 0) + return; - if (unlikely(intr == INT_POWERSAFE_FAIL)) { - mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); return; } - if (unlikely(EXT_UDL_CMD(intr))) { - if (likely(EXT_INT_VALID(intr))) { - if (UDL_CMD_VALID(intr)) - udl_command_handler(dpld, intr); - else if (EXT_CMD_VALID(intr)) - ext_command_handler(dpld, intr); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", - name, intr); - } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); - } + ret = process_res_ack(dpld, IPC_RAW); + if (ret >= 0) { + dpram_allow_sleep(dpld); + mif_netif_wake(ld); return; } - if (likely(INT_VALID(intr))) - dpram_ipc_rx(dpld, intr); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], delay); } -static irqreturn_t ap_idpram_irq_handler(int irq, void *data) +/** + * rfs_tx_work: performs TX for RFS IPC device under DPRAM flow control + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RFS IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RFS IPC device. + */ +static void rfs_tx_work(struct work_struct *work) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - u16 int2ap = recv_intr(dpld); + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; + ld = container_of(work, struct link_device, rfs_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_RFS); + /* ret < 0 if interrupted */ + if (ret < 0) + return; - dpram_intr_handler(dpld, int2ap); + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], 0); + return; + } - return IRQ_HANDLED; + ret = process_res_ack(dpld, IPC_RFS); + if (ret >= 0) { + dpram_allow_sleep(dpld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], delay); } -static irqreturn_t cp_idpram_irq_handler(int irq, void *data) +/** + * dpram_send_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static int dpram_send_ipc(struct dpram_link_device *dpld, int dev) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - u16 int2ap; + struct link_device *ld = &dpld->ld; + int ret; + u16 mask; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; + if (atomic_read(&dpld->res_required[dev]) > 0) { + mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + return 0; + } if (dpram_wake_up(dpld) < 0) { - log_dpram_status(dpld); - trigger_force_cp_crash(dpld); - return IRQ_HANDLED; + trigger_forced_cp_crash(dpld); + return -EIO; + } + + if (!ipc_active(dpld)) { + mif_info("%s: IPC is NOT active\n", ld->name); + ret = -EIO; + goto exit; + } + + ret = xmit_ipc_msg(dpld, dev); + if (likely(ret > 0)) { + mask = get_mask_send(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + goto exit; } - int2ap = recv_intr(dpld); + /* If there was no TX, just exit */ + if (ret == 0) + goto exit; + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) { + /* Prohibit DPRAM from sleeping until the TXQ buffer is empty */ + if (dpram_wake_up(dpld) < 0) { + trigger_forced_cp_crash(dpld); + goto exit; + } - dpram_intr_handler(dpld, int2ap); + /*----------------------------------------------------*/ + /* dpld->res_required[dev] was set in xmit_ipc_msg(). */ + /*----------------------------------------------------*/ - clear_intr(dpld); + if (dev == IPC_RAW) + mif_netif_stop(ld); - dpram_allow_sleep(dpld); + queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], 0); + } - return IRQ_HANDLED; +exit: + dpram_allow_sleep(dpld); + return ret; } -static irqreturn_t ext_dpram_irq_handler(int irq, void *data) +/** + * pm_tx_work: performs TX while DPRAM PM is locked + * @work: pointer to an instance of the work_struct structure + */ +static void pm_tx_work(struct work_struct *work) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - u16 int2ap = recv_intr(dpld); + struct idpram_pm_data *pm_data; + struct idpram_pm_op *pm_op; + struct dpram_link_device *dpld; + struct link_device *ld; + struct workqueue_struct *pm_wq = system_nrt_wq; + int i; + int ret; + unsigned long delay = 0; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; + pm_data = container_of(work, struct idpram_pm_data, tx_dwork.work); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + ld = &dpld->ld; + pm_op = dpld->pm_op; - dpram_intr_handler(dpld, int2ap); + if (pm_op->locked(dpld)) { + queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); + return; + } - return IRQ_HANDLED; + /* Here, PM is not locked. */ + for (i = 0; i < ld->max_ipc_dev; i++) { + ret = dpram_send_ipc(dpld, i); + if (ret < 0) { + struct io_device *iod = dpld->iod[i]; + mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } + } } -static void dpram_send_ipc(struct link_device *ld, int dev, +/** + * dpram_try_send_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 4) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static void dpram_try_send_ipc(struct dpram_link_device *dpld, int dev, struct io_device *iod, struct sk_buff *skb) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct link_device *ld = &dpld->ld; + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct idpram_pm_op *pm_op = dpld->pm_op; + struct workqueue_struct *pm_wq = system_nrt_wq; + unsigned long delay = msecs_to_jiffies(10); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; - u16 mask; - skb_queue_tail(txq, skb); - if (txq->qlen > 1024) { - mif_debug("%s: %s txq->qlen %d > 1024\n", - ld->name, get_dev_name(dev), txq->qlen); + if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { + mif_info("%s: %s txq->qlen %d >= %d\n", ld->name, + get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); + dev_kfree_skb_any(skb); + return; } - if (dpld->dp_type == CP_IDPRAM) { - if (dpram_wake_up(dpld) < 0) { - trigger_force_cp_crash(dpld); + skb_queue_tail(txq, skb); + + if (pm_op && pm_op->locked) { + if (pm_op->locked(dpld)) { + queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); return; } - } - if (!dpram_ipc_active(dpld)) - goto exit; - - if (atomic_read(&dpld->res_required[dev]) > 0) { - mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); - goto exit; + /* Here, PM is not locked. */ + if (work_pending(&pm_data->tx_dwork.work)) + cancel_delayed_work_sync(&pm_data->tx_dwork); } - ret = dpram_try_ipc_tx(dpld, dev); - if (ret > 0) { - mask = get_mask_send(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); - } else if (ret == -ENOSPC) { - mask = get_mask_req_ack(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); - mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); - } else { - mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); + ret = dpram_send_ipc(dpld, dev); + if (ret < 0) { + mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); } - -exit: - if (dpld->dp_type == CP_IDPRAM) - dpram_allow_sleep(dpld); } static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - if (dpld->ext_op && dpld->ext_op->download_binary) - return dpld->ext_op->download_binary(dpld, skb); + if (dpld->ext_op && dpld->ext_op->xmit_binary) + return dpld->ext_op->xmit_binary(dpld, skb); else return -ENODEV; } +/** + * dpram_send + * @ld: pointer to an instance of the link_device structure + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * Returns the length of data transmitted or an error code. + * + * Normal call flow for an IPC message: + * dpram_try_send_ipc -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on PM lock in a DPRAM IPC TXQ: + * dpram_try_send_ipc ,,, queue_delayed_work + * => pm_tx_work -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on congestion in a DPRAM IPC TXQ: + * dpram_try_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work + * => xxx_tx_work -> wait_for_res_ack + * => msg_handler + * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) + */ static int dpram_send(struct link_device *ld, struct io_device *iod, - struct sk_buff *skb) + struct sk_buff *skb) { - enum dev_format dev = iod->format; + struct dpram_link_device *dpld = to_dpram_link_device(ld); + int dev = iod->format; int len = skb->len; switch (dev) { @@ -1379,7 +2266,7 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_send_ipc(ld, dev, iod, skb); + dpram_try_send_ipc(dpld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); @@ -1396,23 +2283,45 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static int dpram_xmit_boot(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->xmit_boot) + return dpld->ext_op->xmit_boot(dpld, arg); + else + return -ENODEV; +} + +static int dpram_set_dload_magic(struct link_device *ld, struct io_device *iod) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - trigger_force_cp_crash(dpld); + + ld->mode = LINK_MODE_DLOAD; + + mif_err("%s: magic = 0x%08X\n", ld->name, DP_MAGIC_DMDL); + iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); + return 0; } -static void dpram_dump_memory(struct link_device *ld, char *buff) +static int dpram_dload_firmware(struct link_device *ld, struct io_device *iod, + unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *base = dpld->dpctl->dp_base; - u32 size = dpld->dpctl->dp_size; - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + if (dpld->ext_op && dpld->ext_op->firm_update) + return dpld->ext_op->firm_update(dpld, arg); + else + return -ENODEV; +} - memcpy(buff, base, size); +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + trigger_forced_cp_crash(dpld); + return 0; } static int dpram_dump_start(struct link_device *ld, struct io_device *iod) @@ -1431,13 +2340,24 @@ static int dpram_dump_update(struct link_device *ld, struct io_device *iod, struct dpram_link_device *dpld = to_dpram_link_device(ld); if (dpld->ext_op && dpld->ext_op->dump_update) - return dpld->ext_op->dump_update(dpld, (void *)arg); + return dpld->ext_op->dump_update(dpld, arg); + else + return -ENODEV; +} + +static int dpram_dump_finish(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dump_finish) + return dpld->ext_op->dump_finish(dpld, arg); else return -ENODEV; } static int dpram_ioctl(struct link_device *ld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); int err = 0; @@ -1447,17 +2367,21 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + return dpld->init_status; - default: - if (dpld->ext_ioctl) { - err = dpld->ext_ioctl(dpld, iod, cmd, arg); - } else { - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - } + case IOCTL_MIF_DPRAM_DUMP: + if (copy_to_user((void __user *)arg, &dpld->size, sizeof(u32))) + return -EFAULT; + capture_dpram_snapshot(ld, iod); break; + + default: + if (dpld->ext_ioctl) + return dpld->ext_ioctl(dpld, iod, cmd, arg); + + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + return -EINVAL; } return err; @@ -1468,9 +2392,9 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) struct dpram_ipc_16k_map *dpram_map; struct dpram_ipc_device *dev; - dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base; + dpram_map = (struct dpram_ipc_16k_map *)dpld->base; - /* magic code and access enable fields */ + /* "magic code" and "access enable" fields */ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; @@ -1519,167 +2443,201 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp; } -static int dpram_table_init(struct dpram_link_device *dpld) +static int dpram_init_boot_map(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - u8 __iomem *dp_base; - int i; - - if (!dpld->dp_base) { - mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); - return -EINVAL; - } - dp_base = dpld->dp_base; - - /* Map for IPC */ - if (dpld->dpctl->ipc_map) { - memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, - sizeof(struct dpram_ipc_map)); - } else { - if (dpld->dp_size == DPRAM_SIZE_16KB) - dpram_remap_std_16k_region(dpld); - else - return -EINVAL; - } - - dpld->magic = dpld->ipc_map.magic; - dpld->access = dpld->ipc_map.access; - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->dev[i] = &dpld->ipc_map.dev[i]; - dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; - dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; - /* Map for booting */ if (dpld->ext_op && dpld->ext_op->init_boot_map) { dpld->ext_op->init_boot_map(dpld); } else { dpld->bt_map.magic = (u32 *)(dp_base); - dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); - dpld->bt_map.size = dpld->dp_size - 8; + dpld->bt_map.buff = (u8 *)(dp_base + magic_size); + dpld->bt_map.space = dpld->size - (magic_size + mbx_size); } - /* Map for download (FOTA, UDL, etc.) */ + return 0; +} + +static int dpram_init_dload_map(struct dpram_link_device *dpld) +{ + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; + if (dpld->ext_op && dpld->ext_op->init_dl_map) { dpld->ext_op->init_dl_map(dpld); } else { dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.buff = (u8 *)(dp_base + magic_size); + dpld->dl_map.space = dpld->size - (magic_size + mbx_size); } - /* Map for upload mode */ + return 0; +} + +static int dpram_init_uload_map(struct dpram_link_device *dpld) +{ + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; + if (dpld->ext_op && dpld->ext_op->init_ul_map) { dpld->ext_op->init_ul_map(dpld); } else { dpld->ul_map.magic = (u32 *)(dp_base); dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + dpld->ul_map.space = dpld->size - (magic_size + mbx_size); + } + + return 0; +} + +static int dpram_init_ipc_map(struct dpram_link_device *dpld) +{ + int i; + struct link_device *ld = &dpld->ld; + + if (dpld->ext_op && dpld->ext_op->init_ipc_map) { + dpld->ext_op->init_ipc_map(dpld); + } else if (dpld->dpram->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpram->ipc_map, + sizeof(struct dpram_ipc_map)); + } else { + if (dpld->size == DPRAM_SIZE_16KB) + dpram_remap_std_16k_region(dpld); + else + return -EINVAL; } + dpld->magic = dpld->ipc_map.magic; + dpld->access = dpld->ipc_map.access; + for (i = 0; i < ld->max_ipc_dev; i++) + dpld->dev[i] = &dpld->ipc_map.dev[i]; + dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; + dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + return 0; } static void dpram_setup_common_op(struct dpram_link_device *dpld) { - dpld->clear_intr = clear_intr; - dpld->recv_intr = recv_intr; - dpld->send_intr = send_intr; + dpld->recv_intr = recv_int2ap; + dpld->send_intr = send_int2cp; dpld->get_magic = get_magic; dpld->set_magic = set_magic; dpld->get_access = get_access; dpld->set_access = set_access; - dpld->get_tx_head = get_tx_head; - dpld->get_tx_tail = get_tx_tail; - dpld->set_tx_head = set_tx_head; - dpld->set_tx_tail = set_tx_tail; - dpld->get_tx_buff = get_tx_buff; - dpld->get_tx_buff_size = get_tx_buff_size; - dpld->get_rx_head = get_rx_head; - dpld->get_rx_tail = get_rx_tail; - dpld->set_rx_head = set_rx_head; - dpld->set_rx_tail = set_rx_tail; - dpld->get_rx_buff = get_rx_buff; - dpld->get_rx_buff_size = get_rx_buff_size; + dpld->get_txq_head = get_txq_head; + dpld->get_txq_tail = get_txq_tail; + dpld->set_txq_head = set_txq_head; + dpld->set_txq_tail = set_txq_tail; + dpld->get_txq_buff = get_txq_buff; + dpld->get_txq_buff_size = get_txq_buff_size; + dpld->get_rxq_head = get_rxq_head; + dpld->get_rxq_tail = get_rxq_tail; + dpld->set_rxq_head = set_rxq_head; + dpld->set_rxq_tail = set_rxq_tail; + dpld->get_rxq_buff = get_rxq_buff; + dpld->get_rxq_buff_size = get_rxq_buff_size; dpld->get_mask_req_ack = get_mask_req_ack; dpld->get_mask_res_ack = get_mask_res_ack; dpld->get_mask_send = get_mask_send; - dpld->ipc_rx_handler = dpram_ipc_rx; -} - -static int dpram_link_init(struct link_device *ld, struct io_device *iod) -{ - return 0; + dpld->get_dpram_status = get_dpram_status; + dpld->ipc_rx_handler = cmd_msg_handler; + dpld->reset_dpram_ipc = reset_dpram_ipc; } static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) { + if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { + if (!atomic_read(&iod->opened)) { + ld->mode = LINK_MODE_OFFLINE; + mif_err("%s: %s: link mode is changed: IPC->OFFLINE\n", + iod->name, ld->name); + } + } + return; } struct link_device *dpram_create_link_device(struct platform_device *pdev) { - struct modem_data *mdm_data = NULL; struct dpram_link_device *dpld = NULL; struct link_device *ld = NULL; + struct modem_data *modem = NULL; + struct modemlink_dpram_data *dpram = NULL; struct resource *res = NULL; resource_size_t res_size; - struct modemlink_dpram_control *dpctl = NULL; - unsigned long task_data; int ret = 0; int i = 0; - int bsize; - int qsize; - /* Get the platform data */ - mdm_data = (struct modem_data *)pdev->dev.platform_data; - if (!mdm_data) { - mif_info("ERR! mdm_data == NULL\n"); + /* + ** Alloc an instance of dpram_link_device structure + */ + dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); + if (!dpld) { + mif_err("ERR! kzalloc dpld fail\n"); goto err; } - mif_info("modem = %s\n", mdm_data->name); - mif_info("link device = %s\n", mdm_data->link_name); + ld = &dpld->ld; - if (!mdm_data->dpram_ctl) { - mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); + /* + ** Get the modem (platform) data + */ + modem = (struct modem_data *)pdev->dev.platform_data; + if (!modem) { + mif_err("ERR! modem == NULL\n"); goto err; } - dpctl = mdm_data->dpram_ctl; - - /* Alloc DPRAM link device structure */ - dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); - if (!dpld) { - mif_info("ERR! kzalloc dpld fail\n"); + mif_info("modem = %s\n", modem->name); + mif_info("link device = %s\n", modem->link_name); + + /* + ** Retrieve modem data and DPRAM control data from the modem data + */ + ld->mdm_data = modem; + ld->name = modem->link_name; + ld->ipc_version = modem->ipc_version; + + if (!modem->dpram) { + mif_err("ERR! no modem->dpram\n"); goto err; } - ld = &dpld->ld; - - /* Retrieve modem data and DPRAM control data from the modem data */ - ld->mdm_data = mdm_data; - ld->name = mdm_data->link_name; - ld->ipc_version = mdm_data->ipc_version; + dpram = modem->dpram; - /* Retrieve the most basic data for IPC from the modem data */ - dpld->dpctl = dpctl; - dpld->dp_type = dpctl->dp_type; + dpld->dpram = dpram; + dpld->type = dpram->type; + dpld->ap = dpram->ap; + dpld->strict_io_access = dpram->strict_io_access; - if (mdm_data->ipc_version < SIPC_VER_50) { - if (!dpctl->max_ipc_dev) { - mif_info("ERR! no max_ipc_dev\n"); + if (ld->ipc_version < SIPC_VER_50) { + if (!modem->max_ipc_dev) { + mif_err("%s: ERR! no max_ipc_dev\n", ld->name); goto err; } - ld->aligned = dpctl->aligned; - dpld->max_ipc_dev = dpctl->max_ipc_dev; + ld->aligned = dpram->aligned; + ld->max_ipc_dev = modem->max_ipc_dev; } else { ld->aligned = 1; - dpld->max_ipc_dev = MAX_SIPC5_DEV; + ld->max_ipc_dev = MAX_SIPC5_DEV; } - /* Set attributes as a link device */ - ld->init_comm = dpram_link_init; + /* + ** Set attributes as a link device + */ ld->terminate_comm = dpram_link_terminate; ld->send = dpram_send; + ld->xmit_boot = dpram_xmit_boot; + ld->dload_start = dpram_set_dload_magic; + ld->firm_update = dpram_dload_firmware; ld->force_dump = dpram_force_dump; ld->dump_start = dpram_dump_start; ld->dump_update = dpram_dump_update; + ld->dump_finish = dpram_dump_finish; + /* IOCTL extension */ ld->ioctl = dpram_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1691,129 +2649,217 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; - /* Set up function pointers */ + skb_queue_head_init(&ld->sk_fmt_rx_q); + skb_queue_head_init(&ld->sk_raw_rx_q); + skb_queue_head_init(&ld->sk_rfs_rx_q); + ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; + ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; + ld->skb_rxq[IPC_RFS] = &ld->sk_rfs_rx_q; + + init_completion(&ld->init_cmpl); + init_completion(&ld->pif_cmpl); + + /* + ** Set up function pointers + */ dpram_setup_common_op(dpld); - dpld->dpram_dump = dpram_dump_memory; - dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); - if (dpld->ext_op && dpld->ext_op->ioctl) - dpld->ext_ioctl = dpld->ext_op->ioctl; - - /* Retrieve DPRAM resource */ - if (!dpctl->dp_base) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dpld->ext_op = dpram_get_ext_op(modem->modem_type); + if (dpld->ext_op) { + if (dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; + + if (dpld->ext_op->wakeup && dpld->ext_op->sleep) + dpld->need_wake_up = true; + } + + /* + ** Retrieve DPRAM resource + */ + if (!dpram->base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + STR_DPRAM_BASE); if (!res) { - mif_info("%s: ERR! platform_get_resource fail\n", + mif_err("%s: ERR! no DPRAM resource\n", ld->name); + goto err; + } + res_size = resource_size(res); + + dpram->base = ioremap_nocache(res->start, res_size); + if (!dpram->base) { + mif_err("%s: ERR! ioremap_nocache for BASE fail\n", ld->name); goto err; } + dpram->size = res_size; + } + dpld->base = dpram->base; + dpld->size = dpram->size; + + mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", + ld->name, dpld->type, ld->aligned, (int)dpld->base, dpld->size); + + /* + ** Retrieve DPRAM SFR resource if exists + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + STR_DPRAM_SFR_BASE); + if (res) { res_size = resource_size(res); + dpld->sfr_base = ioremap_nocache(res->start, res_size); + if (!dpld->sfr_base) { + mif_err("%s: ERR! ioremap_nocache for SFR fail\n", + ld->name); + goto err; + } + } + + /* + ** Initialize DPRAM maps (physical map -> logical map) + */ + ret = dpram_init_boot_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_boot_map fail (err %d)\n", + ld->name, ret); + goto err; + } - dpctl->dp_base = ioremap_nocache(res->start, res_size); - dpctl->dp_size = res_size; + ret = dpram_init_dload_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_dload_map fail (err %d)\n", + ld->name, ret); + goto err; } - dpld->dp_base = dpctl->dp_base; - dpld->dp_size = dpctl->dp_size; - mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", - ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, - dpld->dp_size); + ret = dpram_init_uload_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_uload_map fail (err %d)\n", + ld->name, ret); + goto err; + } - /* Initialize DPRAM map (physical map -> logical map) */ - ret = dpram_table_init(dpld); + ret = dpram_init_ipc_map(dpld); if (ret < 0) { - mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + mif_err("%s: ERR! dpram_init_ipc_map fail (err %d)\n", ld->name, ret); goto err; } + if (dpram->res_ack_wait_timeout > 0) + dpld->res_ack_wait_timeout = dpram->res_ack_wait_timeout; + else + dpld->res_ack_wait_timeout = RES_ACK_WAIT_TIMEOUT; + /* Disable IPC */ - set_magic(dpld, 0); - set_access(dpld, 0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + if (!dpram->disabled) { + set_magic(dpld, 0); + set_access(dpld, 0); + } + dpld->init_status = DPRAM_INIT_STATE_NONE; - /* Initialize locks, completions, and bottom halves */ - snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); + /* + ** Initialize locks, completions, and bottom halves + */ + snprintf(dpld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); - init_completion(&dpld->dpram_init_cmd); - init_completion(&dpld->modem_pif_init_done); - init_completion(&dpld->udl_start_complete); - init_completion(&dpld->udl_cmd_complete); - init_completion(&dpld->dump_start_complete); - init_completion(&dpld->dump_recv_done); - - task_data = (unsigned long)dpld; - tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); - - /* Prepare SKB queue head for RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - skb_queue_head_init(&dpld->skb_rxq[i]); - - /* Prepare RXB queue */ - qsize = DPRAM_MAX_RXBQ_SIZE; - for (i = 0; i < dpld->max_ipc_dev; i++) { - bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); - dpld->rxbq[i].size = qsize; - dpld->rxbq[i].in = 0; - dpld->rxbq[i].out = 0; - dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); - if (!dpld->rxbq[i].rxb) { - mif_info("%s: ERR! %s rxbq_create_pool fail\n", - ld->name, get_dev_name(i)); - goto err; - } - mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n", - ld->name, get_dev_name(i), bsize, qsize); + init_completion(&dpld->udl_cmpl); + init_completion(&dpld->crash_cmpl); + + for (i = 0; i < ld->max_ipc_dev; i++) + init_completion(&dpld->req_ack_cmpl[i]); + + INIT_DELAYED_WORK(&dpld->rx_dwork, ipc_rx_work); + + for (i = 0; i < ld->max_ipc_dev; i++) { + spin_lock_init(&dpld->tx_lock[i]); + atomic_set(&dpld->res_required[i], 0); + } + + ld->tx_wq = create_singlethread_workqueue("dpram_tx_wq"); + if (!ld->tx_wq) { + mif_err("%s: ERR! fail to create tx_wq\n", ld->name); + goto err; } + INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); + INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); + INIT_DELAYED_WORK(&ld->rfs_tx_dwork, rfs_tx_work); + ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; + ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; + ld->tx_dwork[IPC_RFS] = &ld->rfs_tx_dwork; + +#ifdef DEBUG_MODEM_IF + spin_lock_init(&dpld->stat_list.lock); + spin_lock_init(&dpld->trace_list.lock); +#endif /* Prepare a multi-purpose miscellaneous buffer */ - dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); + dpld->buff = kzalloc(dpld->size, GFP_KERNEL); if (!dpld->buff) { - mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + mif_err("%s: ERR! kzalloc dpld->buff fail\n", ld->name); goto err; } - /* Retrieve DPRAM IRQ GPIO# */ - dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; - - /* Retrieve DPRAM IRQ# */ - if (!dpctl->dpram_irq) { - dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); - if (dpctl->dpram_irq < 0) { - mif_info("%s: ERR! platform_get_irq_byname fail\n", - ld->name); + /* + ** Retrieve DPRAM IRQ GPIO#, IRQ#, and IRQ flags + */ + dpld->gpio_int2ap = modem->gpio_ipc_int2ap; + dpld->gpio_cp_status = modem->gpio_cp_status; + dpld->gpio_cp_wakeup = modem->gpio_cp_wakeup; + if (dpram->type == AP_IDPRAM) { + if (!modem->gpio_ipc_int2cp) { + mif_err("%s: ERR! no gpio_ipc_int2cp\n", ld->name); goto err; } + dpld->gpio_int2cp = modem->gpio_ipc_int2cp; } - dpld->irq = dpctl->dpram_irq; - /* Retrieve DPRAM IRQ flags */ - if (!dpctl->dpram_irq_flags) - dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); - dpld->irq_flags = dpctl->dpram_irq_flags; + dpld->irq = modem->irq_ipc_int2ap; + + if (modem->irqf_ipc_int2ap) + dpld->irq_flags = modem->irqf_ipc_int2ap; + else + dpld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + + /* + ** Initialize power management (PM) for AP_IDPRAM + */ + if (dpld->type == AP_IDPRAM) { + dpld->pm_op = idpram_get_pm_op(dpld->ap); + if (!dpld->pm_op) { + mif_err("%s: no pm_op for AP_IDPRAM\n", ld->name); + goto err; + } + + ret = dpld->pm_op->pm_init(dpld, modem, pm_tx_work); + if (ret) { + mif_err("%s: pm_init fail (err %d)\n", ld->name, ret); + goto err; + } + } - /* Register DPRAM interrupt handler */ - snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); + /* + ** Register DPRAM interrupt handler + */ + snprintf(dpld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); if (dpld->ext_op && dpld->ext_op->irq_handler) dpld->irq_handler = dpld->ext_op->irq_handler; - else if (dpld->dp_type == CP_IDPRAM) + else if (dpld->type == CP_IDPRAM) dpld->irq_handler = cp_idpram_irq_handler; - else if (dpld->dp_type == AP_IDPRAM) + else if (dpld->type == AP_IDPRAM) dpld->irq_handler = ap_idpram_irq_handler; else dpld->irq_handler = ext_dpram_irq_handler; - ret = dpram_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, + ret = mif_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, dpld->irq_name, dpld); if (ret) goto err; - else - return ld; + + return ld; err: if (dpld) { - if (dpld->buff) - kfree(dpld->buff); + kfree(dpld->buff); kfree(dpld); } diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h index c651e51..d9ef251 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.h +++ b/drivers/misc/modem_if/modem_link_device_dpram.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,227 +11,11 @@ * GNU General Public License for more details. * */ + #ifndef __MODEM_LINK_DEVICE_DPRAM_H__ #define __MODEM_LINK_DEVICE_DPRAM_H__ -#include -#include -#include -#include -#include - -#include "modem_prj.h" - -#define DPRAM_MAGIC_CODE 0xAA - -/* interrupt masks.*/ -#define INT_MASK_VALID 0x0080 -#define INT_MASK_CMD 0x0040 -#define INT_VALID(x) ((x) & INT_MASK_VALID) -#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) -#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) -#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) - -#define EXT_UDL_MASK 0xF000 -#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) -#define EXT_INT_VALID_MASK 0x8000 -#define EXT_CMD_VALID_MASK 0x4000 -#define UDL_CMD_VALID_MASK 0x2000 -#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) -#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) -#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) -#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) - -#define EXT_CMD_MASK(x) ((x) & 0x0FFF) -#define EXT_CMD_SET_SPEED_LOW 0x0011 -#define EXT_CMD_SET_SPEED_MID 0x0012 -#define EXT_CMD_SET_SPEED_HIGH 0x0013 - -#define UDL_RESULT_SUCCESS 0x1 -#define UDL_RESULT_FAIL 0x2 - -#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) -#define UDL_CMD_RECV_READY 0x1 -#define UDL_CMD_DL_START_REQ 0x2 -#define UDL_CMD_DL_START_RESP 0x3 -#define UDL_CMD_IMAGE_SEND_REQ 0x4 -#define UDL_CMD_SEND_DONE_RESP 0x5 -#define UDL_CMD_SEND_DONE_REQ 0x6 -#define UDL_CMD_UPDATE_DONE 0x7 -#define UDL_CMD_STATUS_UPDATE 0x8 -#define UDL_CMD_IMAGE_SEND_RESP 0x9 -#define UDL_CMD_EFS_CLEAR_RESP 0xB -#define UDL_CMD_ALARM_BOOT_OK 0xC -#define UDL_CMD_ALARM_BOOT_FAIL 0xD - -#define CMD_IMG_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 -#define CMD_UL_RECV_RESP 0x9601 -#define CMD_UL_RECV_DONE_RESP 0x9801 - -/* special interrupt cmd indicating modem boot failure. */ -#define INT_POWERSAFE_FAIL 0xDEAD - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_REQ_ACK_F 0x0020 -#define INT_MASK_REQ_ACK_R 0x0010 -#define INT_MASK_RES_ACK_F 0x0008 -#define INT_MASK_RES_ACK_R 0x0004 -#define INT_MASK_SEND_F 0x0002 -#define INT_MASK_SEND_R 0x0001 - -#define INT_MASK_RES_ACK_SET \ - (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) - -#define INT_MASK_SEND_SET \ - (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) - -#define INT_CMD_MASK(x) ((x) & 0xF) -#define INT_CMD_INIT_START 0x1 -#define INT_CMD_INIT_END 0x2 -#define INT_CMD_REQ_ACTIVE 0x3 -#define INT_CMD_RES_ACTIVE 0x4 -#define INT_CMD_REQ_TIME_SYNC 0x5 -#define INT_CMD_CRASH_RESET 0x7 -#define INT_CMD_PHONE_START 0x8 -#define INT_CMD_ERR_DISPLAY 0x9 -#define INT_CMD_CRASH_EXIT 0x9 -#define INT_CMD_CP_DEEP_SLEEP 0xA -#define INT_CMD_NV_REBUILDING 0xB -#define INT_CMD_EMER_DOWN 0xC -#define INT_CMD_PIF_INIT_DONE 0xD -#define INT_CMD_SILENT_NV_REBUILDING 0xE -#define INT_CMD_NORMAL_PWR_OFF 0xF - -#define START_FLAG 0x7F -#define END_FLAG 0x7E - -#define DP_MAGIC_DMDL 0x4445444C -#define DP_MAGIC_UMDL 0x4445444D -#define DP_DPRAM_SIZE 0x4000 -#define DP_DEFAULT_WRITE_LEN 8168 -#define DP_DEFAULT_DUMP_LEN 16128 -#define DP_DUMP_HEADER_SIZE 7 - -#define UDL_TIMEOUT (50 * HZ) -#define UDL_SEND_TIMEOUT (200 * HZ) -#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) -#define DUMP_TIMEOUT (30 * HZ) -#define DUMP_START_TIMEOUT (100 * HZ) -#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ - -enum host_boot_mode { - HOST_BOOT_MODE_NORMAL, - HOST_BOOT_MODE_DUMP, -}; - -enum dpram_init_status { - DPRAM_INIT_STATE_NONE, - DPRAM_INIT_STATE_READY, -}; - -struct dpram_boot_img { - char *addr; - int size; - enum host_boot_mode mode; - unsigned req; - unsigned resp; -}; - -#define MAX_PAYLOAD_SIZE 0x2000 -struct dpram_boot_frame { - unsigned req; /* AP->CP request */ - unsigned resp; /* response expected by AP */ - ssize_t len; /* data size in the buffer */ - unsigned offset; /* offset to write into DPRAM */ - char data[MAX_PAYLOAD_SIZE]; -}; - -/* buffer type for modem image */ -struct dpram_dump_arg { - char *buff; /* pointer to the buffer */ - int buff_size; /* buffer size */ - unsigned req; /* AP->CP request */ - unsigned resp; /* CP->AP response */ - bool cmd; /* AP->CP command */ -}; - -struct dpram_boot_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 __iomem *req; - u32 __iomem *resp; - u32 size; -}; - -struct qc_dpram_boot_map { - u8 __iomem *buff; - u16 __iomem *frame_size; - u16 __iomem *tag; - u16 __iomem *count; -}; - -struct dpram_dload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct dpram_uload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct ul_header { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -} __packed; - -struct dpram_udl_param { - unsigned char *addr; - unsigned int size; - unsigned int count; - unsigned int tag; -}; - -struct dpram_udl_check { - unsigned int total_size; - unsigned int rest_size; - unsigned int send_size; - unsigned int copy_start; - unsigned int copy_complete; - unsigned int boot_complete; -}; - -#define DP_BOOT_BUFF_OFFSET 4 -#define DP_DLOAD_BUFF_OFFSET 4 -#define DP_ULOAD_BUFF_OFFSET 4 -#define DP_BOOT_REQ_OFFSET 0 -#define DP_BOOT_RESP_OFFSET 8 - -#define MAX_WQ_NAME_LENGTH 64 - -#define DPRAM_MAX_RXBQ_SIZE 256 - -struct dpram_rxb { - u8 *buff; - unsigned size; - - u8 *data; - unsigned len; -}; - -struct dpram_rxb_queue { - int size; - int in; - int out; - struct dpram_rxb *rxb; -}; +#include "modem_link_device_memory.h" /* magic_code + @@ -282,38 +65,110 @@ struct dpram_ipc_16k_map { u16 mbx_ap2cp; }; -#define DP_MAX_NAME_LEN 32 +enum dpram_init_status { + DPRAM_INIT_STATE_NONE, + DPRAM_INIT_STATE_READY, +}; + +struct dpram_boot_frame { + unsigned req; /* AP->CP request */ + unsigned resp; /* response expected by AP */ + ssize_t len; /* data size in the buffer */ + unsigned offset; /* offset to write into DPRAM */ + char data[DP_MAX_PAYLOAD_SIZE]; +}; + +struct dpram_dump_arg { + char *buff; /* pointer to the buffer */ + int buff_size; /* buffer size */ + unsigned req; /* AP->CP request */ + unsigned resp; /* CP->AP response */ + int cmd; /* AP->CP command */ +}; + +/* DPRAM upload/download header */ +struct dpram_udl_header { + u8 bop; + u16 num_frames; + u16 curr_frame; + u16 len; +#ifdef CONFIG_CDMA_MODEM_CBP82 + u8 pad; +#endif +} __packed; + +#define MAX_DUMP_SKB_SIZE 4096 + +enum idpram_link_pm_states { + IDPRAM_PM_SUSPEND_PREPARE, + IDPRAM_PM_DPRAM_POWER_DOWN, + IDPRAM_PM_SUSPEND_START, + IDPRAM_PM_RESUME_START, + IDPRAM_PM_ACTIVE, +}; + +struct idpram_pm_data { + atomic_t pm_lock; + + enum idpram_link_pm_states pm_state; + + struct completion down_cmpl; + + struct wake_lock ap_wlock; + struct wake_lock hold_wlock; + struct delayed_work tx_dwork; + struct delayed_work resume_dwork; + + struct notifier_block pm_noti; + + unsigned resume_try_cnt; + + /* the last value in the mbx_cp2ap */ + unsigned last_msg; +}; + +struct dpram_link_device; struct dpram_ext_op; +struct idpram_pm_op; struct dpram_link_device { struct link_device ld; + enum dpram_type type; /* DPRAM type */ + enum ap_type ap; /* AP type for AP_IDPRAM */ + + /* Stirct I/O access (e.g. ioread16(), etc.) is required */ + bool strict_io_access; + /* DPRAM address and size */ - u8 __iomem *dp_base; /* DPRAM base virtual address */ - u32 dp_size; /* DPRAM size */ - enum dpram_type dp_type; /* DPRAM type */ + u8 __iomem *base; /* Virtual address of DPRAM */ + u32 size; /* DPRAM size */ + + /* DPRAM SFR */ + u8 __iomem *sfr_base; /* Virtual address of SFR */ + + /* Whether or not this DPRAM can go asleep */ + bool need_wake_up; /* DPRAM IRQ GPIO# */ - unsigned gpio_dpram_int; + unsigned gpio_int2ap; + unsigned gpio_cp_status; + unsigned gpio_cp_wakeup; + unsigned gpio_int2cp; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[DP_MAX_NAME_LEN]; + char irq_name[MIF_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - int max_ipc_dev; - struct modemlink_dpram_control *dpctl; + struct modemlink_dpram_data *dpram; /* Physical configuration -> logical configuration */ - union { - struct dpram_boot_map bt_map; - struct qc_dpram_boot_map qc_bt_map; - }; - - struct dpram_dload_map dl_map; - struct dpram_uload_map ul_map; + struct memif_boot_map bt_map; + struct memif_dload_map dl_map; + struct memif_uload_map ul_map; /* IPC device map */ struct dpram_ipc_map ipc_map; @@ -327,40 +182,40 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[DP_MAX_NAME_LEN]; + char wlock_name[MIF_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; - struct completion dpram_init_cmd; - struct completion modem_pif_init_done; /* For UDL */ - struct tasklet_struct ul_tsk; struct tasklet_struct dl_tsk; - struct completion udl_start_complete; - struct completion udl_cmd_complete; - struct dpram_udl_check udl_check; - struct dpram_udl_param udl_param; + struct completion udl_cmpl; - /* For CP RAM dump */ + /* + ** For CP crash dump + */ + bool forced_cp_crash; struct timer_list crash_ack_timer; - struct completion dump_start_complete; - struct completion dump_recv_done; - struct timer_list dump_timer; - int dump_rcvd; /* Count of dump packets received */ + struct timer_list crash_timer; + struct completion crash_cmpl; + /* If this field is wanted to be used, it must be initialized only in + * the "ld->dump_start" method. + */ + struct delayed_work crash_dwork; + /* Count of CP crash dump packets received */ + int crash_rcvd; /* For locking TX process */ spinlock_t tx_lock[MAX_IPC_DEV]; + /* For retransmission under DPRAM flow control after TXQ full state */ + unsigned long res_ack_wait_timeout; + atomic_t res_required[MAX_IPC_DEV]; + struct completion req_ack_cmpl[MAX_IPC_DEV]; + /* For efficient RX process */ - struct tasklet_struct rx_tsk; - struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct delayed_work rx_dwork; struct io_device *iod[MAX_IPC_DEV]; - bool use_skb; - struct sk_buff_head skb_rxq[MAX_IPC_DEV]; - - /* For retransmission after buffer full state */ - atomic_t res_required[MAX_IPC_DEV]; /* For wake-up/sleep control */ atomic_t accessing; @@ -369,45 +224,53 @@ struct dpram_link_device { u8 *buff; /* DPRAM IPC initialization status */ - int dpram_init_status; + int init_status; /* Alias to device-specific IOCTL function */ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg); /* Alias to DPRAM IRQ handler */ - irqreturn_t (*irq_handler)(int irq, void *data); + irq_handler_t irq_handler; - /* For DPRAM dump */ - void (*dpram_dump)(struct link_device *ld, char *buff); + /* For DPRAM logging */ + struct mem_status_queue stat_list; + struct trace_data_queue trace_list; /* Common operations for each DPRAM */ - void (*clear_intr)(struct dpram_link_device *dpld); u16 (*recv_intr)(struct dpram_link_device *dpld); void (*send_intr)(struct dpram_link_device *dpld, u16 mask); u16 (*get_magic)(struct dpram_link_device *dpld); void (*set_magic)(struct dpram_link_device *dpld, u16 value); u16 (*get_access)(struct dpram_link_device *dpld); void (*set_access)(struct dpram_link_device *dpld, u16 value); - u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); - void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); - void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_head)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_tail)(struct dpram_link_device *dpld, int id); + void (*set_txq_head)(struct dpram_link_device *dpld, int id, u32 in); + void (*set_txq_tail)(struct dpram_link_device *dpld, int id, u32 out); + u8 *(*get_txq_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_tail)(struct dpram_link_device *dpld, int id); + void (*set_rxq_head)(struct dpram_link_device *dpld, int id, u32 in); + void (*set_rxq_tail)(struct dpram_link_device *dpld, int id, u32 out); + u8 *(*get_rxq_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_buff_size)(struct dpram_link_device *dpld, int id); u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); - void (*ipc_rx_handler)(struct dpram_link_device *dpld, u16 int2ap); + void (*get_dpram_status)(struct dpram_link_device *dpld, + enum circ_dir_type, struct mem_status *stat); + void (*ipc_rx_handler)(struct dpram_link_device *dpld, + struct mem_status *stat); + void (*reset_dpram_ipc)(struct dpram_link_device *dpld); /* Extended operations for various modems */ struct dpram_ext_op *ext_op; + + /* Power management (PM) for AP_IDPRAM */ + struct idpram_pm_data pm_data; + struct idpram_pm_op *pm_op; }; /* converts from struct link_device* to struct xxx_link_device* */ @@ -415,27 +278,65 @@ struct dpram_link_device { container_of(linkdev, struct dpram_link_device, ld) struct dpram_ext_op { + /* flag for checking whether or not a dpram_ext_op instance exists */ int exist; + /* methods for setting up DPRAM maps */ void (*init_boot_map)(struct dpram_link_device *dpld); void (*init_dl_map)(struct dpram_link_device *dpld); void (*init_ul_map)(struct dpram_link_device *dpld); + void (*init_ipc_map)(struct dpram_link_device *dpld); - int (*download_binary)(struct dpram_link_device *dpld, - struct sk_buff *skb); + /* methods for CP booting */ + int (*xmit_boot)(struct dpram_link_device *dpld, unsigned long arg); + int (*xmit_binary)(struct dpram_link_device *dpld, struct sk_buff *skb); + /* methods for DPRAM command handling */ + void (*cmd_handler)(struct dpram_link_device *dpld, u16 cmd); void (*cp_start_handler)(struct dpram_link_device *dpld); + /* method for CP firmware upgrade */ + int (*firm_update)(struct dpram_link_device *dpld, unsigned long arg); + + /* methods for CP crash dump */ void (*crash_log)(struct dpram_link_device *dpld); int (*dump_start)(struct dpram_link_device *dpld); - int (*dump_update)(struct dpram_link_device *dpld, void *arg); + int (*dump_update)(struct dpram_link_device *dpld, unsigned long arg); + int (*dump_finish)(struct dpram_link_device *dpld, unsigned long arg); + /* IOCTL extension */ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); - irqreturn_t (*irq_handler)(int irq, void *data); + /* methods for interrupt handling */ + irq_handler_t irq_handler; + void (*clear_int2ap)(struct dpram_link_device *dpld); + + /* methods for power management */ + int (*wakeup)(struct dpram_link_device *dpld); + void (*sleep)(struct dpram_link_device *dpld); }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +struct idpram_pm_op { + /* flag for checking whether or not a idpram_pm_op instance exists */ + int exist; + int (*pm_init)(struct dpram_link_device *dpld, struct modem_data *modem, + void (*pm_tx_func)(struct work_struct *work)); + void (*power_down)(struct dpram_link_device *dpld); + void (*power_up)(struct dpram_link_device *dpld); + void (*halt_suspend)(struct dpram_link_device *dpld); + bool (*locked)(struct dpram_link_device *dpld); + bool (*int2cp_possible)(struct dpram_link_device *dpld); +}; + +struct idpram_pm_op *idpram_get_pm_op(enum ap_type id); + +#if 1 +#endif + +extern void set_sromc_access(bool access); + #endif + diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c index 1475a46..b03dce5 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c @@ -25,103 +25,82 @@ #include #include #include -#include +#include +#include +#include +#include #include "modem_prj.h" -#include "modem_link_device_dpram.h" #include "modem_utils.h" +#include "modem_link_device_dpram.h" -#if defined(CONFIG_LTE_MODEM_CMC221) -/* -** For host (flashless) booting via DPRAM -*/ -#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 -#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 -#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A -#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD - -#define CMC22x_HOST_DOWN_START 0x1234 -#define CMC22x_HOST_DOWN_END 0x4321 -#define CMC22x_REG_NV_DOWN_END 0xABCD -#define CMC22x_CAL_NV_DOWN_END 0xDCBA - -#define CMC22x_1ST_BUFF_READY 0xAAAA -#define CMC22x_2ND_BUFF_READY 0xBBBB -#define CMC22x_1ST_BUFF_FULL 0x1111 -#define CMC22x_2ND_BUFF_FULL 0x2222 - -#define CMC22x_CP_RECV_NV_END 0x8888 -#define CMC22x_CP_CAL_OK 0x4F4B -#define CMC22x_CP_CAL_BAD 0x4552 -#define CMC22x_CP_DUMP_END 0xFADE - -#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */ -#endif - -#if defined(CONFIG_CDMA_MODEM_CBP72) -static void cbp72_init_boot_map(struct dpram_link_device *dpld) +#if defined(CONFIG_CDMA_MODEM_CBP72) || defined(CONFIG_CDMA_MODEM_CBP82) +static void cbp_init_boot_map(struct dpram_link_device *dpld) { - struct dpram_boot_map *bt_map = &dpld->bt_map; + struct memif_boot_map *bt_map = &dpld->bt_map; - bt_map->magic = (u32 *)dpld->dp_base; - bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET); - bt_map->size = dpld->dp_size - 4; + bt_map->magic = (u32 *)dpld->base; + bt_map->buff = (u8 *)(dpld->base + DP_BOOT_BUFF_OFFSET); + bt_map->space = dpld->size - 4; } -static void cbp72_init_dl_map(struct dpram_link_device *dpld) +static void cbp_init_dl_map(struct dpram_link_device *dpld) { - dpld->dl_map.magic = (u32 *)dpld->dp_base; - dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); } -static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int cbp_udl_wait_resp(struct dpram_link_device *dpld, u32 resp) { - struct link_device *ld = &dpld->ld; int ret; - int int2cp; + int int2ap; - ret = wait_for_completion_interruptible_timeout( - &dpld->udl_cmd_complete, UDL_TIMEOUT); + mif_debug("wait for 0x%04X\n", resp); + ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); if (!ret) { - mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name); - return -ENXIO; + mif_info("ERR! No UDL_CMD_RESP!!!\n"); + return -EIO; } - int2cp = dpld->recv_intr(dpld); - mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp); - if (resp == int2cp || int2cp == 0xA700) - return int2cp; - else + int2ap = dpld->recv_intr(dpld); + if (resp == int2ap || int2ap == CMD_UL_RECV_DONE_REQ) { + mif_debug("int2ap = 0x%04X\n", int2ap); + return int2ap; + } else { + mif_err("ERR! int2ap 0x%04X != resp 0x%04X\n", int2ap, resp); return -EINVAL; + } } -static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld, +static int cbp_xmit_binary(struct dpram_link_device *dpld, struct sk_buff *skb) { - struct link_device *ld = &dpld->ld; struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; u8 __iomem *buff = dpld->bt_map.buff; int err = 0; - if (bf->len > dpld->bt_map.size) { - mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); - err = -EINVAL; + if (bf->len > dpld->bt_map.space) { + mif_info("ERR! Out of DPRAM boundary\n"); + err = -ERANGE; goto exit; } if (bf->len) - memcpy(buff, bf->data, bf->len); + memcpy16_to_io(buff, bf->data, bf->len); - init_completion(&dpld->udl_cmd_complete); +#ifdef CONFIG_CDMA_MODEM_CBP72 + init_completion(&dpld->udl_cmpl); +#endif - if (bf->req) + if (bf->req) { dpld->send_intr(dpld, (u16)bf->req); + mif_debug("send intr 0x%04X\n", (u16)bf->req); + } if (bf->resp) { - err = _cbp72_edpram_wait_resp(dpld, bf->resp); + err = cbp_udl_wait_resp(dpld, bf->resp); if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); + mif_err("ERR! cbp_udl_wait_resp fail (err %d)\n", err); goto exit; } else if (err == bf->resp) { err = skb->len; @@ -133,24 +112,14 @@ exit: return err; } -static int cbp72_download_binary(struct dpram_link_device *dpld, - struct sk_buff *skb) +static int cbp_dump_start(struct dpram_link_device *dpld) { - if (dpld->dp_type == EXT_DPRAM) - return _cbp72_edpram_download_bin(dpld, skb); - else - return -ENODEV; -} - -static int cbp72_dump_start(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; u8 *dest = dpld->ul_map.buff; int ret; - ld->mode = LINK_MODE_ULOAD; + dpld->ld.mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->dump_timer); + ret = del_timer(&dpld->crash_timer); wake_lock(&dpld->wlock); iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic); @@ -161,53 +130,65 @@ static int cbp72_dump_start(struct dpram_link_device *dpld) iowrite8((u8)0x0, dest + 3); iowrite8((u8)END_FLAG, dest + 4); - init_completion(&dpld->dump_start_complete); + init_completion(&dpld->crash_cmpl); return 0; } -static int _cbp72_edpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dump, unsigned char __user *target) +static int cbp_dump_update(struct dpram_link_device *dpld, unsigned long arg) { - struct link_device *ld = &dpld->ld; - struct ul_header header; + struct dpram_dump_arg dump; + struct dpram_udl_header header; + unsigned char __user *target = (unsigned char __user *)arg; + int err = 0; + int resp = 0; u8 *dest = NULL; u8 *buff = NULL; - u16 plen = 0; - int err = 0; - int ret = 0; + u8 *header_buff = NULL; int buff_size = 0; + u16 plen = 0; - mif_debug("\n"); - - init_completion(&dpld->udl_cmd_complete); - - mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp); + err = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); + if (err < 0) { + mif_err("ERR! ARG copy_from_user fail (err %d)\n", err); + goto exit; + } + mif_debug("req %x, resp %x", dump.req, dump.resp); - if (dump->req) - dpld->send_intr(dpld, (u16)dump->req); + if (dump.req) + dpld->send_intr(dpld, (u16)dump.req); - if (dump->resp) { - err = _cbp72_edpram_wait_resp(dpld, dump->resp); + if (dump.resp) { + resp = err = cbp_udl_wait_resp(dpld, dump.resp); if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); + mif_info("ERR! wait_response fail (err %d)\n", err); goto exit; } } - if (dump->cmd) - return err; + if (dump.cmd) + goto exit; dest = (u8 *)dpld->ul_map.buff; - header.bop = *(u8 *)(dest); - header.total_frame = *(u16 *)(dest + 1); - header.curr_frame = *(u16 *)(dest + 3); - header.len = *(u16 *)(dest + 5); + header_buff = vmalloc(sizeof(struct dpram_udl_header)); + if (!header_buff) { + err = -ENOMEM; + goto exit; + } + + memcpy16_from_io(header_buff, dest, sizeof(struct dpram_udl_header)); + + header.bop = *(u8 *)(header_buff); + header.num_frames = *(u16 *)(header_buff + 1); + header.curr_frame = *(u16 *)(header_buff + 3); + header.len = *(u16 *)(header_buff + 5); +#ifdef CONFIG_CDMA_MODEM_CBP82 + header.pad = *(u8 *)(header_buff + 7); +#endif - mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n", - ld->name, header.total_frame, header.curr_frame, header.len); + mif_debug("total frames:%d, current frame:%d, data len:%d\n", + header.num_frames, header.curr_frame, header.len); plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN); @@ -217,22 +198,26 @@ static int _cbp72_edpram_upload(struct dpram_link_device *dpld, goto exit; } - memcpy(buff, dest + sizeof(struct ul_header), plen); - ret = copy_to_user(dump->buff, buff, plen); - if (ret < 0) { - mif_info("%s: ERR! dump copy_to_user fail\n", ld->name); + memcpy16_from_io(buff, dest + sizeof(struct dpram_udl_header), plen); + err = copy_to_user(dump.buff, buff, plen); + if (err) { + mif_info("ERR! DUMP copy_to_user fail\n"); err = -EIO; goto exit; } - buff_size = plen; - ret = copy_to_user(target + 4, &buff_size, sizeof(int)); - if (ret < 0) { - mif_info("%s: ERR! size copy_to_user fail\n", ld->name); + buff_size = plen; + err = copy_to_user(target + 4, &buff_size, sizeof(int)); + if (err) { + mif_info("ERR! SIZE copy_to_user fail\n"); err = -EIO; goto exit; } + /* Return response value */ + if (err == 0) + err = resp; + vfree(buff); return err; @@ -243,82 +228,169 @@ exit: wake_unlock(&dpld->wlock); return err; } +#endif -static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg) -{ - struct link_device *ld = &dpld->ld; - struct dpram_dump_arg dump; - int ret; +#if defined(CONFIG_LTE_MODEM_CMC221) +/* +** For CMC221 SFR for IDPRAM +*/ +#define CMC_INT2CP_REG 0x10 /* Interrupt to CP */ +#define CMC_INT2AP_REG 0x50 +#define CMC_CLR_INT_REG 0x28 /* Clear Interrupt to AP */ +#define CMC_RESET_REG 0x3C +#define CMC_PUT_REG 0x40 /* AP->CP reg for hostbooting */ +#define CMC_GET_REG 0x50 /* CP->AP reg for hostbooting */ - ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); - if (ret < 0) { - mif_info("%s: ERR! copy_from_user fail\n", ld->name); - return ret; - } +/* +** For host (flashless) booting via DPRAM +*/ +#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 +#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 +#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A +#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD - return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg); -} +#define CMC22x_HOST_DOWN_START 0x1234 +#define CMC22x_HOST_DOWN_END 0x4321 +#define CMC22x_REG_NV_DOWN_END 0xABCD +#define CMC22x_CAL_NV_DOWN_END 0xDCBA -static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); +#define CMC22x_1ST_BUFF_READY 0xAAAA +#define CMC22x_2ND_BUFF_READY 0xBBBB +#define CMC22x_1ST_BUFF_FULL 0x1111 +#define CMC22x_2ND_BUFF_FULL 0x2222 + +#define CMC22x_CP_RECV_NV_END 0x8888 +#define CMC22x_CP_CAL_OK 0x4F4B +#define CMC22x_CP_CAL_BAD 0x4552 +#define CMC22x_CP_DUMP_END 0xFADE - ld->mode = LINK_MODE_DLOAD; +#define CMC22x_DUMP_BUFF_SIZE 8192 - iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); +/* CMC221 IDPRAM SFR */ +struct cmc221_idpram_sfr { + u16 __iomem *int2cp; + u16 __iomem *int2ap; + u16 __iomem *clr_int2ap; + u16 __iomem *reset; + u16 __iomem *msg2cp; + u16 __iomem *msg2ap; +}; - return 0; -} +struct cmc221_boot_img { + char *addr; + int size; + enum cp_boot_mode mode; + unsigned req; + unsigned resp; +}; -static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) +static struct cmc221_idpram_sfr cmc_sfr; + +static void cmc221_init_boot_map(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int err = 0; + struct memif_boot_map *bt_map = &dpld->bt_map; - switch (cmd) { - case IOCTL_MODEM_DL_START: - err = cbp72_set_dl_magic(ld, iod); - if (err < 0) - mif_err("%s: ERR! set_dl_magic fail\n", ld->name); - break; + bt_map->buff = dpld->base; + bt_map->space = dpld->size; + bt_map->req = (u32 *)(dpld->base + DP_BOOT_REQ_OFFSET); + bt_map->resp = (u32 *)(dpld->base + DP_BOOT_RESP_OFFSET); +} - default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - break; - } +static void cmc221_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)dpld->base; +} - return err; +static void cmc221_init_ul_map(struct dpram_link_device *dpld) +{ + dpld->ul_map.magic = (u32 *)dpld->base; + dpld->ul_map.buff = (u8 *)dpld->base; } -#endif -#if defined(CONFIG_LTE_MODEM_CMC221) -static void cmc221_init_boot_map(struct dpram_link_device *dpld) +static void cmc221_init_ipc_map(struct dpram_link_device *dpld) { - struct dpram_boot_map *bt_map = &dpld->bt_map; + struct dpram_ipc_16k_map *dpram_map; + struct dpram_ipc_device *dev; + u8 __iomem *sfr_base = dpld->sfr_base; + + dpram_map = (struct dpram_ipc_16k_map *)dpld->base; + + /* Magic code and access enable fields */ + dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; + dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; + + /* FMT */ + dev = &dpld->ipc_map.dev[IPC_FMT]; + + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; + + dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head; + dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail; + dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0]; + dev->txq.size = DP_16K_FMT_TX_BUFF_SZ; + + dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head; + dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail; + dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0]; + dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_F; + dev->mask_res_ack = INT_MASK_RES_ACK_F; + dev->mask_send = INT_MASK_SEND_F; + + /* RAW */ + dev = &dpld->ipc_map.dev[IPC_RAW]; + + strcpy(dev->name, "RAW"); + dev->id = IPC_RAW; + + dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head; + dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail; + dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0]; + dev->txq.size = DP_16K_RAW_TX_BUFF_SZ; + + dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head; + dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail; + dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0]; + dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_R; + dev->mask_res_ack = INT_MASK_RES_ACK_R; + dev->mask_send = INT_MASK_SEND_R; + + /* SFR */ + cmc_sfr.int2cp = (u16 __iomem *)(sfr_base + CMC_INT2CP_REG); + cmc_sfr.int2ap = (u16 __iomem *)(sfr_base + CMC_INT2AP_REG); + cmc_sfr.clr_int2ap = (u16 __iomem *)(sfr_base + CMC_CLR_INT_REG); + cmc_sfr.reset = (u16 __iomem *)(sfr_base + CMC_RESET_REG); + cmc_sfr.msg2cp = (u16 __iomem *)(sfr_base + CMC_PUT_REG); + cmc_sfr.msg2ap = (u16 __iomem *)(sfr_base + CMC_GET_REG); + + /* Interrupt ports */ + dpld->ipc_map.mbx_cp2ap = cmc_sfr.int2ap; + dpld->ipc_map.mbx_ap2cp = cmc_sfr.int2cp; +} - bt_map->buff = dpld->dp_base; - bt_map->size = dpld->dp_size; - bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET); - bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET); +static inline void cmc221_idpram_reset(struct dpram_link_device *dpld) +{ + iowrite16(1, cmc_sfr.reset); } -static void cmc221_init_dl_map(struct dpram_link_device *dpld) +static inline u16 cmc221_idpram_recv_msg(struct dpram_link_device *dpld) { - dpld->dl_map.magic = (u32 *)dpld->dp_base; - dpld->dl_map.buff = (u8 *)dpld->dp_base; + return ioread16(cmc_sfr.msg2ap); } -static void cmc221_init_ul_map(struct dpram_link_device *dpld) +static inline void cmc221_idpram_send_msg(struct dpram_link_device *dpld, + u16 msg) { - dpld->ul_map.magic = (u32 *)dpld->dp_base; - dpld->ul_map.buff = (u8 *)dpld->dp_base; + iowrite16(msg, cmc_sfr.msg2cp); } -static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) { - struct link_device *ld = &dpld->ld; int count = 50000; u32 rcvd = 0; @@ -328,16 +400,14 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) if (rcvd == resp) break; - rcvd = dpld->dpctl->recv_msg(); + rcvd = cmc221_idpram_recv_msg(dpld); if (rcvd == 0x9999) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%04X\n", rcvd); panic("CP Crash ... BAD CRC in CP"); } if (count-- < 0) { - mif_info("%s: Invalid resp 0x%08X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%08X\n", rcvd); return -EAGAIN; } @@ -345,20 +415,19 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) } } else { while (1) { - rcvd = dpld->dpctl->recv_msg(); + rcvd = cmc221_idpram_recv_msg(dpld); if (rcvd == resp) break; if (resp == CMC22x_CP_RECV_NV_END && rcvd == CMC22x_CP_CAL_BAD) { - mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name); + mif_info("invalid resp CMC22x_CP_CAL_BAD\n"); break; } if (count-- < 0) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%04X\n", rcvd); return -EAGAIN; } @@ -369,110 +438,104 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) return rcvd; } -static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg) +static int cmc221_xmit_boot(struct dpram_link_device *dpld, unsigned long arg) { struct link_device *ld = &dpld->ld; u8 __iomem *bt_buff = dpld->bt_map.buff; - struct dpram_boot_img cp_img; + struct cmc221_boot_img cp_img; u8 *img_buff = NULL; int err = 0; int cnt = 0; + mif_info("+++\n"); ld->mode = LINK_MODE_BOOT; - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + dpld->dpram->setup_speed(DPRAM_SPEED_LOW); /* Test memory... After testing, memory is cleared. */ - if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) { - mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name); + if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.space) < 0) { + mif_info("ERR! mif_test_dpram fail!\n"); ld->mode = LINK_MODE_OFFLINE; return -EIO; } - memset(&cp_img, 0, sizeof(struct dpram_boot_img)); + memset(&cp_img, 0, sizeof(struct cmc221_boot_img)); /* Get information about the boot image */ - err = copy_from_user(&cp_img, arg, sizeof(cp_img)); - mif_info("%s: CP image addr = 0x%08X, size = %d\n", - ld->name, (int)cp_img.addr, cp_img.size); + err = copy_from_user(&cp_img, (void __user *)arg, sizeof(cp_img)); + mif_info("CP image addr = 0x%08X, size = %d\n", + (int)cp_img.addr, cp_img.size); /* Alloc a buffer for the boot image */ - img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL); + img_buff = kzalloc(dpld->bt_map.space, GFP_KERNEL); if (!img_buff) { - mif_info("%s: ERR! kzalloc fail\n", ld->name); + mif_info("ERR! kzalloc fail\n"); ld->mode = LINK_MODE_OFFLINE; return -ENOMEM; } /* Copy boot image from the user space to the image buffer */ - err = copy_from_user(img_buff, cp_img.addr, cp_img.size); + err = copy_from_user(img_buff, (void __user *)cp_img.addr, cp_img.size); /* Copy boot image to DPRAM and verify it */ memcpy(bt_buff, img_buff, cp_img.size); if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) { - mif_info("%s: ERR! Boot may be broken!!!\n", ld->name); + mif_info("ERR! Boot may be broken!!!\n"); goto err; } - dpld->dpctl->reset(); + cmc221_idpram_reset(dpld); usleep_range(1000, 2000); - if (cp_img.mode == HOST_BOOT_MODE_NORMAL) { - mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name); - mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req); + if (cp_img.mode == CP_BOOT_MODE_NORMAL) { + mif_info("CP_BOOT_MODE_NORMAL\n"); + mif_info("send req 0x%08X\n", cp_img.req); iowrite32(cp_img.req, dpld->bt_map.req); /* Wait for cp_img.resp for up to 2 seconds */ - mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp); + mif_info("wait resp 0x%08X\n", cp_img.resp); while (ioread32(dpld->bt_map.resp) != cp_img.resp) { cnt++; usleep_range(1000, 2000); if (cnt > 1000) { - mif_info("%s: ERR! Invalid resp 0x%08X\n", - ld->name, ioread32(dpld->bt_map.resp)); + mif_info("ERR! invalid resp 0x%08X\n", + ioread32(dpld->bt_map.resp)); goto err; } } } else { - mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name); + mif_info("CP_BOOT_MODE_DUMP\n"); } kfree(img_buff); - mif_info("%s: Send BOOT done\n", ld->name); + mif_info("send BOOT done\n"); - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + mif_info("---\n"); return 0; err: ld->mode = LINK_MODE_OFFLINE; kfree(img_buff); - mif_info("%s: ERR! Boot send fail!!!\n", ld->name); + mif_err("FAIL!!!\n"); + mif_info("---\n"); return -EIO; } -static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg) -{ - if (dpld->dp_type == CP_IDPRAM) - return _cmc221_idpram_send_boot(dpld, arg); - else - return -ENODEV; -} - -static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, +static int cmc221_idpram_download_bin(struct dpram_link_device *dpld, struct sk_buff *skb) { int err = 0; int ret = 0; - struct link_device *ld = &dpld->ld; struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; u8 __iomem *buff = (dpld->bt_map.buff + bf->offset); - if ((bf->offset + bf->len) > dpld->bt_map.size) { - mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); + if ((bf->offset + bf->len) > dpld->bt_map.space) { + mif_info("ERR! out of DPRAM boundary\n"); err = -EINVAL; goto exit; } @@ -481,17 +544,16 @@ static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, memcpy(buff, bf->data, bf->len); if (bf->req) - dpld->dpctl->send_msg((u16)bf->req); + cmc221_idpram_send_msg(dpld, (u16)bf->req); if (bf->resp) { - err = _cmc221_idpram_wait_resp(dpld, bf->resp); + err = cmc221_idpram_wait_resp(dpld, bf->resp); if (err < 0) - mif_info("%s: ERR! wait_response fail (err %d)\n", - ld->name, err); + mif_info("ERR! wait_resp fail (err %d)\n", err); } if (bf->req == CMC22x_CAL_NV_DOWN_END) - mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name); + mif_info("request CMC22x_CAL_NV_DOWN_END\n"); exit: if (err < 0) @@ -504,89 +566,84 @@ exit: return ret; } -static int cmc221_download_binary(struct dpram_link_device *dpld, +static int cmc221_xmit_binary(struct dpram_link_device *dpld, struct sk_buff *skb) { - if (dpld->dp_type == CP_IDPRAM) - return _cmc221_idpram_download_bin(dpld, skb); + if (dpld->type == CP_IDPRAM) + return cmc221_idpram_download_bin(dpld, skb); else return -ENODEV; } static int cmc221_dump_start(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int ret; - - ld->mode = LINK_MODE_ULOAD; + dpld->ld.mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->dump_timer); + del_timer(&dpld->crash_timer); wake_lock(&dpld->wlock); - dpld->dump_rcvd = 0; + dpld->crash_rcvd = 0; iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic); - init_completion(&dpld->dump_start_complete); + init_completion(&dpld->crash_cmpl); return 0; } -static void _cmc221_idpram_wait_dump(unsigned long arg) +static void cmc221_idpram_wait_dump(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; u16 msg; - msg = dpld->dpctl->recv_msg(); + msg = cmc221_idpram_recv_msg(dpld); if (msg == CMC22x_CP_DUMP_END) { - complete_all(&dpld->dump_recv_done); + complete_all(&dpld->crash_cmpl); return; } - if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (((dpld->crash_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { + complete_all(&dpld->crash_cmpl); return; } - if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (((dpld->crash_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { + complete_all(&dpld->crash_cmpl); return; } - mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, - _cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, + cmc221_idpram_wait_dump, (unsigned long)dpld); } -static int _cmc221_idpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dumparg) +static int cmc221_idpram_upload(struct dpram_link_device *dpld, + struct dpram_dump_arg *dumparg) { - struct link_device *ld = &dpld->ld; int ret; u8 __iomem *src; int buff_size = CMC22x_DUMP_BUFF_SIZE; - if ((dpld->dump_rcvd & 0x1) == 0) - dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY); + if ((dpld->crash_rcvd & 0x1) == 0) + cmc221_idpram_send_msg(dpld, CMC22x_1ST_BUFF_READY); else - dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY); + cmc221_idpram_send_msg(dpld, CMC22x_2ND_BUFF_READY); - init_completion(&dpld->dump_recv_done); + init_completion(&dpld->crash_cmpl); - mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, - _cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, + cmc221_idpram_wait_dump, (unsigned long)dpld); - ret = wait_for_completion_interruptible_timeout( - &dpld->dump_recv_done, DUMP_TIMEOUT); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, DUMP_TIMEOUT); if (!ret) { - mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name); + mif_info("ERR! no dump from CP!!!\n"); goto err_out; } - if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) { - mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name); + if (cmc221_idpram_recv_msg(dpld) == CMC22x_CP_DUMP_END) { + mif_info("recv CMC22x_CP_DUMP_END\n"); return 0; } - if ((dpld->dump_rcvd & 0x1) == 0) + if ((dpld->crash_rcvd & 0x1) == 0) src = dpld->ul_map.buff; else src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE; @@ -595,52 +652,64 @@ static int _cmc221_idpram_upload(struct dpram_link_device *dpld, ret = copy_to_user(dumparg->buff, dpld->buff, buff_size); if (ret < 0) { - mif_info("%s: ERR! copy_to_user fail\n", ld->name); + mif_info("ERR! copy_to_user fail\n"); goto err_out; } - dpld->dump_rcvd++; + dpld->crash_rcvd++; return buff_size; err_out: return -EIO; } -static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg) +static int cmc221_dump_update(struct dpram_link_device *dpld, unsigned long arg) { - struct link_device *ld = &dpld->ld; struct dpram_dump_arg dump; int ret; ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); if (ret < 0) { - mif_info("%s: ERR! copy_from_user fail\n", ld->name); + mif_info("ERR! copy_from_user fail\n"); return ret; } - return _cmc221_idpram_upload(dpld, &dump); + return cmc221_idpram_upload(dpld, &dump); } -static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) +static void cmc221_idpram_clr_int2ap(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int err = 0; + iowrite16(0xFFFF, cmc_sfr.clr_int2ap); +} - switch (cmd) { - case IOCTL_DPRAM_SEND_BOOT: - err = cmc221_download_boot(dpld, (void *)arg); - if (err < 0) - mif_info("%s: ERR! download_boot fail\n", ld->name); - break; +static int cmc221_idpram_wakeup(struct dpram_link_device *dpld) +{ + int cnt = 0; - default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - break; + gpio_set_value(dpld->gpio_cp_wakeup, 1); + + while (!gpio_get_value(dpld->gpio_cp_status)) { + if (cnt++ > 10) { + if (in_irq()) + mif_err("ERR! gpio_cp_status == 0 in IRQ\n"); + else + mif_err("ERR! gpio_cp_status == 0\n"); + return -EACCES; + } + + mif_info("gpio_cp_status == 0 (cnt %d)\n", cnt); + if (in_interrupt()) + udelay(1000); + else + usleep_range(1000, 2000); } - return err; + return 0; +} + +static void cmc221_idpram_sleep(struct dpram_link_device *dpld) +{ + gpio_set_value(dpld->gpio_cp_wakeup, 0); } #endif @@ -652,17 +721,44 @@ enum qc_dload_tag { QC_DLOAD_TAG_MAX }; +struct qc_dpram_boot_map { + u8 __iomem *buff; + u16 __iomem *frame_size; + u16 __iomem *tag; + u16 __iomem *count; +}; + +struct qc_dpram_udl_param { + unsigned char *addr; + unsigned int size; + unsigned int count; + unsigned int tag; +}; + +struct qc_dpram_udl_check { + unsigned int total_size; + unsigned int rest_size; + unsigned int send_size; + unsigned int copy_start; + unsigned int copy_complete; + unsigned int boot_complete; +}; + +static struct qc_dpram_boot_map qc_bt_map; +static struct qc_dpram_udl_param qc_udl_param; +static struct qc_dpram_udl_check qc_udl_check; + static void qc_dload_task(unsigned long data); static void qc_init_boot_map(struct dpram_link_device *dpld) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; + struct modemlink_dpram_data *dpram = dpld->dpram; - bt_map->buff = dpld->dp_base; - bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); - bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); - bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); + qbt_map->buff = dpld->base; + qbt_map->frame_size = (u16 *)(dpld->base + dpram->boot_size_offset); + qbt_map->tag = (u16 *)(dpld->base + dpram->boot_tag_offset); + qbt_map->count = (u16 *)(dpld->base + dpram->boot_count_offset); tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); } @@ -673,15 +769,15 @@ static int qc_prepare_download(struct dpram_link_device *dpld) int count = 0; while (1) { - if (dpld->udl_check.copy_start) { - dpld->udl_check.copy_start = 0; + if (qc_udl_check.copy_start) { + qc_udl_check.copy_start = 0; break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; - if (count > 200) { + if (count > 300) { mif_err("ERR! count %d\n", count); return -1; } @@ -690,32 +786,33 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void _qc_do_download(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static void qc_do_download(struct dpram_link_device *dpld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; - if (param->size <= dpld->dpctl->max_boot_frame_size) { - memcpy(bt_map->buff, param->addr, param->size); - iowrite16(param->size, bt_map->frame_size); - iowrite16(param->tag, bt_map->tag); - iowrite16(param->count, bt_map->count); + if (param->size <= dpld->dpram->max_boot_frame_size) { + memcpy(qbt_map->buff, param->addr, param->size); + iowrite16(param->size, qbt_map->frame_size); + iowrite16(param->tag, qbt_map->tag); + iowrite16(param->count, qbt_map->count); dpld->send_intr(dpld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int _qc_download(struct dpram_link_device *dpld, void *arg, +static int qc_download(struct dpram_link_device *dpld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + retval = copy_from_user((void *)¶m, (void __user *)arg, + sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail\n"); return -1; @@ -729,24 +826,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, memset(img, 0, param.size); memcpy(img, param.addr, param.size); - dpld->udl_check.total_size = param.size; - dpld->udl_check.rest_size = param.size; - dpld->udl_check.send_size = 0; - dpld->udl_check.copy_complete = 0; + qc_udl_check.total_size = param.size; + qc_udl_check.rest_size = param.size; + qc_udl_check.send_size = 0; + qc_udl_check.copy_complete = 0; - dpld->udl_param.addr = img; - dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + qc_udl_param.addr = img; + qc_udl_param.size = dpld->dpram->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - dpld->udl_param.count = 1; + qc_udl_param.count = 1; else - dpld->udl_param.count = param.count; - dpld->udl_param.tag = tag; + qc_udl_param.count = param.count; + qc_udl_param.tag = tag; - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) + qc_udl_param.size = qc_udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(dpld, &dpld->udl_param); + qc_do_download(dpld, &qc_udl_param); /* Wait for completion */ @@ -756,18 +853,18 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (dpld->udl_check.copy_complete) { - dpld->udl_check.copy_complete = 0; + if (qc_udl_check.copy_complete) { + qc_udl_check.copy_complete = 0; retval = 0; break; } - msleep(10); + usleep_range(10000, 11000); count++; if (count > cnt_limit) { - dpld->udl_check.total_size = 0; - dpld->udl_check.rest_size = 0; + qc_udl_check.total_size = 0; + qc_udl_check.rest_size = 0; mif_err("ERR! count %d\n", count); retval = -1; break; @@ -781,51 +878,51 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, static int qc_download_binary(struct dpram_link_device *dpld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + return qc_download(dpld, arg, QC_DLOAD_TAG_BIN); } static int qc_download_nv(struct dpram_link_device *dpld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); + return qc_download(dpld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { struct dpram_link_device *dpld = (struct dpram_link_device *)data; - dpld->udl_check.send_size += dpld->udl_param.size; - dpld->udl_check.rest_size -= dpld->udl_param.size; + qc_udl_check.send_size += qc_udl_param.size; + qc_udl_check.rest_size -= qc_udl_param.size; - dpld->udl_param.addr += dpld->udl_param.size; + qc_udl_param.addr += qc_udl_param.size; - if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { - dpld->udl_check.copy_complete = 1; - dpld->udl_param.tag = 0; + if (qc_udl_check.send_size >= qc_udl_check.total_size) { + qc_udl_check.copy_complete = 1; + qc_udl_param.tag = 0; return; } - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) + qc_udl_param.size = qc_udl_check.rest_size; - dpld->udl_param.count += 1; + qc_udl_param.count += 1; - _qc_do_download(dpld, &dpld->udl_param); + qc_do_download(dpld, &qc_udl_param); } static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) { switch (cmd) { case 0x1234: - dpld->udl_check.copy_start = 1; + qc_udl_check.copy_start = 1; break; case 0xDBAB: - if (dpld->udl_check.total_size) + if (qc_udl_check.total_size) tasklet_schedule(&dpld->dl_tsk); break; case 0xABCD: - dpld->udl_check.boot_complete = 1; + qc_udl_check.boot_complete = 1; break; default: @@ -843,12 +940,12 @@ static int qc_boot_start(struct dpram_link_device *dpld) dpld->send_intr(dpld, mask); while (1) { - if (dpld->udl_check.boot_complete) { - dpld->udl_check.boot_complete = 0; + if (qc_udl_check.boot_complete) { + qc_udl_check.boot_complete = 0; break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -870,7 +967,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -900,27 +997,26 @@ static void qc_start_handler(struct dpram_link_device *dpld) static void qc_crash_log(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = dpld->get_rx_buff(dpld, IPC_FMT); + data = dpld->get_rxq_buff(dpld, IPC_FMT); memcpy(buf, data, (sizeof(buf) - 1)); - mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); + mif_info("PHONE ERR MSG\t| %s Crash\n", dpld->ld.mc->name); mif_info("PHONE ERR MSG\t| %s\n", buf); } -static int _qc_data_upload(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static int qc_data_upload(struct dpram_link_device *dpld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { + if (!gpio_get_value(dpld->gpio_int2ap)) { intval = dpld->recv_intr(dpld); if (intval == 0xDBAB) { break; @@ -930,7 +1026,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - msleep_interruptible(1); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -939,10 +1035,10 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - param->size = ioread16(bt_map->frame_size); - memcpy(param->addr, bt_map->buff, param->size); - param->tag = ioread16(bt_map->tag); - param->count = ioread16(bt_map->count); + param->size = ioread16(qbt_map->frame_size); + memcpy(param->addr, qbt_map->buff, param->size); + param->tag = ioread16(qbt_map->tag); + param->count = ioread16(qbt_map->count); dpld->send_intr(dpld, 0xDB12); @@ -961,7 +1057,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { + if (!gpio_get_value(dpld->gpio_int2ap)) { intval = dpld->recv_intr(dpld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { @@ -972,7 +1068,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - msleep_interruptible(1); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -993,17 +1089,18 @@ static int qc_uload_step1(struct dpram_link_device *dpld) static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) { int retval = 0; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + retval = copy_from_user((void *)¶m, (void __user *)arg, + sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail (err %d)\n", retval); return -1; } - retval = _qc_data_upload(dpld, ¶m); + retval = qc_data_upload(dpld, ¶m); if (retval < 0) { - mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); + mif_err("ERR! qc_data_upload fail (err %d)\n", retval); return -1; } @@ -1025,40 +1122,39 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) } static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { - struct link_device *ld = &dpld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: err = qc_prepare_download(dpld); if (err < 0) - mif_info("%s: ERR! prepare_download fail\n", ld->name); + mif_info("ERR! prepare_download fail\n"); break; case IOCTL_DPRAM_PHONEIMG_LOAD: err = qc_download_binary(dpld, (void *)arg); if (err < 0) - mif_info("%s: ERR! download_binary fail\n", ld->name); + mif_info("ERR! download_binary fail\n"); break; case IOCTL_DPRAM_NVDATA_LOAD: err = qc_download_nv(dpld, (void *)arg); if (err < 0) - mif_info("%s: ERR! download_nv fail\n", ld->name); + mif_info("ERR! download_nv fail\n"); break; case IOCTL_DPRAM_PHONE_BOOTSTART: err = qc_boot_start(dpld); if (err < 0) { - mif_info("%s: ERR! boot_start fail\n", ld->name); + mif_info("ERR! boot_start fail\n"); break; } err = qc_boot_post_process(dpld); if (err < 0) - mif_info("%s: ERR! boot_post_process fail\n", ld->name); + mif_info("ERR! boot_post_process fail\n"); break; @@ -1067,7 +1163,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step1(dpld); if (err < 0) { enable_irq(dpld->irq); - mif_info("%s: ERR! upload_step1 fail\n", ld->name); + mif_info("ERR! upload_step1 fail\n"); } break; @@ -1075,12 +1171,12 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step2(dpld, (void *)arg); if (err < 0) { enable_irq(dpld->irq); - mif_info("%s: ERR! upload_step2 fail\n", ld->name); + mif_info("ERR! upload_step2 fail\n"); } break; default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + mif_err("ERR! invalid cmd 0x%08X\n", cmd); err = -EINVAL; break; } @@ -1091,44 +1187,402 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, static irqreturn_t qc_dpram_irq_handler(int irq, void *data) { struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; + struct link_device *ld = &dpld->ld; + struct mem_status stat; u16 int2ap = 0; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + if (ld->mode == LINK_MODE_OFFLINE) { + int2ap = dpld->recv_intr(dpld); return IRQ_HANDLED; + } - int2ap = dpld->recv_intr(dpld); + dpld->get_dpram_status(dpld, RX, &stat); + int2ap = stat.int2ap; if (int2ap == INT_POWERSAFE_FAIL) { - mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); + mif_info("int2ap == INT_POWERSAFE_FAIL\n"); goto exit; } if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { qc_dload_cmd_handler(dpld, int2ap); goto exit; + } else if (int2ap == 0x4321 || int2ap == 0x5432) { + mif_err("ERR! CP error command (0x%04X)\n", int2ap); + goto exit; } if (likely(INT_VALID(int2ap))) - dpld->ipc_rx_handler(dpld, int2ap); + dpld->ipc_rx_handler(dpld, &stat); else - mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap); + mif_info("ERR! invalid intr 0x%04X\n", int2ap); exit: return IRQ_HANDLED; } #endif -static struct dpram_ext_op ext_op_set[] = { +#if defined(CONFIG_CDMA_MODEM_QSC6085) +#define CMD_CP_RAMDUMP_START_REQ 0x9200 +#define CMD_CP_RAMDUMP_SEND_REQ 0x9400 +#define CMD_CP_RAMDUMP_SEND_DONE_REQ 0x9600 + +#define CMD_CP_RAMDUMP_START_RESP 0x0300 +#define CMD_CP_RAMDUMP_SEND_RESP 0x0500 +#define CMD_CP_RAMDUMP_SEND_DONE_RESP 0x0700 + +#define QSC_UPLOAD_MODE (0x444D554C) +#define QSC_UPLOAD_MODE_COMPLETE (0xABCDEF90) + +#define RAMDUMP_CMD_TIMEOUT (5 * HZ) +#define QSC6085_RAM_SIZE (32 * 1024 * 1024) /* 32MB */ + +struct qsc6085_dump_command { + u32 addr; + u32 size; + u32 copyto_offset; +}; + +struct qsc6085_dump_status { + u32 dump_size; + u32 addr; + u32 rcvd; + u32 rest; +}; + +static struct qsc6085_dump_status qsc_dump_stat; + +static void qsc6085_dump_work(struct work_struct *work); + +static void qsc6085_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); +} + +static void qsc6085_init_ul_map(struct dpram_link_device *dpld) +{ + int magic_size = DP_ULOAD_MAGIC_SIZE; + int cmd_size = sizeof(struct qsc6085_dump_command); + int mbx_size = DP_MBX_SET_SIZE; + + dpld->ul_map.magic = (u32 *)dpld->base; + dpld->ul_map.cmd = dpld->base + magic_size; + dpld->ul_map.cmd_size = cmd_size; + dpld->ul_map.buff = dpld->base + magic_size + cmd_size; + dpld->ul_map.space = dpld->size - (magic_size + cmd_size + mbx_size); +} + +static void qsc6085_req_active_handler(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + mif_info("pda_active = %d\n", gpio_get_value(mc->gpio_pda_active)); + dpld->send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); +} + +static void qsc6085_error_display_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_err("recv 0xC9 (CRASH_EXIT)\n"); + mif_err("CP Crash: %s\n", dpld->get_rxq_buff(dpld, IPC_FMT)); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); +} + +static void qsc6085_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_info("recv 0xC8 (CP_START)\n"); + + mif_info("send 0xC1 (INIT_START)\n"); + dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_START)); + + dpld->reset_dpram_ipc(dpld); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("ERR! no iod\n"); + return; + } + iod->modem_state_changed(iod, STATE_ONLINE); + + mif_info("send 0xC2 (INIT_END)\n"); + dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); +} + +static void qsc6085_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + qsc6085_req_active_handler(dpld); + break; + + case INT_CMD_ERR_DISPLAY: +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + /* If modem crashes while PDA_SLEEP is in progres */ + dpld->pm_op->halt_suspend(dpld); +#endif + qsc6085_error_display_handler(dpld); + break; + + case INT_CMD_PHONE_START: + qsc6085_start_handler(dpld); + complete_all(&ld->init_cmpl); + break; + +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + case INT_CMD_IDPRAM_SUSPEND_ACK: + dpld->pm_op->power_down(dpld); + break; + + case INT_CMD_IDPRAM_WAKEUP_START: + dpld->pm_op->power_up(dpld); + break; +#endif + + case INT_CMD_NORMAL_POWER_OFF: + complete(&dpld->crash_cmpl); + qsc6085_error_display_handler(dpld); + break; + + default: + mif_err("unknown command 0x%04X\n", cmd); + break; + } +} + +static int qsc6085_download_firmware(struct dpram_link_device *dpld, + struct modem_firmware *fw) +{ + int ret = 0; + char __user *src = fw->binary; + int rest = fw->size; + char __iomem *dst = NULL; + unsigned long timeout; + u16 curr_frame = 0; + u16 len = 0; + struct dpram_udl_header header; + + header.bop = START_FLAG; + header.num_frames = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN); + mif_err("FW %d bytes = %d frames\n", fw->size, header.num_frames); + + while (rest > 0) { + curr_frame++; + len = min(rest, DP_DEFAULT_WRITE_LEN); + + header.curr_frame = curr_frame; + header.len = len; + mif_info(">>> frame# %u, len %u\n", curr_frame, len); + + dst = dpld->dl_map.buff; + memcpy(dst, &header, sizeof(header)); + + dst += sizeof(header); + ret = copy_from_user(dst, (void __user *)src, len); + if (ret < 0) { + mif_err("copy_from_user fail\n"); + return -EIO; + } + + dst += len; + src += len; + rest -= len; + + iowrite8(END_FLAG, (dst+3)); + + if (curr_frame == 1) { + dpld->send_intr(dpld, 0); + timeout = UDL_TIMEOUT; + } else { + dpld->send_intr(dpld, CMD_DL_SEND_REQ); + timeout = UDL_SEND_TIMEOUT; + } + + ret = wait_for_completion_timeout(&dpld->udl_cmpl, timeout); + if (!ret) { + mif_err("ERR! no response from CP\n"); + return -EIO; + } + } + + mif_err("send CMD_DL_DONE_REQ to CP\n"); + dpld->send_intr(dpld, CMD_DL_DONE_REQ); + + ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); + if (!ret) { + mif_err("ERR! no response from CP\n"); + return -EIO; + } + + return 0; +} + +static int qsc6085_dload_firmware(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct modem_firmware fw; + mif_err("+++\n"); + + ret = copy_from_user(&fw, (void __user *)arg, sizeof(fw)); + if (ret < 0) { + mif_err("ERR! copy_from_user fail!\n"); + return ret; + } + + ret = qsc6085_download_firmware(dpld, &fw); + + mif_err("---\n"); + return ret; +} + +static int qsc6085_dump_start(struct dpram_link_device *dpld) +{ + int ret; + struct link_device *ld = &dpld->ld; + struct modem_ctl *mc = ld->mc; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + mif_err("+++\n"); + + init_completion(&dpld->crash_cmpl); + INIT_DELAYED_WORK(&dpld->crash_dwork, qsc6085_dump_work); + + iowrite32(QSC_UPLOAD_MODE, &dpld->ul_map.magic); + + /* reset modem so that it goes to upload mode */ + /* ap does not need to reset cp during CRASH_EXIT case */ + if (gpio_get_value(mc->gpio_phone_active)) + mc->ops.modem_reset(mc); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_START_REQ); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, + RAMDUMP_CMD_TIMEOUT); + if (!ret) { + mif_err("ERR! no response to CP_RAMDUMP_START_REQ\n"); + dump_stat->dump_size = 0; + } else { + dump_stat->dump_size = QSC6085_RAM_SIZE; + dump_stat->addr = 0; + dump_stat->rcvd = 0; + dump_stat->rest = dump_stat->dump_size; + } + + queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_dump_update(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct link_device *ld = &dpld->ld; + struct io_device *iod = link_get_iod_with_format(ld, IPC_RAMDUMP); + struct memif_uload_map *ul_map = &dpld->ul_map; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + struct qsc6085_dump_command dump_cmd; + + while (iod->sk_rx_q.qlen > 0) + usleep_range(1000, 1100); + + memset(&dump_cmd, 0, sizeof(dump_cmd)); + dump_cmd.addr = dump_stat->addr; + dump_cmd.size = min(dump_stat->rest, ul_map->space); + dump_cmd.copyto_offset = 0x38000010; + + memcpy_toio(ul_map->cmd, &dump_cmd, ul_map->cmd_size); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_REQ); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, + RAMDUMP_CMD_TIMEOUT); + if (!ret) { + dump_stat->dump_size = 0; + mif_err("ERR! no response to CP_RAMDUMP_SEND_REQ\n"); + ret = -EIO; + goto exit; + } + + memcpy_fromio(dpld->buff, ul_map->buff, dump_cmd.size); + + ret = iod->recv(iod, ld, dpld->buff, dump_cmd.size); + if (ret < 0) + goto exit; + + dump_stat->addr += dump_cmd.size; + dump_stat->rcvd += dump_cmd.size; + dump_stat->rest -= dump_cmd.size; + mif_info("rest = %u bytes\n", dump_stat->rest); + + ret = dump_cmd.size; + +exit: + return ret; +} + +static void qsc6085_dump_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + int ret; + + dpld = container_of(work, struct dpram_link_device, crash_dwork.work); + ld = &dpld->ld; + + ret = qsc6085_dump_update(dpld, 0); + if (ret > 0 && dump_stat->rest > 0) + queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); +} + +static int qsc6085_dump_finish(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct completion *cmpl = &dpld->crash_cmpl; + mif_err("+++\n"); + + init_completion(cmpl); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_DONE_REQ); + + ret = wait_for_completion_timeout(cmpl, RAMDUMP_CMD_TIMEOUT); + if (!ret) { + mif_err("ERR! no response to CP_RAMDUMP_SEND_DONE_REQ\n"); + ret = -EIO; + } + + mif_err("---\n"); + return ret; +} +#endif + +static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = { #ifdef CONFIG_CDMA_MODEM_CBP72 [VIA_CBP72] = { .exist = 1, - .init_boot_map = cbp72_init_boot_map, - .init_dl_map = cbp72_init_dl_map, - .download_binary = cbp72_download_binary, - .dump_start = cbp72_dump_start, - .dump_update = cbp72_dump_update, - .ioctl = cbp72_ioctl, + .init_boot_map = cbp_init_boot_map, + .init_dl_map = cbp_init_dl_map, + .xmit_binary = cbp_xmit_binary, + .dump_start = cbp_dump_start, + .dump_update = cbp_dump_update, + }, +#endif +#ifdef CONFIG_CDMA_MODEM_CBP82 + [VIA_CBP82] = { + .exist = 1, + .init_boot_map = cbp_init_boot_map, + .init_dl_map = cbp_init_dl_map, + .xmit_binary = cbp_xmit_binary, + .dump_start = cbp_dump_start, + .dump_update = cbp_dump_update, }, #endif #ifdef CONFIG_LTE_MODEM_CMC221 @@ -1137,10 +1591,14 @@ static struct dpram_ext_op ext_op_set[] = { .init_boot_map = cmc221_init_boot_map, .init_dl_map = cmc221_init_dl_map, .init_ul_map = cmc221_init_ul_map, - .download_binary = cmc221_download_binary, + .init_ipc_map = cmc221_init_ipc_map, + .xmit_boot = cmc221_xmit_boot, + .xmit_binary = cmc221_xmit_binary, .dump_start = cmc221_dump_start, .dump_update = cmc221_dump_update, - .ioctl = cmc221_ioctl, + .clear_int2ap = cmc221_idpram_clr_int2ap, + .wakeup = cmc221_idpram_wakeup, + .sleep = cmc221_idpram_sleep, }, #endif #if defined(CONFIG_CDMA_MODEM_MDM6600) @@ -1163,6 +1621,18 @@ static struct dpram_ext_op ext_op_set[] = { .irq_handler = qc_dpram_irq_handler, }, #endif +#if defined(CONFIG_CDMA_MODEM_QSC6085) + [QC_QSC6085] = { + .exist = 1, + .init_dl_map = qsc6085_init_dl_map, + .init_ul_map = qsc6085_init_ul_map, + .cmd_handler = qsc6085_command_handler, + .firm_update = qsc6085_dload_firmware, + .dump_start = qsc6085_dump_start, + .dump_update = qsc6085_dump_update, + .dump_finish = qsc6085_dump_finish, + }, +#endif }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) @@ -1173,3 +1643,422 @@ struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) return NULL; } +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM +#define GPIO_IDPRAM_SFN S3C_GPIO_SFN(2) + +#define MAX_CHECK_RETRY_CNT 5 +#define MAX_RESUME_TRY_CNT 5 + +static bool s5p_idpram_is_pm_locked(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + struct idpram_pm_data *pm_data = &dpld->pm_data; + + /* If PM is in SUSPEND */ + if (atomic_read(&pm_data->pm_lock) > 0) { + mif_info("in SUSPEND\n"); + return true; + } + + /* If AP is in or into LPA */ + if (!gpio_get_value(mc->gpio_pda_active)) { + mif_info("in LPA\n"); + return true; + } + + return false; +} + +static void s5p_idpram_set_pm_lock(struct dpram_link_device *dpld, int lock) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + + /* 0 = unlock, 1 = lock */ + switch (lock) { + case 0: + if (atomic_read(&pm_data->pm_lock)) + atomic_set(&pm_data->pm_lock, lock); + break; + + case 1: + if (!atomic_read(&pm_data->pm_lock)) + atomic_set(&pm_data->pm_lock, lock); + break; + + default: + break; + } +} + +static void s5p_idpram_try_resume(struct work_struct *work) +{ + struct idpram_pm_data *pm_data; + struct dpram_link_device *dpld; + struct link_device *ld; + unsigned long delay; + u16 cmd; + mif_info("+++\n"); + + pm_data = container_of(work, struct idpram_pm_data, resume_dwork.work); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + ld = &dpld->ld; + + if (pm_data->last_msg == INT_CMD(INT_CMD_IDPRAM_RESUME_REQ)) { + pm_data->last_msg = 0; + + s5p_idpram_set_pm_lock(dpld, 0); + wake_unlock(&pm_data->hold_wlock); + + delay = msecs_to_jiffies(10); + schedule_delayed_work(&pm_data->tx_dwork, delay); + + mif_info("%s resumed\n", ld->name); + goto exit; + } + + if (pm_data->resume_try_cnt++ < MAX_RESUME_TRY_CNT) { + mif_info("%s not resumed yet\n", ld->name); + + cmd = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); + mif_info("send IDPRAM_RESUME_REQ (0x%X)\n", cmd); + dpld->send_intr(dpld, cmd); + + delay = msecs_to_jiffies(200); + schedule_delayed_work(&pm_data->resume_dwork, delay); + } else { + struct io_device *iod; + mif_err("ERR! %s resume T-I-M-E-O-U-T\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + wake_unlock(&pm_data->hold_wlock); + + /* hold wakelock until uevnet sent to rild */ + wake_lock_timeout(&pm_data->hold_wlock, HZ*7); + s5p_idpram_set_pm_lock(dpld, 0); + } + +exit: + mif_info("---\n"); +} + +static irqreturn_t s5p_cp_dump_irq_handler(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static irqreturn_t s5p_ap_wakeup_irq_handler(int irq, void *data) +{ + struct idpram_pm_data *pm_data = data; + wake_lock_timeout(&pm_data->ap_wlock, HZ*5); + return IRQ_HANDLED; +} + +static void s5p_idpram_power_down(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK); + complete(&pm_data->down_cmpl); + + mif_info("---\n"); +} + +static void s5p_idpram_power_up(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); + pm_data->pm_state = IDPRAM_PM_ACTIVE; + + mif_info("---\n"); +} + +static void s5p_idpram_halt_suspend(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + complete(&pm_data->down_cmpl); + + mif_info("---\n"); +} + +static int s5p_idpram_prepare_suspend(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct modem_ctl *mc = dpld->ld.mc; + struct completion *cmpl; + unsigned long timeout; + unsigned long rest; + int cnt = 0; + u16 cmd = INT_CMD(INT_CMD_IDPRAM_SUSPEND_REQ); + mif_info("+++\n"); + + pm_data->pm_state = IDPRAM_PM_SUSPEND_PREPARE; + pm_data->last_msg = 0; + s5p_idpram_set_pm_lock(dpld, 1); + + /* + * Because, if dpram was powered down, cp dpram random intr was + * ocurred. so, fixed by muxing cp dpram intr pin to GPIO output + * high,.. + */ + gpio_set_value(dpld->gpio_int2cp, 1); + s3c_gpio_cfgpin(dpld->gpio_int2cp, S3C_GPIO_OUTPUT); + + /* prevent PDA_ACTIVE status is low */ + gpio_set_value(mc->gpio_pda_active, 1); + + cmpl = &pm_data->down_cmpl; + timeout = IDPRAM_SUSPEND_REQ_TIMEOUT; + cnt = 0; + do { + init_completion(cmpl); + + mif_info("send IDPRAM_SUSPEND_REQ (0x%X)\n", cmd); + dpld->send_intr(dpld, cmd); + + rest = wait_for_completion_timeout(cmpl, timeout); + if (rest == 0) { + cnt++; + mif_err("timeout!!! (count = %d)\n", cnt); + if (cnt >= 3) { + mif_err("ERR! no response from CP\n"); + break; + } + } + } while (rest == 0); + + switch (pm_data->last_msg) { + case INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK): + mif_info("recv IDPRAM_SUSPEND_ACK (0x%X)\n", pm_data->last_msg); + pm_data->pm_state = IDPRAM_PM_DPRAM_POWER_DOWN; + break; + + default: + mif_err("ERR! %s down or not ready!!! (intr 0x%04X)\n", + ld->name, dpld->recv_intr(dpld)); + timeout = msecs_to_jiffies(500); + wake_lock_timeout(&pm_data->hold_wlock, timeout); + s5p_idpram_set_pm_lock(dpld, 0); + break; + } + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_resume_init(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->pm_state = IDPRAM_PM_RESUME_START; + pm_data->last_msg = 0; + + dpld->reset_dpram_ipc(dpld); + + /* re-initialize internal dpram gpios */ + s3c_gpio_cfgpin(dpld->gpio_int2cp, GPIO_IDPRAM_SFN); + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_start_resume(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct modem_ctl *mc = dpld->ld.mc; + unsigned long delay; + mif_info("+++ (pm_state = %d)\n", pm_data->pm_state); + + switch (pm_data->pm_state) { + /* schedule_work */ + case IDPRAM_PM_DPRAM_POWER_DOWN: + gpio_set_value(mc->gpio_pda_active, 0); + msleep(50); + + s5p_idpram_resume_init(dpld); + msleep(50); + + gpio_set_value(mc->gpio_pda_active, 1); + msleep(20); + + pm_data->resume_try_cnt = 0; + wake_lock(&pm_data->hold_wlock); + + delay = msecs_to_jiffies(20); + schedule_delayed_work(&pm_data->resume_dwork, delay); + break; + + case IDPRAM_PM_RESUME_START: + case IDPRAM_PM_SUSPEND_PREPARE: + default: + break; + } + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_notify_pm_event(struct notifier_block *this, + unsigned long event, void *v) +{ + struct idpram_pm_data *pm_data; + struct dpram_link_device *dpld; + int err; + mif_info("+++ (event 0x%08X)\n", (int)event); + + pm_data = container_of(this, struct idpram_pm_data, pm_noti); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + + switch (event) { + case PM_SUSPEND_PREPARE: + err = s5p_idpram_prepare_suspend(dpld); + break; + + case PM_POST_SUSPEND: + err = s5p_idpram_start_resume(dpld); + break; + + default: + break; + } + + mif_info("---\n"); + return NOTIFY_DONE; +} + +static int s5p_idpram_pm_init(struct dpram_link_device *dpld, + struct modem_data *modem, void (*pm_tx_func)(struct work_struct *work)) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + int err; + unsigned gpio; + unsigned irq; + mif_info("+++\n"); + + atomic_set(&pm_data->pm_lock, 0); + + init_completion(&pm_data->down_cmpl); + + wake_lock_init(&pm_data->ap_wlock, WAKE_LOCK_SUSPEND, "ap_wakeup"); + wake_lock_init(&pm_data->hold_wlock, WAKE_LOCK_SUSPEND, "dpram_hold"); + + INIT_DELAYED_WORK(&pm_data->tx_dwork, pm_tx_func); + INIT_DELAYED_WORK(&pm_data->resume_dwork, s5p_idpram_try_resume); + + pm_data->resume_try_cnt = 0; + + /* register PM notifier */ + pm_data->pm_noti.notifier_call = s5p_idpram_notify_pm_event; + register_pm_notifier(&pm_data->pm_noti); + + /* + ** Register gpio_ap_wakeup interrupt handler + */ + gpio = modem->gpio_ap_wakeup; + irq = gpio_to_irq(gpio); + mif_info("gpio_ap_wakeup: GPIO# %d, IRQ# %d\n", gpio, irq); + + err = request_irq(irq, s5p_ap_wakeup_irq_handler, IRQF_TRIGGER_RISING, + "idpram_ap_wakeup", (void *)pm_data); + if (err) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); + goto exit; + } + + err = enable_irq_wake(irq); + if (err) { + mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); + free_irq(irq, (void *)pm_data); + goto exit; + } + + /* + ** Register gpio_cp_dump_int interrupt handler for LPA mode + */ + gpio = modem->gpio_cp_dump_int; + irq = gpio_to_irq(gpio); + mif_info("gpio_cp_dump_int: GPIO# %d, IRQ# %d\n", gpio, irq); + + err = request_irq(irq, s5p_cp_dump_irq_handler, IRQF_TRIGGER_RISING, + "idpram_cp_dump", (void *)pm_data); + if (err) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); + free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); + goto exit; + } + + err = enable_irq_wake(irq); + if (err) { + mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); + free_irq(gpio_to_irq(modem->gpio_cp_dump_int), (void *)pm_data); + free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); + goto exit; + } + +exit: + mif_err("---\n"); + return err; +} + +static bool s5p_idpram_int2cp_possible(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + int i; + int level; + + for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { + level = gpio_get_value(dpld->gpio_int2cp); + if (level) + break; + + /* CP has not yet received previous command. */ + mif_info("gpio_ipc_int2cp == 0 (count %d)\n", i); + + usleep_range(1000, 1100); + } + + for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { + level = gpio_get_value(mc->gpio_pda_active); + if (level) + break; + + /* AP is in transition to LPA mode. */ + mif_info("gpio_pda_active == 0 (count %d)\n", i); + + usleep_range(1000, 1100); + } + + return true; +} +#endif + +static struct idpram_pm_op idpram_pm_op_set[MAX_AP_TYPE] = { +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + [S5P] = { + .pm_init = s5p_idpram_pm_init, + .power_down = s5p_idpram_power_down, + .power_up = s5p_idpram_power_up, + .halt_suspend = s5p_idpram_halt_suspend, + .locked = s5p_idpram_is_pm_locked, + .int2cp_possible = s5p_idpram_int2cp_possible, + }, +#endif +}; + +struct idpram_pm_op *idpram_get_pm_op(enum ap_type ap) +{ + if (idpram_pm_op_set[ap].exist) + return &idpram_pm_op_set[ap]; + else + return NULL; +} + diff --git a/drivers/misc/modem_if/modem_link_device_hsic.c b/drivers/misc/modem_if/modem_link_device_hsic.c index eb5dfc6..e5bcf8a 100644 --- a/drivers/misc/modem_if/modem_link_device_hsic.c +++ b/drivers/misc/modem_if/modem_link_device_hsic.c @@ -269,7 +269,7 @@ static void usb_rx_complete(struct urb *urb) switch (pipe_data->format) { case IF_USB_FMT_EP: if (usb_ld->if_usb_is_main) { -// pr_urb("IPC-RX", urb); + pr_urb("IPC-RX", urb); iod_format = IPC_FMT; } else { iod_format = IPC_BOOT; @@ -477,13 +477,13 @@ static int _usb_tx_work(struct sk_buff *skb) if (!pipe_data) return -ENOENT; -/* + if (iod->format == IPC_FMT && usb_ld->if_usb_is_main) pr_skb("IPC-TX", skb); if (iod->format == IPC_RAW) mif_debug("TX[RAW]\n"); -*/ + return usb_tx_urb_with_skb(usb_ld->usbdev, skb, pipe_data); } @@ -741,11 +741,11 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data) != HOSTWAKE_TRIGLEVEL) { if (gpio_get_value(pm_data->gpio_link_slavewake)) { gpio_set_value(pm_data->gpio_link_slavewake, 0); - mif_debug("gpio [SWK] set [0]\n"); + mif_info("gpio [SWK] set [0]\n"); mdelay(5); } gpio_set_value(pm_data->gpio_link_slavewake, 1); - mif_debug("gpio [SWK] set [1]\n"); + mif_info("gpio [SWK] set [1]\n"); mdelay(5); /* wait host wake signal*/ @@ -860,7 +860,7 @@ static irqreturn_t link_pm_irq_handler(int irq, void *data) runtime pm status changes to ACTIVE */ value = gpio_get_value(pm_data->gpio_link_hostwake); - mif_debug("gpio [HWK] get [%d]\n", value); + mif_info("gpio [HWK] get [%d]\n", value); /* * igonore host wakeup interrupt at suspending kernel @@ -975,9 +975,7 @@ static int link_pm_notifier_event(struct notifier_block *this, { struct link_pm_data *pm_data = container_of(this, struct link_pm_data, pm_notifier); -#ifdef CONFIG_UMTS_MODEM_XMM6262 struct modem_ctl *mc = if_usb_get_modemctl(pm_data); -#endif switch (event) { case PM_SUSPEND_PREPARE: @@ -986,13 +984,11 @@ static int link_pm_notifier_event(struct notifier_block *this, case PM_RESTORE_PREPARE: #endif pm_data->dpm_suspending = true; -#ifdef CONFIG_UMTS_MODEM_XMM6262 /* set PDA Active High if previous state was LPA */ if (!gpio_get_value(pm_data->gpio_link_active)) { mif_info("PDA active High to LPA suspend spot\n"); gpio_set_value(mc->gpio_pda_active, 1); } -#endif mif_debug("dpm suspending set to true\n"); return NOTIFY_OK; case PM_POST_SUSPEND: @@ -1006,15 +1002,14 @@ static int link_pm_notifier_event(struct notifier_block *this, queue_delayed_work(pm_data->wq, &pm_data->link_pm_work, 0); mif_info("post resume\n"); - } -#ifdef CONFIG_UMTS_MODEM_XMM6262 + } else { /* LPA to Kernel suspend and User Freezing task fail resume, restore to LPA GPIO states. */ - if (!gpio_get_value(pm_data->gpio_link_active)) { - mif_info("PDA active low to LPA GPIO state\n"); - gpio_set_value(mc->gpio_pda_active, 0); + if (!gpio_get_value(pm_data->gpio_link_active)) { + mif_info("PDA active low to LPA GPIO state\n"); + gpio_set_value(mc->gpio_pda_active, 0); + } } -#endif mif_debug("dpm suspending set to false\n"); return NOTIFY_OK; } @@ -1458,8 +1453,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) struct modem_data *pdata = (struct modem_data *)pdev->dev.platform_data; struct modemlink_pm_data *pm_pdata; - struct link_pm_data *pm_data = - kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); + struct link_pm_data *pm_data; if (!pdata || !pdata->link_pm_data) { mif_err("platform data is NULL\n"); @@ -1467,6 +1461,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) } pm_pdata = pdata->link_pm_data; + pm_data = kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); if (!pm_data) { mif_err("link_pm_data is NULL\n"); return -ENOMEM; diff --git a/drivers/misc/modem_if/modem_link_device_memory.c b/drivers/misc/modem_if/modem_link_device_memory.c new file mode 100644 index 0000000..9231135 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_memory.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2011 Samsung Electronics. + * + * 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 "modem_prj.h" +#include "modem_utils.h" +#include "modem_link_device_memory.h" +#ifdef CONFIG_LINK_DEVICE_DPRAM +#include "modem_link_device_dpram.h" +#endif + +/** + * msq_get_free_slot + * @trq : pointer to an instance of mem_status_queue structure + * + * Succeeds always by dropping the oldest slot if a "msq" is full. + */ +struct mem_status *msq_get_free_slot(struct mem_status_queue *msq) +{ + int qsize = MAX_MEM_LOG_CNT; + int in; + int out; + unsigned long flags; + struct mem_status *stat; + + spin_lock_irqsave(&msq->lock, flags); + + in = msq->in; + out = msq->out; + + if (circ_get_space(qsize, in, out) < 1) { + /* Make the oldest slot empty */ + out++; + msq->out = (out == qsize) ? 0 : out; + } + + /* Get a free slot */ + stat = &msq->stat[in]; + + /* Make it as "data" slot */ + in++; + msq->in = (in == qsize) ? 0 : in; + + spin_unlock_irqrestore(&msq->lock, flags); + + memset(stat, 0, sizeof(struct mem_status)); + + return stat; +} + +struct mem_status *msq_get_data_slot(struct mem_status_queue *msq) +{ + int qsize = MAX_MEM_LOG_CNT; + int in; + int out; + unsigned long flags; + struct mem_status *stat; + + spin_lock_irqsave(&msq->lock, flags); + + in = msq->in; + out = msq->out; + + if (in == out) { + stat = NULL; + goto exit; + } + + /* Get a data slot */ + stat = &msq->stat[out]; + + /* Make it "free" slot */ + out++; + msq->out = (out == qsize) ? 0 : out; + +exit: + spin_unlock_irqrestore(&msq->lock, flags); + return stat; +} + +/** + * memcpy16_from_io + * @to: pointer to "real" memory + * @from: pointer to IO memory + * @count: data length in bytes to be copied + * + * Copies data from IO memory space to "real" memory space. + */ +void memcpy16_from_io(const void *to, const void __iomem *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + u32 words = count >> 1; + while (words--) + *d++ = ioread16(s++); +} + +/** + * memcpy16_to_io + * @to: pointer to IO memory + * @from: pointer to "real" memory + * @count: data length in bytes to be copied + * + * Copies data from "real" memory space to IO memory space. + */ +void memcpy16_to_io(const void __iomem *to, const void *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + u32 words = count >> 1; + while (words--) + iowrite16(*s++, d++); +} + +/** + * memcmp16_to_io + * @to: pointer to IO memory + * @from: pointer to "real" memory + * @count: data length in bytes to be compared + * + * Compares data from "real" memory space to IO memory space. + */ +int memcmp16_to_io(const void __iomem *to, const void *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + int words = count >> 1; + int diff = 0; + int i; + u16 d1; + u16 s1; + + for (i = 0; i < words; i++) { + d1 = ioread16(d); + s1 = *s; + if (d1 != s1) { + diff++; + mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); + } + d++; + s++; + } + + return diff; +} + +/** + * circ_read16_from_io + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy16_from_io(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) data start (out) ~ buffer end */ + memcpy16_from_io(dst, (src + out), len1); + + /* 2) buffer start ~ data end (in - 1) */ + memcpy16_from_io((dst + len1), src, (len - len1)); + } +} + +/** + * circ_write16_to_io + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + memcpy16_to_io((dst + in), src, len); + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + memcpy16_to_io((dst + in), src, ((len > space) ? space : len)); + + /* 2) buffer start ~ data end */ + if (len > space) + memcpy16_to_io(dst, (src + space), (len - space)); + } +} + +/** + * copy_circ_to_user + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + if (copy_to_user(dst, (src + out), len)) { + mif_err("ERR! copy_to_user fail\n", + CALLER); + return -EFAULT; + } + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) data start (out) ~ buffer end */ + if (copy_to_user(dst, (src + out), len1)) { + mif_err("ERR! copy_to_user fail\n", + CALLER); + return -EFAULT; + } + + /* 2) buffer start ~ data end (in?) */ + if (copy_to_user((dst + len1), src, (len - len1))) { + mif_err("ERR! copy_to_user fail\n", + CALLER); + return -EFAULT; + } + } + + return 0; +} + +/** + * copy_user_to_circ + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + u32 len1; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + if (copy_from_user((dst + in), src, len)) { + mif_err("ERR! copy_from_user fail\n", + CALLER); + return -EFAULT; + } + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + len1 = (len > space) ? space : len; + if (copy_from_user((dst + in), src, len1)) { + mif_err("ERR! copy_from_user fail\n", + CALLER); + return -EFAULT; + } + + /* 2) buffer start ~ data end */ + if (len > len1) { + if (copy_from_user(dst, (src + space), (len - len1))) { + mif_err("ERR! copy_from_user " + "fail\n", CALLER); + return -EFAULT; + } + } + } + + return 0; +} + +/** + * print_mem_status + * @ld: pointer to an instance of link_device structure + * @mst: pointer to an instance of mem_status structure + * + * Prints a snapshot of the status of a SHM. + */ +void print_mem_status(struct link_device *ld, struct mem_status *mst) +{ + struct utc_time utc; + int us = ns2us(mst->ts.tv_nsec); + + ts2utc(&mst->ts, &utc); + pr_info("%s: %s: [%02d:%02d:%02d.%06d] " + "[%s] ACC{%X %d} " + "FMT{TI:%u TO:%u RI:%u RO:%u} " + "RAW{TI:%u TO:%u RI:%u RO:%u} " + "INTR{RX:0x%X TX:0x%X}\n", + MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, + get_dir_str(mst->dir), mst->magic, mst->access, + mst->head[IPC_FMT][TX], mst->tail[IPC_FMT][TX], + mst->head[IPC_FMT][RX], mst->tail[IPC_FMT][RX], + mst->head[IPC_RAW][TX], mst->tail[IPC_RAW][TX], + mst->head[IPC_RAW][RX], mst->tail[IPC_RAW][RX], + mst->int2ap, mst->int2cp); +} + +/** + * print_circ_status + * @ld: pointer to an instance of link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Prints a snapshot of the status of a memory + */ +void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst) +{ + struct utc_time utc; + int us = ns2us(mst->ts.tv_nsec); + + if (dev > IPC_RAW) + return; + + ts2utc(&mst->ts, &utc); + pr_info("%s: %s: [%02d:%02d:%02d.%06d] " + "[%s] %s | TXQ{in:%u out:%u} RXQ{in:%u out:%u}\n", + MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, + get_dir_str(mst->dir), get_dev_name(dev), + mst->head[dev][TX], mst->tail[dev][TX], + mst->head[dev][RX], mst->tail[dev][RX]); +} + +/** + * print_ipc_trace + * @ld: pointer to an instance of link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @stat: pointer to an instance of circ_status structure + * @ts: pointer to an instance of timespec structure + * @buff: start address of a buffer into which RX IPC messages were copied + * @rcvd: size of data in the buffer + * + * Prints IPC messages in a local memory buffer to a kernel log. + */ +void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, + struct timespec *ts, u8 *buff, u32 rcvd) +{ + struct utc_time utc; + + ts2utc(ts, &utc); + + pr_info("%s: [%d-%02d-%02d %02d:%02d:%02d.%03d] " + "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", + MIF_TAG, utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec, + utc.msec, ld->name, get_dev_name(dev), stat->in, stat->out, + stat->size); + + mif_print_dump(buff, rcvd, 4); +} + +/** + * capture_mem_dump + * @ld: pointer to an instance of link_device structure + * @base: base virtual address to a memory interface medium + * @size: size of the memory interface medium + * + * Captures a dump for a memory interface medium. + * + * Returns the pointer to a memory dump buffer. + */ +u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size) +{ + u8 *buff = kzalloc(size, GFP_ATOMIC); + if (!buff) { + mif_err("%s: ERR! kzalloc(%d) fail\n", ld->name, size); + return NULL; + } else { + memcpy16_from_io(buff, base, size); + return buff; + } +} + +/** + * trq_get_free_slot + * @trq : pointer to an instance of trace_data_queue structure + * + * Succeeds always by dropping the oldest slot if a "trq" is full. + */ +struct trace_data *trq_get_free_slot(struct trace_data_queue *trq) +{ + int qsize = MAX_TRACE_SIZE; + int in; + int out; + unsigned long flags; + struct trace_data *trd; + + spin_lock_irqsave(&trq->lock, flags); + + in = trq->in; + out = trq->out; + + /* The oldest slot can be dropped. */ + if (circ_get_space(qsize, in, out) < 1) { + /* Free the data buffer in the oldest slot */ + trd = &trq->trd[out]; + kfree(trd->data); + + /* Make the oldest slot empty */ + out++; + trq->out = (out == qsize) ? 0 : out; + } + + /* Get a free slot and make it occupied */ + trd = &trq->trd[in++]; + trq->in = (in == qsize) ? 0 : in; + + spin_unlock_irqrestore(&trq->lock, flags); + + memset(trd, 0, sizeof(struct trace_data)); + + return trd; +} + +struct trace_data *trq_get_data_slot(struct trace_data_queue *trq) +{ + int qsize = MAX_TRACE_SIZE; + int in; + int out; + unsigned long flags; + struct trace_data *trd; + + spin_lock_irqsave(&trq->lock, flags); + + in = trq->in; + out = trq->out; + + if (circ_get_usage(qsize, in, out) < 1) { + spin_unlock_irqrestore(&trq->lock, flags); + return NULL; + } + + /* Get a data slot and make it empty */ + trd = &trq->trd[out++]; + trq->out = (out == qsize) ? 0 : out; + + spin_unlock_irqrestore(&trq->lock, flags); + + return trd; +} + diff --git a/drivers/misc/modem_if/modem_link_device_memory.h b/drivers/misc/modem_if/modem_link_device_memory.h new file mode 100644 index 0000000..9976b23 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_memory.h @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 __MODEM_LINK_DEVICE_MEMORY_H__ +#define __MODEM_LINK_DEVICE_MEMORY_H__ + +#include +#include +#include +#include +#include +#if defined(CONFIG_HAS_EARLYSUSPEND) +#include +#elif defined(CONFIG_FB) +#include +#endif + +#include +#include "modem_prj.h" + +#define DPRAM_MAGIC_CODE 0xAA + +/* interrupt masks.*/ +#define INT_MASK_VALID 0x0080 +#define INT_MASK_CMD 0x0040 +#define INT_VALID(x) ((x) & INT_MASK_VALID) +#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) +#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) +#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) + +#define EXT_UDL_MASK 0xF000 +#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) +#define EXT_INT_VALID_MASK 0x8000 +#define EXT_CMD_VALID_MASK 0x4000 +#define UDL_CMD_VALID_MASK 0x2000 +#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) +#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) +#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) +#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) + +#define EXT_CMD_MASK(x) ((x) & 0x0FFF) +#define EXT_CMD_SET_SPEED_LOW 0x0011 +#define EXT_CMD_SET_SPEED_MID 0x0012 +#define EXT_CMD_SET_SPEED_HIGH 0x0013 + +#define UDL_RESULT_SUCCESS 0x1 +#define UDL_RESULT_FAIL 0x2 + +#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) +#define UDL_CMD_RECV_READY 0x1 +#define UDL_CMD_DL_START_REQ 0x2 +#define UDL_CMD_DL_START_RESP 0x3 +#define UDL_CMD_IMAGE_SEND_REQ 0x4 +#define UDL_CMD_SEND_DONE_RESP 0x5 +#define UDL_CMD_SEND_DONE_REQ 0x6 +#define UDL_CMD_UPDATE_DONE 0x7 +#define UDL_CMD_STATUS_UPDATE 0x8 +#define UDL_CMD_IMAGE_SEND_RESP 0x9 +#define UDL_CMD_EFS_CLEAR_RESP 0xB +#define UDL_CMD_ALARM_BOOT_OK 0xC +#define UDL_CMD_ALARM_BOOT_FAIL 0xD + +#define CMD_DL_READY 0xA100 +#define CMD_DL_START_REQ 0x9200 +#define CMD_DL_START_RESP 0xA301 +#define CMD_DL_SEND_REQ 0x9400 +#define CMD_DL_SEND_RESP 0xA501 +#define CMD_DL_DONE_REQ 0x9600 +#define CMD_DL_DONE_RESP 0xA701 + +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_REQ 0xA700 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#define INT_MASK_REQ_ACK_F 0x0020 +#define INT_MASK_REQ_ACK_R 0x0010 +#define INT_MASK_RES_ACK_F 0x0008 +#define INT_MASK_RES_ACK_R 0x0004 +#define INT_MASK_SEND_F 0x0002 +#define INT_MASK_SEND_R 0x0001 + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_REQ_ACK_SET \ + (INT_MASK_REQ_ACK_F | INT_MASK_REQ_ACK_R | INT_MASK_REQ_ACK_RFS) + +#define INT_MASK_RES_ACK_SET \ + (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) + +#define INT_CMD_MASK(x) ((x) & 0xF) +#define INT_CMD_INIT_START 0x1 +#define INT_CMD_INIT_END 0x2 +#define INT_CMD_REQ_ACTIVE 0x3 +#define INT_CMD_RES_ACTIVE 0x4 +#define INT_CMD_REQ_TIME_SYNC 0x5 +#define INT_CMD_CRASH_RESET 0x7 +#define INT_CMD_PHONE_START 0x8 +#define INT_CMD_ERR_DISPLAY 0x9 +#define INT_CMD_CRASH_EXIT 0x9 +#define INT_CMD_CP_DEEP_SLEEP 0xA +#define INT_CMD_NV_REBUILDING 0xB +#define INT_CMD_EMER_DOWN 0xC +#define INT_CMD_PIF_INIT_DONE 0xD +#define INT_CMD_SILENT_NV_REBUILDING 0xE +#define INT_CMD_NORMAL_POWER_OFF 0xF + +/* AP_IDPRAM PM control command with QSC6085 */ +#define INT_CMD_IDPRAM_SUSPEND_REQ 0xD +#define INT_CMD_IDPRAM_SUSPEND_ACK 0xB +#define INT_CMD_IDPRAM_WAKEUP_START 0xE +#define INT_CMD_IDPRAM_RESUME_REQ 0xC + +#define START_FLAG 0x7F +#define END_FLAG 0x7E + +#define DP_MAGIC_DMDL 0x4445444C +#define DP_MAGIC_UMDL 0x4445444D +#define DP_DPRAM_SIZE 0x4000 +#define DP_DEFAULT_WRITE_LEN 8168 +#define DP_DEFAULT_DUMP_LEN 16128 +#define DP_DUMP_HEADER_SIZE 7 + +#define UDL_TIMEOUT (50 * HZ) +#define UDL_SEND_TIMEOUT (200 * HZ) +#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) +#define DUMP_TIMEOUT (30 * HZ) +#define DUMP_START_TIMEOUT (100 * HZ) +#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ + +#define IDPRAM_SUSPEND_REQ_TIMEOUT (50 * HZ) + +#define RES_ACK_WAIT_TIMEOUT 10 /* 10 ms */ +#define REQ_ACK_DELAY 10 /* 10 ms */ + +#ifdef DEBUG_MODEM_IF +#define MAX_RETRY_CNT 1 +#else +#define MAX_RETRY_CNT 3 +#endif + +#define MAX_SKB_TXQ_DEPTH 1024 + +struct memif_boot_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 __iomem *req; + u32 __iomem *resp; + u32 space; +}; + +struct memif_dload_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 space; +}; + +struct memif_uload_map { + u32 __iomem *magic; + u8 __iomem *cmd; + u32 cmd_size; + u8 __iomem *buff; + u32 space; +}; + +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_MAGIC_SIZE 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_MAGIC_SIZE 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 +#define DP_MBX_SET_SIZE 4 +#define DP_MAX_PAYLOAD_SIZE 0x2000 + +enum circ_dir_type { + TX, + RX, + MAX_DIR, +}; + +enum circ_ptr_type { + HEAD, + TAIL, +}; + +static inline bool circ_valid(u32 qsize, u32 in, u32 out) +{ + if (in >= qsize) + return false; + + if (out >= qsize) + return false; + + return true; +} + +static inline u32 circ_get_space(u32 qsize, u32 in, u32 out) +{ + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline u32 circ_get_usage(u32 qsize, u32 in, u32 out) +{ + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline u32 circ_new_pointer(u32 qsize, u32 p, u32 len) +{ + p += len; + return (p < qsize) ? p : (p - qsize); +} + +/** + * circ_read + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +static inline void circ_read(void *dst, void *src, u32 qsize, u32 out, u32 len) +{ + unsigned len1; + + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + + /* 1) data start (out) ~ buffer end */ + len1 = qsize - out; + memcpy(dst, (src + out), len1); + + /* 2) buffer start ~ data end (in?) */ + memcpy((dst + len1), src, (len - len1)); + } +} + +/** + * circ_write + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +static inline void circ_write(void *dst, void *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + memcpy((dst + in), src, len); + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + memcpy((dst + in), src, ((len > space) ? space : len)); + + /* 2) buffer start ~ data end */ + if (len > space) + memcpy(dst, (src + space), (len - space)); + } +} + +/** + * circ_dir + * @dir: communication direction (enum circ_dir_type) + * + * Returns the direction of a circular queue + * + */ +static const inline char *circ_dir(enum circ_dir_type dir) +{ + if (dir == TX) + return "TXQ"; + else + return "RXQ"; +} + +/** + * circ_ptr + * @ptr: circular queue pointer (enum circ_ptr_type) + * + * Returns the name of a circular queue pointer + * + */ +static const inline char *circ_ptr(enum circ_ptr_type ptr) +{ + if (ptr == HEAD) + return "head"; + else + return "tail"; +} + +/** + * get_dir_str + * @dir: communication direction (enum circ_dir_type) + * + * Returns the direction of a circular queue + * + */ +static const inline char *get_dir_str(enum circ_dir_type dir) +{ + if (dir == TX) + return "AP->CP"; + else + return "CP->AP"; +} + +void memcpy16_from_io(const void *to, const void __iomem *from, u32 count); +void memcpy16_to_io(const void __iomem *to, const void *from, u32 count); +int memcmp16_to_io(const void __iomem *to, const void *from, u32 count); +void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len); +void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len); +int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len); +int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len); + +#define MAX_MEM_LOG_CNT 8192 +#define MAX_TRACE_SIZE 1024 + +struct mem_status { + /* Timestamp */ + struct timespec ts; + + /* Direction (TX or RX) */ + enum circ_dir_type dir; + + /* The status of memory interface at the time */ + u32 magic; + u32 access; + + u32 head[MAX_IPC_DEV][MAX_DIR]; + u32 tail[MAX_IPC_DEV][MAX_DIR]; + + u16 int2ap; + u16 int2cp; +}; + +struct mem_status_queue { + spinlock_t lock; + u32 in; + u32 out; + struct mem_status stat[MAX_MEM_LOG_CNT]; +}; + +struct circ_status { + u8 *buff; + u32 qsize; /* the size of a circular buffer */ + u32 in; + u32 out; + u32 size; /* the size of free space or received data */ +}; + +struct trace_data { + struct timespec ts; + enum dev_format dev; + struct circ_status circ_stat; + u8 *data; + u32 size; +}; + +struct trace_data_queue { + spinlock_t lock; + u32 in; + u32 out; + struct trace_data trd[MAX_TRACE_SIZE]; +}; + +struct mem_status *msq_get_free_slot(struct mem_status_queue *msq); +struct mem_status *msq_get_data_slot(struct mem_status_queue *msq); + +void print_mem_status(struct link_device *ld, struct mem_status *mst); +void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst); +void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, + struct timespec *ts, u8 *buff, u32 rcvd); + +u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size); +struct trace_data *trq_get_free_slot(struct trace_data_queue *trq); +struct trace_data *trq_get_data_slot(struct trace_data_queue *trq); + +#endif + diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c index b68040e..b8b6a3b 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.c +++ b/drivers/misc/modem_if/modem_link_device_pld.c @@ -25,190 +25,76 @@ #include #include #include -#include +#include #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" - /* ** Function prototypes for basic DPRAM operations */ -static inline void clear_intr(struct dpram_link_device *dpld); -static inline u16 recv_intr(struct dpram_link_device *dpld); -static inline void send_intr(struct dpram_link_device *dpld, u16 mask); - -static inline u16 get_magic(struct dpram_link_device *dpld); -static inline void set_magic(struct dpram_link_device *dpld, u16 val); -static inline u16 get_access(struct dpram_link_device *dpld); -static inline void set_access(struct dpram_link_device *dpld, u16 val); - -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); - -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); - -static void handle_cp_crash(struct dpram_link_device *dpld); -static int trigger_force_cp_crash(struct dpram_link_device *dpld); +static inline void clear_intr(struct pld_link_device *pld); +static inline u16 recv_intr(struct pld_link_device *pld); +static inline void send_intr(struct pld_link_device *pld, u16 mask); + +static inline u16 get_magic(struct pld_link_device *pld); +static inline void set_magic(struct pld_link_device *pld, u16 val); +static inline u16 get_access(struct pld_link_device *pld); +static inline void set_access(struct pld_link_device *pld, u16 val); + +static inline u32 get_tx_head(struct pld_link_device *pld, int id); +static inline u32 get_tx_tail(struct pld_link_device *pld, int id); +static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in); +static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out); +static inline u8 *get_tx_buff(struct pld_link_device *pld, int id); +static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id); + +static inline u32 get_rx_head(struct pld_link_device *pld, int id); +static inline u32 get_rx_tail(struct pld_link_device *pld, int id); +static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in); +static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out); +static inline u8 *get_rx_buff(struct pld_link_device *pld, int id); +static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id); + +static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id); +static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id); +static inline u16 get_mask_send(struct pld_link_device *pld, int id); + +static void handle_cp_crash(struct pld_link_device *pld); +static int trigger_force_cp_crash(struct pld_link_device *pld); /* ** Functions for debugging */ -static inline void log_dpram_status(struct dpram_link_device *dpld) -{ - pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " - "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", - dpld->ld.mc->name, - get_magic(dpld), get_access(dpld), - get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), - get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), - get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), - get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), - recv_intr(dpld)); -} - -static void set_dpram_map(struct dpram_link_device *dpld, +static void set_dpram_map(struct pld_link_device *pld, struct mif_irq_map *map) { - map->magic = get_magic(dpld); - map->access = get_access(dpld); - - map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); - map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); - map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); - map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); - map->raw_tx_in = get_tx_head(dpld, IPC_RAW); - map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); - map->raw_rx_in = get_rx_head(dpld, IPC_RAW); - map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); - - map->cp2ap = recv_intr(dpld); -} - -/* -** RXB (DPRAM RX buffer) functions -*/ -static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) -{ - struct dpram_rxb *rxb; - u8 *buff; - int i; - - rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); - if (!rxb) { - mif_info("ERR! kzalloc rxb fail\n"); - return NULL; - } - - buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); - if (!buff) { - mif_info("ERR! kzalloc buff fail\n"); - kfree(rxb); - return NULL; - } - - for (i = 0; i < count; i++) { - rxb[i].buff = buff; - rxb[i].size = size; - buff += size; - } - - return rxb; -} - -static inline unsigned rxbq_get_page_size(unsigned len) -{ - return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; -} - -static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) -{ - return (rxbq->in == rxbq->out) ? true : false; -} - -static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in < out) ? (out - in - 1) : (qsize + out - in - 1); -} - -static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; - - if (likely(rxbq_free_size(rxbq) > 0)) { - rxb = &rxbq->rxb[rxbq->in]; - rxbq->in++; - if (rxbq->in >= rxbq->size) - rxbq->in -= rxbq->size; - rxb->data = rxb->buff; - } - - return rxb; -} - -static inline int rxbq_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in >= out) ? (in - out) : (qsize - out + in); -} + map->magic = get_magic(pld); + map->access = get_access(pld); -static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; + map->fmt_tx_in = get_tx_head(pld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(pld, IPC_FMT); + map->fmt_rx_in = get_rx_head(pld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(pld, IPC_FMT); + map->raw_tx_in = get_tx_head(pld, IPC_RAW); + map->raw_tx_out = get_tx_tail(pld, IPC_RAW); + map->raw_rx_in = get_rx_head(pld, IPC_RAW); + map->raw_rx_out = get_rx_tail(pld, IPC_RAW); - if (likely(!rxbq_empty(rxbq))) { - rxb = &rxbq->rxb[rxbq->out]; - rxbq->out++; - if (rxbq->out >= rxbq->size) - rxbq->out -= rxbq->size; - } - - return rxb; -} - -static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) -{ - rxb->len = len; - return rxb->data; -} - -static inline void rxb_clear(struct dpram_rxb *rxb) -{ - rxb->data = NULL; - rxb->len = 0; + map->cp2ap = recv_intr(pld); } /* ** DPRAM operations */ -static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), +static int pld_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), unsigned long flag, const char *name, - struct dpram_link_device *dpld) + struct pld_link_device *pld) { int ret = 0; - ret = request_irq(irq, isr, flag, name, dpld); + ret = request_irq(irq, isr, flag, name, pld); if (ret) { mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); return ret; @@ -223,31 +109,31 @@ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), return 0; } -static inline void clear_intr(struct dpram_link_device *dpld) +static inline void clear_intr(struct pld_link_device *pld) { - if (dpld->dpctl->clear_intr) - dpld->dpctl->clear_intr(); + if (pld->ext_op && pld->ext_op->clear_intr) + pld->ext_op->clear_intr(pld); } -static inline u16 recv_intr(struct dpram_link_device *dpld) +static inline u16 recv_intr(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -255,31 +141,31 @@ static inline u16 recv_intr(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +static inline void send_intr(struct pld_link_device *pld, u16 mask) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - iowrite16((u16)mask, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + iowrite16((u16)mask, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == mask)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -287,35 +173,35 @@ static inline void send_intr(struct dpram_link_device *dpld, u16 mask) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - iowrite16((u16)mask, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + iowrite16((u16)mask, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u16 get_magic(struct dpram_link_device *dpld) +static inline u16 get_magic(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -324,31 +210,31 @@ static inline u16 get_magic(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_magic(struct dpram_link_device *dpld, u16 in) +static inline void set_magic(struct pld_link_device *pld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -356,34 +242,34 @@ static inline void set_magic(struct dpram_link_device *dpld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u16 get_access(struct dpram_link_device *dpld) +static inline u16 get_access(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -392,31 +278,31 @@ static inline u16 get_access(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_access(struct dpram_link_device *dpld, u16 in) +static inline void set_access(struct pld_link_device *pld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -424,34 +310,34 @@ static inline void set_access(struct dpram_link_device *dpld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_head(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -461,29 +347,29 @@ static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_tail(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -493,30 +379,30 @@ static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -525,49 +411,49 @@ static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out) { return; } -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +static inline u8 *get_tx_buff(struct pld_link_device *pld, int id) { - return dpld->dev[id]->txq.buff; + return pld->dev[id]->txq.buff; } -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id) { - return dpld->dev[id]->txq.size; + return pld->dev[id]->txq.size; } -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_head(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -577,29 +463,29 @@ static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_tail(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -609,35 +495,35 @@ static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in) { return; } -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - iowrite16((u16)out, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + iowrite16((u16)out, pld->base); do { /* Check tail value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val = ioread16(pld->base); if (val == out) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -646,71 +532,60 @@ static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) udelay(100); /* Write tail value again */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - iowrite16((u16)out, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + iowrite16((u16)out, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) -{ - return dpld->dev[id]->rxq.buff; -} - -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +static inline u8 *get_rx_buff(struct pld_link_device *pld, int id) { - return dpld->dev[id]->rxq.size; + return pld->dev[id]->rxq.buff; } -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_req_ack; + return pld->dev[id]->rxq.size; } -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_res_ack; + return pld->dev[id]->mask_req_ack; } -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_send; + return pld->dev[id]->mask_res_ack; } -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +static inline u16 get_mask_send(struct pld_link_device *pld, int id) { - if (in >= size) - return false; - - if (out >= size) - return false; - - return true; + return pld->dev[id]->mask_send; } /* Get free space in the TXQ as well as in & out pointers */ -static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +static inline int get_txq_space(struct pld_link_device *pld, int dev, u32 qsize, + u32 *in, u32 *out) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int cnt = 3; u32 head; u32 tail; int space; do { - head = get_tx_head(dpld, dev); - tail = get_tx_tail(dpld, dev); + head = get_tx_head(pld, dev); + tail = get_tx_tail(pld, dev); space = (head < tail) ? (tail - head - 1) : (qsize + tail - head - 1); mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", ld->name, get_dev_name(dev), qsize, head, tail, space); - if (dpram_circ_valid(qsize, head, tail)) { + if (circ_valid(qsize, head, tail)) { *in = head; *out = tail; return space; @@ -729,27 +604,27 @@ static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, return -EINVAL; } -static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +static void reset_tx_circ(struct pld_link_device *pld, int dev) { - set_tx_head(dpld, dev, 0); - set_tx_tail(dpld, dev, 0); + set_tx_head(pld, dev, 0); + set_tx_tail(pld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + trigger_force_cp_crash(pld); } /* Get data size in the RXQ as well as in & out pointers */ -static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +static inline int get_rxq_rcvd(struct pld_link_device *pld, int dev, u32 qsize, + u32 *in, u32 *out) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int cnt = 3; u32 head; u32 tail; u32 rcvd; do { - head = get_rx_head(dpld, dev); - tail = get_rx_tail(dpld, dev); + head = get_rx_head(pld, dev); + tail = get_rx_tail(pld, dev); if (head == tail) { *in = head; *out = tail; @@ -760,7 +635,7 @@ static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, mif_info("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", ld->name, get_dev_name(dev), qsize, head, tail, rcvd); - if (dpram_circ_valid(qsize, head, tail)) { + if (circ_valid(qsize, head, tail)) { *in = head; *out = tail; return rcvd; @@ -779,54 +654,20 @@ static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, return -EINVAL; } -static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +static void reset_rx_circ(struct pld_link_device *pld, int dev) { - set_rx_head(dpld, dev, 0); - set_rx_tail(dpld, dev, 0); + set_rx_head(pld, dev, 0); + set_rx_tail(pld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + trigger_force_cp_crash(pld); } -/* -** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success -*/ -static int dpram_wake_up(struct dpram_link_device *dpld) +static int check_access(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; - - if (!dpld->dpctl->wakeup) - return 0; - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail\n", - ld->name, __builtin_return_address(0)); - return -EACCES; - } - - atomic_inc(&dpld->accessing); - return 0; -} - -static void dpram_allow_sleep(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - - if (!dpld->dpctl->sleep) - return; - - if (atomic_dec_return(&dpld->accessing) <= 0) { - dpld->dpctl->sleep(); - atomic_set(&dpld->accessing, 0); - mif_debug("%s: DPRAM sleep possible\n", ld->name); - } -} - -static int dpram_check_access(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i; - u16 magic = get_magic(dpld); - u16 access = get_access(dpld); + u16 magic = get_magic(pld); + u16 access = get_access(pld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; @@ -836,8 +677,8 @@ static int dpram_check_access(struct dpram_link_device *dpld) ld->name, magic, access, i); udelay(100); - magic = get_magic(dpld); - access = get_access(dpld); + magic = get_magic(pld); + access = get_access(pld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; } @@ -846,9 +687,9 @@ static int dpram_check_access(struct dpram_link_device *dpld) return -EACCES; } -static bool dpram_ipc_active(struct dpram_link_device *dpld) +static bool ipc_active(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { @@ -857,8 +698,8 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return false; } - if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pf> dpram_check_access fail\n", + if (check_access(pld) < 0) { + mif_info("%s: ERR! <%pf> check_access fail\n", ld->name, __builtin_return_address(0)); return false; } @@ -866,67 +707,72 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return true; } -static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, +static void pld_ipc_write(struct pld_link_device *pld, int dev, u32 qsize, u32 in, u32 out, struct sk_buff *skb) { - struct link_device *ld = &dpld->ld; - u8 __iomem *buff = get_tx_buff(dpld, dev); + struct link_device *ld = &pld->ld; + u8 __iomem *buff = get_tx_buff(pld, dev); u8 *src = skb->data; u32 len = skb->len; u32 inp; struct mif_irq_map map; + unsigned long int flags; + + spin_lock_irqsave(&pld->pld_lock, flags); if (in < out) { /* +++++++++ in ---------- out ++++++++++ */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); - memcpy(dpld->dp_base, src, len); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); + memcpy(pld->base, src, len); } else { /* ------ out +++++++++++ in ------------ */ u32 space = qsize - in; /* 1) in -> buffer end */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); - memcpy(dpld->dp_base, src, ((len > space) ? space : len)); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); + memcpy(pld->base, src, ((len > space) ? space : len)); if (len > space) { iowrite16(PLD_ADDR_MASK(&buff[0]), - dpld->address_buffer); - memcpy(dpld->dp_base, (src+space), (len-space)); + pld->address_buffer); + memcpy(pld->base, (src+space), (len-space)); } } + spin_unlock_irqrestore(&pld->pld_lock, flags); + /* update new in pointer */ inp = in + len; if (inp >= qsize) inp -= qsize; - set_tx_head(dpld, dev, inp); + set_tx_head(pld, dev, inp); if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); + set_dpram_map(pld, &map); mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); } } -static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) +static int pld_try_ipc_tx(struct pld_link_device *pld, int dev) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct sk_buff_head *txq = ld->skb_txq[dev]; struct sk_buff *skb; unsigned long int flags; - u32 qsize = get_tx_buff_size(dpld, dev); + u32 qsize = get_tx_buff_size(pld, dev); u32 in; u32 out; int space; int copied = 0; - spin_lock_irqsave(&dpld->tx_rx_lock, flags); + spin_lock_irqsave(&pld->tx_rx_lock, flags); while (1) { - space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); + space = get_txq_space(pld, dev, qsize, &in, &out); if (unlikely(space < 0)) { - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); - dpram_reset_tx_circ(dpld, dev); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + reset_tx_circ(pld, dev); return space; } @@ -935,9 +781,9 @@ static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) break; if (unlikely(space < skb->len)) { - atomic_set(&dpld->res_required[dev], 1); + atomic_set(&pld->res_required[dev], 1); skb_queue_head(txq, skb); - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); mif_info("%s: %s " "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", ld->name, get_dev_name(dev), @@ -946,30 +792,30 @@ static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) } /* TX if there is enough room in the queue */ - dpram_ipc_write(dpld, dev, qsize, in, out, skb); + pld_ipc_write(pld, dev, qsize, in, out, skb); copied += skb->len; dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return copied; } -static void dpram_ipc_rx_task(unsigned long data) +static void pld_ipc_rx_task(unsigned long data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = &dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)data; + struct link_device *ld = &pld->ld; struct io_device *iod; - struct dpram_rxb *rxb; + struct mif_rxb *rxb; unsigned qlen; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { - iod = dpld->iod[i]; - qlen = rxbq_size(&dpld->rxbq[i]); + for (i = 0; i < ld->max_ipc_dev; i++) { + iod = pld->iod[i]; + qlen = rxbq_size(&pld->rxbq[i]); while (qlen > 0) { - rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); + rxb = rxbq_get_data_rxb(&pld->rxbq[i]); iod->recv(iod, ld, rxb->data, rxb->len); rxb_clear(rxb); qlen--; @@ -977,36 +823,39 @@ static void dpram_ipc_rx_task(unsigned long data) } } -static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, +static void pld_ipc_read(struct pld_link_device *pld, int dev, u8 *dst, u8 __iomem *src, u32 out, u32 len, u32 qsize) { u8 *ori_det = dst; unsigned long flags; + spin_lock_irqsave(&pld->pld_lock, flags); + if ((out + len) <= qsize) { /* ----- (out) (in) ----- */ /* ----- 7f 00 00 7e ----- */ - iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, len); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); + memcpy(dst, pld->base, len); } else { /* (in) ----------- (out) */ /* 00 7e ----------- 7f 00 */ unsigned len1 = qsize - out; /* 1) out -> buffer end */ - iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, len1); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); + memcpy(dst, pld->base, len1); /* 2) buffer start -> in */ dst += len1; - iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, (len - len1)); + iowrite16(PLD_ADDR_MASK(&src[0]), pld->address_buffer); + memcpy(dst, pld->base, (len - len1)); } - if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { + spin_unlock_irqrestore(&pld->pld_lock, flags); + if (pld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { mif_info("ipc read error!! in[%d], out[%d]\n", - get_rx_head(dpld, dev), - get_rx_tail(dpld, dev)); + get_rx_head(pld, dev), + get_rx_tail(pld, dev)); } } @@ -1016,190 +865,106 @@ static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, ret == 0 : no data ret > 0 : valid data */ -static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) +static int pld_ipc_recv_data_with_rxb(struct pld_link_device *pld, int dev) { - struct link_device *ld = &dpld->ld; - struct dpram_rxb *rxb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); + struct link_device *ld = &pld->ld; + struct mif_rxb *rxb; + u8 __iomem *src = get_rx_buff(pld, dev); + u32 qsize = get_rx_buff_size(pld, dev); u32 in; u32 out; u32 rcvd; struct mif_irq_map map; unsigned long int flags; - spin_lock_irqsave(&dpld->tx_rx_lock, flags); + spin_lock_irqsave(&pld->tx_rx_lock, flags); - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + rcvd = get_rxq_rcvd(pld, dev, qsize, &in, &out); if (rcvd <= 0) { - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); + set_dpram_map(pld, &map); mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } /* Allocate an rxb */ - rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); + rxb = rxbq_get_free_rxb(&pld->rxbq[dev]); if (!rxb) { mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", ld->name, get_dev_name(dev)); - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return -ENOMEM; } /* Read data from each DPRAM buffer */ - dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); + pld_ipc_read(pld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); /* Calculate and set new out */ out += rcvd; if (out >= qsize) out -= qsize; - set_rx_tail(dpld, dev, out); - - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); - return rcvd; -} - -/* - ret < 0 : error - ret == 0 : no data - ret > 0 : valid data -*/ -static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) -{ - struct link_device *ld = &dpld->ld; - struct io_device *iod = dpld->iod[dev]; - struct sk_buff *skb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); - u32 in; - u32 out; - u32 rcvd; - int rest; - u8 *frm; - u8 *dst; - unsigned int len; - unsigned int pad; - unsigned int tot; - struct mif_irq_map map; - - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); - if (rcvd <= 0) - return rcvd; - - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } - - rest = rcvd; - while (rest > 0) { - frm = src + out; - if (unlikely(!sipc5_start_valid(frm[0]))) { - mif_err("%s: ERR! %s invalid start 0x%02X\n", - ld->name, get_dev_name(dev), frm[0]); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } - - len = sipc5_get_frame_sz16(frm); - if (unlikely(len > rest)) { - mif_err("%s: ERR! %s len %d > rest %d\n", - ld->name, get_dev_name(dev), len, rest); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } - - pad = sipc5_calc_padding_size(len); - tot = len + pad; - - /* Allocate an skb */ - skb = dev_alloc_skb(tot); - if (!skb) { - mif_err("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); - return -ENOMEM; - } - - /* Read data from each DPRAM buffer */ - dst = skb_put(skb, tot); - dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); - skb_trim(skb, len); - iod->recv_skb(iod, ld, skb); - - /* Calculate and set new out */ - rest -= tot; - out += tot; - if (out >= qsize) - out -= qsize; - } - - set_rx_tail(dpld, dev, out); + set_rx_tail(pld, dev, out); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } -static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) +static void non_command_handler(struct pld_link_device *pld, u16 non_cmd) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i = 0; int ret = 0; - u16 tx_mask = 0; + u16 mask = 0; - if (!dpram_ipc_active(dpld)) + if (!ipc_active(pld)) return; /* Read data from DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (dpld->use_skb) - ret = dpram_ipc_recv_data_with_skb(dpld, i); - else - ret = dpram_ipc_recv_data_with_rxb(dpld, i); + for (i = 0; i < ld->max_ipc_dev; i++) { + ret = pld_ipc_recv_data_with_rxb(pld, i); if (ret < 0) - dpram_reset_rx_circ(dpld, i); + reset_rx_circ(pld, i); /* Check and process REQ_ACK (at this time, in == out) */ - if (non_cmd & get_mask_req_ack(dpld, i)) { + if (non_cmd & get_mask_req_ack(pld, i)) { mif_debug("%s: send %s_RES_ACK\n", ld->name, get_dev_name(i)); - tx_mask |= get_mask_res_ack(dpld, i); + mask |= get_mask_res_ack(pld, i); } } - if (!dpld->use_skb) { - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); - } + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&pld->rx_tsk); /* Try TX via DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (atomic_read(&dpld->res_required[i]) > 0) { - ret = dpram_try_ipc_tx(dpld, i); + for (i = 0; i < ld->max_ipc_dev; i++) { + if (atomic_read(&pld->res_required[i]) > 0) { + ret = pld_try_ipc_tx(pld, i); if (ret > 0) { - atomic_set(&dpld->res_required[i], 0); - tx_mask |= get_mask_send(dpld, i); + atomic_set(&pld->res_required[i], 0); + mask |= get_mask_send(pld, i); } else if (ret == -ENOSPC) { - tx_mask |= get_mask_req_ack(dpld, i); + mask |= get_mask_req_ack(pld, i); } } } - if (tx_mask) { - send_intr(dpld, INT_NON_CMD(tx_mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); + if (mask) { + send_intr(pld, INT_NON_CMD(mask)); + mif_debug("%s: send intr 0x%04X\n", ld->name, mask); } } -static void handle_cp_crash(struct dpram_link_device *dpld) +static void handle_cp_crash(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); skb_queue_purge(ld->skb_txq[i]); } @@ -1210,27 +975,27 @@ static void handle_cp_crash(struct dpram_link_device *dpld) iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_EXIT); - iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); if (iod) iodevs_for_each(iod->msd, iodev_netif_stop, 0); } static void handle_no_crash_ack(unsigned long arg) { - struct dpram_link_device *dpld = (struct dpram_link_device *)arg; - struct link_device *ld = &dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)arg; + struct link_device *ld = &pld->ld; mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name); - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); - handle_cp_crash(dpld); + handle_cp_crash(pld); } -static int trigger_force_cp_crash(struct dpram_link_device *dpld) +static int trigger_force_cp_crash(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; if (ld->mode == LINK_MODE_ULOAD) { mif_err("%s: CP crash is already in progress\n", ld->mc->name); @@ -1240,79 +1005,72 @@ static int trigger_force_cp_crash(struct dpram_link_device *dpld) ld->mode = LINK_MODE_ULOAD; mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); - dpram_wake_up(dpld); + send_intr(pld, INT_CMD(INT_CMD_CRASH_EXIT)); - send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); - - mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)dpld); + mif_add_timer(&pld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_crash_ack, (unsigned long)pld); return 0; } -static int dpram_init_ipc(struct dpram_link_device *dpld) +static int pld_init_ipc(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i; if (ld->mode == LINK_MODE_IPC && - get_magic(dpld) == DPRAM_MAGIC_CODE && - get_access(dpld) == 1) + get_magic(pld) == DPRAM_MAGIC_CODE && + get_access(pld) == 1) mif_info("%s: IPC already initialized\n", ld->name); /* Clear pointers in every circular queue */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - set_tx_head(dpld, i, 0); - set_tx_tail(dpld, i, 0); - set_rx_head(dpld, i, 0); - set_rx_tail(dpld, i, 0); + for (i = 0; i < ld->max_ipc_dev; i++) { + set_tx_head(pld, i, 0); + set_tx_tail(pld, i, 0); + set_rx_head(pld, i, 0); + set_rx_tail(pld, i, 0); } /* Initialize variables for efficient TX/RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->iod[i] = link_get_iod_with_format(ld, i); - dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - - if (dpld->iod[IPC_RAW]->recv_skb) - dpld->use_skb = true; + for (i = 0; i < ld->max_ipc_dev; i++) + pld->iod[i] = link_get_iod_with_format(ld, i); + pld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - for (i = 0; i < dpld->max_ipc_dev; i++) { - atomic_set(&dpld->res_required[i], 0); - skb_queue_purge(&dpld->skb_rxq[i]); - } + for (i = 0; i < ld->max_ipc_dev; i++) + atomic_set(&pld->res_required[i], 0); - spin_lock_init(&dpld->tx_rx_lock); + spin_lock_init(&pld->tx_rx_lock); /* Enable IPC */ - atomic_set(&dpld->accessing, 0); + atomic_set(&pld->accessing, 0); - set_magic(dpld, DPRAM_MAGIC_CODE); - set_access(dpld, 1); - if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + set_magic(pld, DPRAM_MAGIC_CODE); + set_access(pld, 1); + if (get_magic(pld) != DPRAM_MAGIC_CODE || get_access(pld) != 1) return -EACCES; ld->mode = LINK_MODE_IPC; - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + if (wake_lock_active(&pld->wlock)) + wake_unlock(&pld->wlock); return 0; } -static void cmd_req_active_handler(struct dpram_link_device *dpld) +static void cmd_req_active_handler(struct pld_link_device *pld) { - send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_intr(pld, INT_CMD(INT_CMD_RES_ACTIVE)); } -static void cmd_crash_reset_handler(struct dpram_link_device *dpld) +static void cmd_crash_reset_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod = NULL; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); @@ -1323,35 +1081,33 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) iod->modem_state_changed(iod, STATE_CRASH_RESET); } -static void cmd_crash_exit_handler(struct dpram_link_device *dpld) +static void cmd_crash_exit_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - dpram_wake_up(dpld); + del_timer(&pld->crash_ack_timer); - del_timer(&dpld->crash_ack_timer); + if (pld->ext_op && pld->ext_op->crash_log) + pld->ext_op->crash_log(pld); - if (dpld->ext_op && dpld->ext_op->crash_log) - dpld->ext_op->crash_log(dpld); - - handle_cp_crash(dpld); + handle_cp_crash(pld); } -static void cmd_phone_start_handler(struct dpram_link_device *dpld) +static void cmd_phone_start_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod = NULL; mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); - dpram_init_ipc(dpld); + pld_init_ipc(pld); iod = link_get_iod_with_format(ld, IPC_FMT); if (!iod) { @@ -1359,8 +1115,8 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) return; } - if (dpld->ext_op && dpld->ext_op->cp_start_handler) - dpld->ext_op->cp_start_handler(dpld); + if (pld->ext_op && pld->ext_op->cp_start_handler) + pld->ext_op->cp_start_handler(pld); if (ld->mc->phone_state != STATE_ONLINE) { mif_info("%s: phone_state: %d -> ONLINE\n", @@ -1369,32 +1125,32 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) } mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); + send_intr(pld, INT_CMD(INT_CMD_INIT_END)); } -static void command_handler(struct dpram_link_device *dpld, u16 cmd) +static void command_handler(struct pld_link_device *pld, u16 cmd) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; switch (INT_CMD_MASK(cmd)) { case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(dpld); + cmd_req_active_handler(pld); break; case INT_CMD_CRASH_RESET: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_reset_handler(dpld); + pld->init_status = PLD_INIT_STATE_NONE; + cmd_crash_reset_handler(pld); break; case INT_CMD_CRASH_EXIT: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_exit_handler(dpld); + pld->init_status = PLD_INIT_STATE_NONE; + cmd_crash_exit_handler(pld); break; case INT_CMD_PHONE_START: - dpld->dpram_init_status = DPRAM_INIT_STATE_READY; - cmd_phone_start_handler(dpld); - complete_all(&dpld->dpram_init_cmd); + pld->init_status = PLD_INIT_STATE_READY; + cmd_phone_start_handler(pld); + complete_all(&pld->dpram_init_cmd); break; case INT_CMD_NV_REBUILDING: @@ -1402,14 +1158,14 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&dpld->modem_pif_init_done); + complete_all(&pld->modem_pif_init_done); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_PWR_OFF: + case INT_CMD_NORMAL_POWER_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1424,123 +1180,46 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) } } -static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) +static irqreturn_t pld_irq_handler(int irq, void *data) { - struct link_device *ld = &dpld->ld; - u16 resp; - - switch (EXT_CMD_MASK(cmd)) { - case EXT_CMD_SET_SPEED_LOW: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); - send_intr(dpld, resp); - } - break; - - case EXT_CMD_SET_SPEED_MID: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_MID); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); - send_intr(dpld, resp); - } - break; - - case EXT_CMD_SET_SPEED_HIGH: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); - send_intr(dpld, resp); - } - break; - - default: - mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); - break; - } -} - -static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) -{ - struct link_device *ld = &dpld->ld; - - if (cmd & UDL_RESULT_FAIL) { - mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); - return; - } - - switch (UDL_CMD_MASK(cmd)) { - case UDL_CMD_RECV_READY: - mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); - send_intr(dpld, CMD_IMG_START_REQ); - break; - default: - complete_all(&dpld->udl_cmd_complete); - } -} - -static irqreturn_t dpram_irq_handler(int irq, void *data) -{ - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)data; + struct link_device *ld = (struct link_device *)&pld->ld; u16 int2ap = 0; if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - if (dpram_wake_up(dpld) < 0) { - log_dpram_status(dpld); - trigger_force_cp_crash(dpld); - return IRQ_HANDLED; - } - - int2ap = recv_intr(dpld); + int2ap = recv_intr(pld); if (unlikely(int2ap == INT_POWERSAFE_FAIL)) { mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); goto exit; } else if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { - if (dpld->ext_op && dpld->ext_op->dload_cmd_handler) { - dpld->ext_op->dload_cmd_handler(dpld, int2ap); + if (pld->ext_op && pld->ext_op->dload_cmd_handler) { + pld->ext_op->dload_cmd_handler(pld, int2ap); goto exit; } } - if (unlikely(EXT_UDL_CMD(int2ap))) { - if (likely(EXT_INT_VALID(int2ap))) { - if (UDL_CMD_VALID(int2ap)) - udl_command_handler(dpld, int2ap); - else if (EXT_CMD_VALID(int2ap)) - ext_command_handler(dpld, int2ap); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); - } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); - } + if (likely(INT_VALID(int2ap))) { + if (unlikely(INT_CMD_VALID(int2ap))) + command_handler(pld, int2ap); + else + non_command_handler(pld, int2ap); } else { - if (likely(INT_VALID(int2ap))) { - if (unlikely(INT_CMD_VALID(int2ap))) - command_handler(dpld, int2ap); - else - non_command_handler(dpld, int2ap); - } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); - } + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); } exit: - clear_intr(dpld); - dpram_allow_sleep(dpld); + clear_intr(pld); return IRQ_HANDLED; } -static void dpram_send_ipc(struct link_device *ld, int dev, +static void pld_send_ipc(struct link_device *ld, int dev, struct io_device *iod, struct sk_buff *skb) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; u16 mask; @@ -1551,46 +1230,31 @@ static void dpram_send_ipc(struct link_device *ld, int dev, ld->name, get_dev_name(dev), txq->qlen); } - if (dpram_wake_up(dpld) < 0) { - trigger_force_cp_crash(dpld); - return; - } - - if (!dpram_ipc_active(dpld)) + if (!ipc_active(pld)) goto exit; - if (atomic_read(&dpld->res_required[dev]) > 0) { + if (atomic_read(&pld->res_required[dev]) > 0) { mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); goto exit; } - ret = dpram_try_ipc_tx(dpld, dev); + ret = pld_try_ipc_tx(pld, dev); if (ret > 0) { - mask = get_mask_send(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); + mask = get_mask_send(pld, dev); + send_intr(pld, INT_NON_CMD(mask)); } else if (ret == -ENOSPC) { - mask = get_mask_req_ack(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); + mask = get_mask_req_ack(pld, dev); + send_intr(pld, INT_NON_CMD(mask)); mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); } else { - mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); + mif_info("%s: pld_try_ipc_tx fail (err %d)\n", ld->name, ret); } exit: - dpram_allow_sleep(dpld); -} - -static int dpram_download_bin(struct link_device *ld, struct sk_buff *skb) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - if (dpld->ext_op && dpld->ext_op->dload_bin) - return dpld->ext_op->dload_bin(dpld, skb); - else - return -ENODEV; + return; } -static int dpram_send(struct link_device *ld, struct io_device *iod, +static int pld_send(struct link_device *ld, struct io_device *iod, struct sk_buff *skb) { enum dev_format dev = iod->format; @@ -1601,16 +1265,13 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_send_ipc(ld, dev, iod, skb); + pld_send_ipc(ld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); } return len; - case IPC_BOOT: - return dpram_download_bin(ld, skb); - default: mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name); dev_kfree_skb_any(skb); @@ -1618,48 +1279,38 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static int pld_force_dump(struct link_device *ld, struct io_device *iod) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); - trigger_force_cp_crash(dpld); + struct pld_link_device *pld = to_pld_link_device(ld); + trigger_force_cp_crash(pld); return 0; } -static void dpram_dump_memory(struct link_device *ld, char *buff) +static int pld_dump_start(struct link_device *ld, struct io_device *iod) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *base = dpld->dpctl->dp_base; - u32 size = dpld->dpctl->dp_size; + struct pld_link_device *pld = to_pld_link_device(ld); - dpram_wake_up(dpld); - memcpy(buff, base, size); -} - -static int dpram_dump_start(struct link_device *ld, struct io_device *iod) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - if (dpld->ext_op && dpld->ext_op->dump_start) - return dpld->ext_op->dump_start(dpld); + if (pld->ext_op && pld->ext_op->dump_start) + return pld->ext_op->dump_start(pld); else return -ENODEV; } -static int dpram_dump_update(struct link_device *ld, struct io_device *iod, +static int pld_dump_update(struct link_device *ld, struct io_device *iod, unsigned long arg) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); - if (dpld->ext_op && dpld->ext_op->dump_update) - return dpld->ext_op->dump_update(dpld, (void *)arg); + if (pld->ext_op && pld->ext_op->dump_update) + return pld->ext_op->dump_update(pld, (void *)arg); else return -ENODEV; } -static int dpram_ioctl(struct link_device *ld, struct io_device *iod, +static int pld_ioctl(struct link_device *ld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); int err = 0; /* @@ -1669,11 +1320,11 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + return pld->init_status; default: - if (dpld->ext_ioctl) { - err = dpld->ext_ioctl(dpld, iod, cmd, arg); + if (pld->ext_ioctl) { + err = pld->ext_ioctl(pld, iod, cmd, arg); } else { mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); err = -EINVAL; @@ -1685,97 +1336,97 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, return err; } -static int dpram_table_init(struct dpram_link_device *dpld) +static int pld_table_init(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; u8 __iomem *dp_base; int i; - if (!dpld->dp_base) { - mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); + if (!pld->base) { + mif_info("%s: ERR! pld->base == NULL\n", ld->name); return -EINVAL; } - dp_base = dpld->dp_base; + dp_base = pld->base; /* Map for IPC */ - if (dpld->dpctl->ipc_map) { - memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, - sizeof(struct dpram_ipc_map)); + if (pld->dpram->ipc_map) { + memcpy(&pld->ipc_map, pld->dpram->ipc_map, + sizeof(struct pld_ipc_map)); } - dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp; - dpld->access_ap2cp = dpld->ipc_map.access_ap2cp; + pld->magic_ap2cp = pld->ipc_map.magic_ap2cp; + pld->access_ap2cp = pld->ipc_map.access_ap2cp; - dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap; - dpld->access_cp2ap = dpld->ipc_map.access_cp2ap; + pld->magic_cp2ap = pld->ipc_map.magic_cp2ap; + pld->access_cp2ap = pld->ipc_map.access_cp2ap; - dpld->address_buffer = dpld->ipc_map.address_buffer; + pld->address_buffer = pld->ipc_map.address_buffer; - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->dev[i] = &dpld->ipc_map.dev[i]; - dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; - dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + for (i = 0; i < ld->max_ipc_dev; i++) + pld->dev[i] = &pld->ipc_map.dev[i]; + pld->mbx2ap = pld->ipc_map.mbx_cp2ap; + pld->mbx2cp = pld->ipc_map.mbx_ap2cp; /* Map for booting */ - if (dpld->ext_op && dpld->ext_op->init_boot_map) { - dpld->ext_op->init_boot_map(dpld); + if (pld->ext_op && pld->ext_op->init_boot_map) { + pld->ext_op->init_boot_map(pld); } else { - dpld->bt_map.magic = (u32 *)(dp_base); - dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); - dpld->bt_map.size = dpld->dp_size - 8; + pld->bt_map.magic = (u32 *)(dp_base); + pld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); + pld->bt_map.space = pld->size - 8; } /* Map for download (FOTA, UDL, etc.) */ - if (dpld->ext_op && dpld->ext_op->init_dl_map) { - dpld->ext_op->init_dl_map(dpld); + if (pld->ext_op && pld->ext_op->init_dl_map) { + pld->ext_op->init_dl_map(pld); } else { - dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + pld->dl_map.magic = (u32 *)(dp_base); + pld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); } /* Map for upload mode */ - if (dpld->ext_op && dpld->ext_op->init_ul_map) { - dpld->ext_op->init_ul_map(dpld); + if (pld->ext_op && pld->ext_op->init_ul_map) { + pld->ext_op->init_ul_map(pld); } else { - dpld->ul_map.magic = (u32 *)(dp_base); - dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + pld->ul_map.magic = (u32 *)(dp_base); + pld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); } return 0; } -static void dpram_setup_common_op(struct dpram_link_device *dpld) -{ - dpld->clear_intr = clear_intr; - dpld->recv_intr = recv_intr; - dpld->send_intr = send_intr; - dpld->get_magic = get_magic; - dpld->set_magic = set_magic; - dpld->get_access = get_access; - dpld->set_access = set_access; - dpld->get_tx_head = get_tx_head; - dpld->get_tx_tail = get_tx_tail; - dpld->set_tx_head = set_tx_head; - dpld->set_tx_tail = set_tx_tail; - dpld->get_tx_buff = get_tx_buff; - dpld->get_tx_buff_size = get_tx_buff_size; - dpld->get_rx_head = get_rx_head; - dpld->get_rx_tail = get_rx_tail; - dpld->set_rx_head = set_rx_head; - dpld->set_rx_tail = set_rx_tail; - dpld->get_rx_buff = get_rx_buff; - dpld->get_rx_buff_size = get_rx_buff_size; - dpld->get_mask_req_ack = get_mask_req_ack; - dpld->get_mask_res_ack = get_mask_res_ack; - dpld->get_mask_send = get_mask_send; -} - -static int dpram_link_init(struct link_device *ld, struct io_device *iod) +static void pld_setup_common_op(struct pld_link_device *pld) +{ + pld->clear_intr = clear_intr; + pld->recv_intr = recv_intr; + pld->send_intr = send_intr; + pld->get_magic = get_magic; + pld->set_magic = set_magic; + pld->get_access = get_access; + pld->set_access = set_access; + pld->get_tx_head = get_tx_head; + pld->get_tx_tail = get_tx_tail; + pld->set_tx_head = set_tx_head; + pld->set_tx_tail = set_tx_tail; + pld->get_tx_buff = get_tx_buff; + pld->get_tx_buff_size = get_tx_buff_size; + pld->get_rx_head = get_rx_head; + pld->get_rx_tail = get_rx_tail; + pld->set_rx_head = set_rx_head; + pld->set_rx_tail = set_rx_tail; + pld->get_rx_buff = get_rx_buff; + pld->get_rx_buff_size = get_rx_buff_size; + pld->get_mask_req_ack = get_mask_req_ack; + pld->get_mask_res_ack = get_mask_res_ack; + pld->get_mask_send = get_mask_send; +} + +static int pld_link_init(struct link_device *ld, struct io_device *iod) { return 0; } -static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +static void pld_link_terminate(struct link_device *ld, struct io_device *iod) { return; } @@ -1783,11 +1434,11 @@ static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) struct link_device *pld_create_link_device(struct platform_device *pdev) { struct modem_data *mdm_data = NULL; - struct dpram_link_device *dpld = NULL; + struct pld_link_device *pld = NULL; struct link_device *ld = NULL; struct resource *res = NULL; resource_size_t res_size; - struct modemlink_dpram_control *dpctl = NULL; + struct modemlink_dpram_data *dpram = NULL; unsigned long task_data; int ret = 0; int i = 0; @@ -1803,19 +1454,19 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) mif_info("modem = %s\n", mdm_data->name); mif_info("link device = %s\n", mdm_data->link_name); - if (!mdm_data->dpram_ctl) { - mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); + if (!mdm_data->dpram) { + mif_info("ERR! no mdm_data->dpram\n"); goto err; } - dpctl = mdm_data->dpram_ctl; + dpram = mdm_data->dpram; /* Alloc DPRAM link device structure */ - dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); - if (!dpld) { - mif_info("ERR! kzalloc dpld fail\n"); + pld = kzalloc(sizeof(struct pld_link_device), GFP_KERNEL); + if (!pld) { + mif_info("ERR! kzalloc pld fail\n"); goto err; } - ld = &dpld->ld; + ld = &pld->ld; /* Retrieve modem data and DPRAM control data from the modem data */ ld->mdm_data = mdm_data; @@ -1823,30 +1474,30 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) ld->ipc_version = mdm_data->ipc_version; /* Retrieve the most basic data for IPC from the modem data */ - dpld->dpctl = dpctl; - dpld->dp_type = dpctl->dp_type; + pld->dpram = dpram; + pld->type = dpram->type; if (mdm_data->ipc_version < SIPC_VER_50) { - if (!dpctl->max_ipc_dev) { + if (!mdm_data->max_ipc_dev) { mif_info("ERR! no max_ipc_dev\n"); goto err; } - ld->aligned = dpctl->aligned; - dpld->max_ipc_dev = dpctl->max_ipc_dev; + ld->aligned = dpram->aligned; + ld->max_ipc_dev = mdm_data->max_ipc_dev; } else { ld->aligned = 1; - dpld->max_ipc_dev = MAX_SIPC5_DEV; + ld->max_ipc_dev = MAX_SIPC5_DEV; } /* Set attributes as a link device */ - ld->init_comm = dpram_link_init; - ld->terminate_comm = dpram_link_terminate; - ld->send = dpram_send; - ld->force_dump = dpram_force_dump; - ld->dump_start = dpram_dump_start; - ld->dump_update = dpram_dump_update; - ld->ioctl = dpram_ioctl; + ld->init_comm = pld_link_init; + ld->terminate_comm = pld_link_terminate; + ld->send = pld_send; + ld->force_dump = pld_force_dump; + ld->dump_start = pld_dump_start; + ld->dump_update = pld_dump_update; + ld->ioctl = pld_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1858,14 +1509,13 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; /* Set up function pointers */ - dpram_setup_common_op(dpld); - dpld->dpram_dump = dpram_dump_memory; - dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); - if (dpld->ext_op && dpld->ext_op->ioctl) - dpld->ext_ioctl = dpld->ext_op->ioctl; + pld_setup_common_op(pld); + pld->ext_op = pld_get_ext_op(mdm_data->modem_type); + if (pld->ext_op && pld->ext_op->ioctl) + pld->ext_ioctl = pld->ext_op->ioctl; /* Retrieve DPRAM resource */ - if (!dpctl->dp_base) { + if (!dpram->base) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { mif_info("%s: ERR! platform_get_resource fail\n", @@ -1874,59 +1524,54 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } res_size = resource_size(res); - dpctl->dp_base = ioremap_nocache(res->start, res_size); - dpctl->dp_size = res_size; + dpram->base = ioremap_nocache(res->start, res_size); + dpram->size = res_size; } - dpld->dp_base = dpctl->dp_base; - dpld->dp_size = dpctl->dp_size; + pld->base = dpram->base; + pld->size = dpram->size; - mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", - ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, - dpld->dp_size); + mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", + ld->name, pld->type, ld->aligned, (int)pld->base, pld->size); /* Initialize DPRAM map (physical map -> logical map) */ - ret = dpram_table_init(dpld); + ret = pld_table_init(pld); if (ret < 0) { - mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + mif_info("%s: ERR! pld_table_init fail (err %d)\n", ld->name, ret); goto err; } - spin_lock_init(&dpld->pld_lock); + spin_lock_init(&pld->pld_lock); /* Disable IPC */ - set_magic(dpld, 0); - set_access(dpld, 0); + set_magic(pld, 0); + set_access(pld, 0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + pld->init_status = PLD_INIT_STATE_NONE; /* Initialize locks, completions, and bottom halves */ - snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); - wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); - - init_completion(&dpld->dpram_init_cmd); - init_completion(&dpld->modem_pif_init_done); - init_completion(&dpld->udl_start_complete); - init_completion(&dpld->udl_cmd_complete); - init_completion(&dpld->dump_start_complete); - init_completion(&dpld->dump_recv_done); + snprintf(pld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); + wake_lock_init(&pld->wlock, WAKE_LOCK_SUSPEND, pld->wlock_name); - task_data = (unsigned long)dpld; - tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); + init_completion(&pld->dpram_init_cmd); + init_completion(&pld->modem_pif_init_done); + init_completion(&pld->udl_start_complete); + init_completion(&pld->udl_cmd_complete); + init_completion(&pld->crash_start_complete); + init_completion(&pld->crash_recv_done); - /* Prepare SKB queue head for RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - skb_queue_head_init(&dpld->skb_rxq[i]); + task_data = (unsigned long)pld; + tasklet_init(&pld->rx_tsk, pld_ipc_rx_task, task_data); /* Prepare RXB queue */ - qsize = DPRAM_MAX_RXBQ_SIZE; - for (i = 0; i < dpld->max_ipc_dev; i++) { - bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); - dpld->rxbq[i].size = qsize; - dpld->rxbq[i].in = 0; - dpld->rxbq[i].out = 0; - dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); - if (!dpld->rxbq[i].rxb) { + qsize = MAX_RXBQ_SIZE; + for (i = 0; i < ld->max_ipc_dev; i++) { + bsize = rxbq_get_page_size(get_rx_buff_size(pld, i)); + pld->rxbq[i].size = qsize; + pld->rxbq[i].in = 0; + pld->rxbq[i].out = 0; + pld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); + if (!pld->rxbq[i].rxb) { mif_info("%s: ERR! %s rxbq_create_pool fail\n", ld->name, get_dev_name(i)); goto err; @@ -1936,44 +1581,37 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } /* Prepare a multi-purpose miscellaneous buffer */ - dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); - if (!dpld->buff) { - mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + pld->buff = kzalloc(pld->size, GFP_KERNEL); + if (!pld->buff) { + mif_info("%s: ERR! kzalloc pld->buff fail\n", ld->name); goto err; } /* Retrieve DPRAM IRQ GPIO# */ - dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; + pld->gpio_ipc_int2ap = mdm_data->gpio_ipc_int2ap; /* Retrieve DPRAM IRQ# */ - if (!dpctl->dpram_irq) { - dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); - if (dpctl->dpram_irq < 0) { - mif_info("%s: ERR! platform_get_irq_byname fail\n", - ld->name); - goto err; - } - } - dpld->irq = dpctl->dpram_irq; + pld->irq = mdm_data->irq_ipc_int2ap; /* Retrieve DPRAM IRQ flags */ - if (!dpctl->dpram_irq_flags) - dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); - dpld->irq_flags = dpctl->dpram_irq_flags; + if (mdm_data->irqf_ipc_int2ap) + pld->irq_flags = mdm_data->irqf_ipc_int2ap; + else + pld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); /* Register DPRAM interrupt handler */ - snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); - ret = dpram_register_isr(dpld->irq, dpram_irq_handler, dpld->irq_flags, - dpld->irq_name, dpld); + snprintf(pld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); + ret = pld_register_isr(pld->irq, pld_irq_handler, pld->irq_flags, + pld->irq_name, pld); if (ret) goto err; return ld; err: - if (dpld) { - kfree(dpld->buff); - kfree(dpld); + if (pld) { + kfree(pld->buff); + kfree(pld); } return NULL; diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h index 2656110..2690faa 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.h +++ b/drivers/misc/modem_if/modem_link_device_pld.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,181 +11,134 @@ * GNU General Public License for more details. * */ -#ifndef __MODEM_LINK_DEVICE_DPRAM_H__ -#define __MODEM_LINK_DEVICE_DPRAM_H__ - -#include -#include -#include -#include -#include - -#include "modem_prj.h" - -#define DPRAM_MAGIC_CODE 0xAA - -/* interrupt masks.*/ -#define INT_MASK_VALID 0x0080 -#define INT_MASK_CMD 0x0040 -#define INT_VALID(x) ((x) & INT_MASK_VALID) -#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) -#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) -#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) - -#define EXT_UDL_MASK 0xF000 -#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) -#define EXT_INT_VALID_MASK 0x8000 -#define EXT_CMD_VALID_MASK 0x4000 -#define UDL_CMD_VALID_MASK 0x2000 -#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) -#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) -#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) -#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) - -#define EXT_CMD_MASK(x) ((x) & 0x0FFF) -#define EXT_CMD_SET_SPEED_LOW 0x0011 -#define EXT_CMD_SET_SPEED_MID 0x0012 -#define EXT_CMD_SET_SPEED_HIGH 0x0013 - -#define UDL_RESULT_SUCCESS 0x1 -#define UDL_RESULT_FAIL 0x2 - -#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) -#define UDL_CMD_RECV_READY 0x1 -#define UDL_CMD_DL_START_REQ 0x2 -#define UDL_CMD_DL_START_RESP 0x3 -#define UDL_CMD_IMAGE_SEND_REQ 0x4 -#define UDL_CMD_SEND_DONE_RESP 0x5 -#define UDL_CMD_SEND_DONE_REQ 0x6 -#define UDL_CMD_UPDATE_DONE 0x7 -#define UDL_CMD_STATUS_UPDATE 0x8 -#define UDL_CMD_IMAGE_SEND_RESP 0x9 -#define UDL_CMD_EFS_CLEAR_RESP 0xB -#define UDL_CMD_ALARM_BOOT_OK 0xC -#define UDL_CMD_ALARM_BOOT_FAIL 0xD - -#define CMD_IMG_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 -#define CMD_UL_RECV_RESP 0x9601 -#define CMD_UL_RECV_DONE_RESP 0x9801 - -/* special interrupt cmd indicating modem boot failure. */ -#define INT_POWERSAFE_FAIL 0xDEAD - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_REQ_ACK_F 0x0020 -#define INT_MASK_REQ_ACK_R 0x0010 -#define INT_MASK_RES_ACK_F 0x0008 -#define INT_MASK_RES_ACK_R 0x0004 -#define INT_MASK_SEND_F 0x0002 -#define INT_MASK_SEND_R 0x0001 - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_RES_ACK_SET \ - (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) - -#define INT_MASK_SEND_SET \ - (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) - -#define INT_CMD_MASK(x) ((x) & 0xF) -#define INT_CMD_INIT_START 0x1 -#define INT_CMD_INIT_END 0x2 -#define INT_CMD_REQ_ACTIVE 0x3 -#define INT_CMD_RES_ACTIVE 0x4 -#define INT_CMD_REQ_TIME_SYNC 0x5 -#define INT_CMD_CRASH_RESET 0x7 -#define INT_CMD_PHONE_START 0x8 -#define INT_CMD_ERR_DISPLAY 0x9 -#define INT_CMD_CRASH_EXIT 0x9 -#define INT_CMD_CP_DEEP_SLEEP 0xA -#define INT_CMD_NV_REBUILDING 0xB -#define INT_CMD_EMER_DOWN 0xC -#define INT_CMD_PIF_INIT_DONE 0xD -#define INT_CMD_SILENT_NV_REBUILDING 0xE -#define INT_CMD_NORMAL_PWR_OFF 0xF - -#define START_FLAG 0x7F -#define END_FLAG 0x7E - -#define DP_MAGIC_DMDL 0x4445444C -#define DP_MAGIC_UMDL 0x4445444D -#define DP_DPRAM_SIZE 0x4000 -#define DP_DEFAULT_WRITE_LEN 8168 -#define DP_DEFAULT_DUMP_LEN 16128 -#define DP_DUMP_HEADER_SIZE 7 - -#define UDL_TIMEOUT (50 * HZ) -#define UDL_SEND_TIMEOUT (200 * HZ) -#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) -#define DUMP_TIMEOUT (30 * HZ) -#define DUMP_START_TIMEOUT (100 * HZ) -#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ +#ifndef __MODEM_LINK_DEVICE_PLD_H__ +#define __MODEM_LINK_DEVICE_PLD_H__ -#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) +#include "modem_link_device_memory.h" -enum host_boot_mode { - HOST_BOOT_MODE_NORMAL, - HOST_BOOT_MODE_DUMP, -}; +#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) -enum dpram_init_status { - DPRAM_INIT_STATE_NONE, - DPRAM_INIT_STATE_READY, +enum pld_init_status { + PLD_INIT_STATE_NONE, + PLD_INIT_STATE_READY, }; -struct dpram_boot_img { - char *addr; - int size; - enum host_boot_mode mode; - unsigned req; - unsigned resp; -}; +#if 1 +#define MAX_RXBQ_SIZE 256 -#define MAX_PAYLOAD_SIZE 0x2000 -struct dpram_boot_frame { - unsigned req; /* AP->CP request */ - unsigned resp; /* response expected by AP */ - ssize_t len; /* data size in the buffer */ - unsigned offset; /* offset to write into DPRAM */ - char data[MAX_PAYLOAD_SIZE]; -}; +struct mif_rxb { + u8 *buff; + unsigned size; -/* buffer type for modem image */ -struct dpram_dump_arg { - char *buff; /* pointer to the buffer */ - int buff_size; /* buffer size */ - unsigned req; /* AP->CP request */ - unsigned resp; /* CP->AP response */ - bool cmd; /* AP->CP command */ + u8 *data; + unsigned len; }; -struct dpram_firmware { - char *firmware; +struct mif_rxb_queue { int size; - int is_delta; -}; -enum dpram_link_mode { - DPRAM_LINK_MODE_INVALID = 0, - DPRAM_LINK_MODE_IPC, - DPRAM_LINK_MODE_BOOT, - DPRAM_LINK_MODE_DLOAD, - DPRAM_LINK_MODE_ULOAD, + int in; + int out; + struct mif_rxb *rxb; }; -struct dpram_boot_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 __iomem *req; - u32 __iomem *resp; - u32 size; -}; +/* +** RXB (DPRAM RX buffer) functions +*/ +static inline struct mif_rxb *rxbq_create_pool(unsigned size, int count) +{ + struct mif_rxb *rxb; + u8 *buff; + int i; + + rxb = kzalloc(sizeof(struct mif_rxb) * count, GFP_KERNEL); + if (!rxb) { + mif_info("ERR! kzalloc rxb fail\n"); + return NULL; + } + + buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); + if (!buff) { + mif_info("ERR! kzalloc buff fail\n"); + kfree(rxb); + return NULL; + } + + for (i = 0; i < count; i++) { + rxb[i].buff = buff; + rxb[i].size = size; + buff += size; + } + + return rxb; +} + +static inline unsigned rxbq_get_page_size(unsigned len) +{ + return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; +} + +static inline bool rxbq_empty(struct mif_rxb_queue *rxbq) +{ + return (rxbq->in == rxbq->out) ? true : false; +} + +static inline int rxbq_free_size(struct mif_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline struct mif_rxb *rxbq_get_free_rxb(struct mif_rxb_queue *rxbq) +{ + struct mif_rxb *rxb = NULL; + + if (likely(rxbq_free_size(rxbq) > 0)) { + rxb = &rxbq->rxb[rxbq->in]; + rxbq->in++; + if (rxbq->in >= rxbq->size) + rxbq->in -= rxbq->size; + rxb->data = rxb->buff; + } + + return rxb; +} + +static inline int rxbq_size(struct mif_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline struct mif_rxb *rxbq_get_data_rxb(struct mif_rxb_queue *rxbq) +{ + struct mif_rxb *rxb = NULL; + + if (likely(!rxbq_empty(rxbq))) { + rxb = &rxbq->rxb[rxbq->out]; + rxbq->out++; + if (rxbq->out >= rxbq->size) + rxbq->out -= rxbq->size; + } + + return rxb; +} + +static inline u8 *rxb_put(struct mif_rxb *rxb, unsigned len) +{ + rxb->len = len; + return rxb->data; +} + +static inline void rxb_clear(struct mif_rxb *rxb) +{ + rxb->data = NULL; + rxb->len = 0; +} +#endif struct qc_dpram_boot_map { u8 __iomem *buff; @@ -195,39 +147,14 @@ struct qc_dpram_boot_map { u16 __iomem *count; }; -struct dpram_dload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct dpram_uload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct dpram_ota_header { - u8 start_index; - u16 nframes; - u16 curframe; - u16 len; - -} __packed; - -struct ul_header { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -} __packed; - -struct dpram_udl_param { +struct qc_dpram_udl_param { unsigned char *addr; unsigned int size; unsigned int count; unsigned int tag; }; -struct dpram_udl_check { +struct qc_dpram_udl_check { unsigned int total_size; unsigned int rest_size; unsigned int send_size; @@ -236,180 +163,38 @@ struct dpram_udl_check { unsigned int boot_complete; }; -#define DP_BOOT_BUFF_OFFSET 4 -#define DP_DLOAD_BUFF_OFFSET 4 -#define DP_ULOAD_BUFF_OFFSET 4 -#define DP_BOOT_REQ_OFFSET 0 -#define DP_BOOT_RESP_OFFSET 8 - -#define MAX_WQ_NAME_LENGTH 64 - -#define DPRAM_MAX_RXBQ_SIZE 256 - -struct dpram_rxb { - u8 *buff; - unsigned size; +struct pld_ext_op; - u8 *data; - unsigned len; -}; - -struct dpram_rxb_queue { - int size; - int in; - int out; - struct dpram_rxb *rxb; -}; - -/* - mbx_ap2cp + 0x0 - magic_code + - access_enable + - padding + - mbx_cp2ap + 0x1000 - magic_code + - access_enable + - padding + - fmt_tx_head + fmt_tx_tail + fmt_tx_buff + 0x2000 - raw_tx_head + raw_tx_tail + raw_tx_buff + - fmt_rx_head + fmt_rx_tail + fmt_rx_buff + 0x3000 - raw_rx_head + raw_rx_tail + raw_rx_buff + - = 2 + - 4094 + - 2 + - 4094 + - 2 + - 2 + - 2 + 2 + 1020 + - 2 + 2 + 3064 + - 2 + 2 + 1020 + - 2 + 2 + 3064 - */ - -#define DP_PLD_FMT_TX_BUFF_SZ 1024 -#define DP_PLD_RAW_TX_BUFF_SZ 3072 -#define DP_PLD_FMT_RX_BUFF_SZ 1024 -#define DP_PLD_RAW_RX_BUFF_SZ 3072 - -#define MAX_MSM_EDPRAM_IPC_DEV 2 /* FMT, RAW */ - -struct dpram_ipc_pld_map { - u16 mbx_ap2cp; - u16 magic_ap2cp; - u16 access_ap2cp; - u16 fmt_tx_head; - u16 raw_tx_head; - u16 fmt_rx_tail; - u16 raw_rx_tail; - u16 temp1; - u8 padding1[4080]; - - u16 mbx_cp2ap; - u16 magic_cp2ap; - u16 access_cp2ap; - u16 fmt_tx_tail; - u16 raw_tx_tail; - u16 fmt_rx_head; - u16 raw_rx_head; - u16 temp2; - u8 padding2[4080]; - - u8 fmt_tx_buff[DP_PLD_FMT_TX_BUFF_SZ]; - u8 raw_tx_buff[DP_PLD_RAW_TX_BUFF_SZ]; - u8 fmt_rx_buff[DP_PLD_RAW_TX_BUFF_SZ]; - u8 raw_rx_buff[DP_PLD_RAW_RX_BUFF_SZ]; - - u8 padding3[16384]; - - u16 address_buffer; -}; - -/* - magic_code + - access_enable + - fmt_tx_head + fmt_tx_tail + fmt_tx_buff + - raw_tx_head + raw_tx_tail + raw_tx_buff + - fmt_rx_head + fmt_rx_tail + fmt_rx_buff + - raw_rx_head + raw_rx_tail + raw_rx_buff + - mbx_cp2ap + - mbx_ap2cp - = 2 + - 2 + - 2 + 2 + 1336 + - 2 + 2 + 4564 + - 2 + 2 + 1336 + - 2 + 2 + 9124 + - 2 + - 2 - = 16384 -*/ -#define DP_16K_FMT_TX_BUFF_SZ 1336 -#define DP_16K_RAW_TX_BUFF_SZ 4564 -#define DP_16K_FMT_RX_BUFF_SZ 1336 -#define DP_16K_RAW_RX_BUFF_SZ 9124 - -struct dpram_ipc_16k_map { - u16 magic; - u16 access; - - u16 fmt_tx_head; - u16 fmt_tx_tail; - u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ]; - - u16 raw_tx_head; - u16 raw_tx_tail; - u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ]; - - u16 fmt_rx_head; - u16 fmt_rx_tail; - u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ]; - - u16 raw_rx_head; - u16 raw_rx_tail; - u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ]; - - u16 mbx_cp2ap; - u16 mbx_ap2cp; -}; - -#define DP_MAX_NAME_LEN 32 - -struct dpram_ext_op; - -struct dpram_link_device { +struct pld_link_device { struct link_device ld; - /* The mode of this DPRAM link device */ - enum dpram_link_mode mode; - /* DPRAM address and size */ - u8 __iomem *dp_base; /* DPRAM base virtual address */ - u32 dp_size; /* DPRAM size */ - enum dpram_type dp_type; /* DPRAM type */ + enum dpram_type type; /* DPRAM type */ + u8 __iomem *base; /* DPRAM base virtual address */ + u32 size; /* DPRAM size */ /* DPRAM IRQ GPIO# */ - unsigned gpio_dpram_int; + unsigned gpio_ipc_int2ap; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[DP_MAX_NAME_LEN]; + char irq_name[MIF_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - int max_ipc_dev; - struct modemlink_dpram_control *dpctl; + struct modemlink_dpram_data *dpram; /* Physical configuration -> logical configuration */ union { - struct dpram_boot_map bt_map; + struct memif_boot_map bt_map; struct qc_dpram_boot_map qc_bt_map; }; - struct dpram_dload_map dl_map; - struct dpram_uload_map ul_map; + struct memif_dload_map dl_map; + struct memif_uload_map ul_map; /* IPC device map */ - struct dpram_ipc_map ipc_map; + struct pld_ipc_map ipc_map; /* Pointers (aliases) to IPC device map */ u16 __iomem *magic_ap2cp; @@ -424,7 +209,7 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[DP_MAX_NAME_LEN]; + char wlock_name[MIF_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; @@ -432,19 +217,16 @@ struct dpram_link_device { struct completion modem_pif_init_done; /* For UDL */ - struct tasklet_struct ul_tsk; struct tasklet_struct dl_tsk; struct completion udl_start_complete; struct completion udl_cmd_complete; - struct dpram_udl_check udl_check; - struct dpram_udl_param udl_param; + struct qc_dpram_udl_check qc_udl_check; + struct qc_dpram_udl_param qc_udl_param; - /* For CP RAM dump */ + /* For CP crash dump */ struct timer_list crash_ack_timer; - struct completion dump_start_complete; - struct completion dump_recv_done; - struct timer_list dump_timer; - int dump_rcvd; /* Count of dump packets received */ + struct completion crash_start_complete; + struct completion crash_recv_done; /* For locking TX process */ spinlock_t tx_rx_lock; @@ -452,10 +234,8 @@ struct dpram_link_device { /* For efficient RX process */ struct tasklet_struct rx_tsk; - struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct mif_rxb_queue rxbq[MAX_IPC_DEV]; struct io_device *iod[MAX_IPC_DEV]; - bool use_skb; - struct sk_buff_head skb_rxq[MAX_IPC_DEV]; /* For retransmission after buffer full state */ atomic_t res_required[MAX_IPC_DEV]; @@ -466,67 +246,65 @@ struct dpram_link_device { /* Multi-purpose miscellaneous buffer */ u8 *buff; - /* DPRAM IPC initialization status */ - int dpram_init_status; + /* PLD IPC initialization status */ + int init_status; /* Alias to device-specific IOCTL function */ - int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + int (*ext_ioctl)(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg); - /* For DPRAM dump */ - void (*dpram_dump)(struct link_device *ld, char *buff); - /* Common operations for each DPRAM */ - void (*clear_intr)(struct dpram_link_device *dpld); - u16 (*recv_intr)(struct dpram_link_device *dpld); - void (*send_intr)(struct dpram_link_device *dpld, u16 mask); - u16 (*get_magic)(struct dpram_link_device *dpld); - void (*set_magic)(struct dpram_link_device *dpld, u16 value); - u16 (*get_access)(struct dpram_link_device *dpld); - void (*set_access)(struct dpram_link_device *dpld, u16 value); - u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); - void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); - void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); - u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); - u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); - u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); + void (*clear_intr)(struct pld_link_device *pld); + u16 (*recv_intr)(struct pld_link_device *pld); + void (*send_intr)(struct pld_link_device *pld, u16 mask); + u16 (*get_magic)(struct pld_link_device *pld); + void (*set_magic)(struct pld_link_device *pld, u16 value); + u16 (*get_access)(struct pld_link_device *pld); + void (*set_access)(struct pld_link_device *pld, u16 value); + u32 (*get_tx_head)(struct pld_link_device *pld, int id); + u32 (*get_tx_tail)(struct pld_link_device *pld, int id); + void (*set_tx_head)(struct pld_link_device *pld, int id, u32 head); + void (*set_tx_tail)(struct pld_link_device *pld, int id, u32 tail); + u8 *(*get_tx_buff)(struct pld_link_device *pld, int id); + u32 (*get_tx_buff_size)(struct pld_link_device *pld, int id); + u32 (*get_rx_head)(struct pld_link_device *pld, int id); + u32 (*get_rx_tail)(struct pld_link_device *pld, int id); + void (*set_rx_head)(struct pld_link_device *pld, int id, u32 head); + void (*set_rx_tail)(struct pld_link_device *pld, int id, u32 tail); + u8 *(*get_rx_buff)(struct pld_link_device *pld, int id); + u32 (*get_rx_buff_size)(struct pld_link_device *pld, int id); + u16 (*get_mask_req_ack)(struct pld_link_device *pld, int id); + u16 (*get_mask_res_ack)(struct pld_link_device *pld, int id); + u16 (*get_mask_send)(struct pld_link_device *pld, int id); /* Extended operations for various modems */ - struct dpram_ext_op *ext_op; + struct pld_ext_op *ext_op; }; /* converts from struct link_device* to struct xxx_link_device* */ -#define to_dpram_link_device(linkdev) \ - container_of(linkdev, struct dpram_link_device, ld) +#define to_pld_link_device(linkdev) \ + container_of(linkdev, struct pld_link_device, ld) -struct dpram_ext_op { +struct pld_ext_op { int exist; - void (*init_boot_map)(struct dpram_link_device *dpld); - void (*init_dl_map)(struct dpram_link_device *dpld); - void (*init_ul_map)(struct dpram_link_device *dpld); + void (*init_boot_map)(struct pld_link_device *pld); + void (*init_dl_map)(struct pld_link_device *pld); + void (*init_ul_map)(struct pld_link_device *pld); - int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb); - void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd); + void (*dload_cmd_handler)(struct pld_link_device *pld, u16 cmd); - void (*cp_start_handler)(struct dpram_link_device *dpld); + void (*cp_start_handler)(struct pld_link_device *pld); - void (*crash_log)(struct dpram_link_device *dpld); - int (*dump_start)(struct dpram_link_device *dpld); - int (*dump_update)(struct dpram_link_device *dpld, void *arg); + void (*crash_log)(struct pld_link_device *pld); + int (*dump_start)(struct pld_link_device *pld); + int (*dump_update)(struct pld_link_device *pld, void *arg); - int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + int (*ioctl)(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg); + + void (*clear_intr)(struct pld_link_device *pld); }; -struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +struct pld_ext_op *pld_get_ext_op(enum modem_t modem); #endif diff --git a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c index ae6578c..6c259f4 100644 --- a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c @@ -25,8 +25,8 @@ #include #include #include -#include +#include #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" @@ -41,56 +41,55 @@ enum qc_dload_tag { static void qc_dload_task(unsigned long data); -static void qc_init_boot_map(struct dpram_link_device *dpld) +static void qc_init_boot_map(struct pld_link_device *pld) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct modemlink_dpram_data *dpram = pld->dpram; - bt_map->buff = dpld->dev[0]->txq.buff; - bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); - bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); - bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); + qbt_map->buff = pld->dev[0]->txq.buff; + qbt_map->frame_size = (u16 *)(pld->base + dpram->boot_size_offset); + qbt_map->tag = (u16 *)(pld->base + dpram->boot_tag_offset); + qbt_map->count = (u16 *)(pld->base + dpram->boot_count_offset); - tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); + tasklet_init(&pld->dl_tsk, qc_dload_task, (unsigned long)pld); } -static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload) +static void qc_dload_map(struct pld_link_device *pld, u8 is_upload) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct modemlink_dpram_data *dpram = pld->dpram; unsigned int upload_offset = 0; if (is_upload == 1) { upload_offset = 0x1000; - bt_map->buff = dpld->dev[0]->rxq.buff; + qbt_map->buff = pld->dev[0]->rxq.buff; } else { upload_offset = 0; - bt_map->buff = dpld->dev[0]->txq.buff; + qbt_map->buff = pld->dev[0]->txq.buff; } - bt_map->frame_size = (u16 *)(dpld->dp_base + - dpctl->boot_size_offset + upload_offset); - bt_map->tag = (u16 *)(dpld->dp_base + - dpctl->boot_tag_offset + upload_offset); - bt_map->count = (u16 *)(dpld->dp_base + - dpctl->boot_count_offset + upload_offset); - + qbt_map->frame_size = (u16 *)(pld->base + + dpram->boot_size_offset + upload_offset); + qbt_map->tag = (u16 *)(pld->base + + dpram->boot_tag_offset + upload_offset); + qbt_map->count = (u16 *)(pld->base + + dpram->boot_count_offset + upload_offset); } -static int qc_prepare_download(struct dpram_link_device *dpld) +static int qc_prepare_download(struct pld_link_device *pld) { int retval = 0; int count = 0; - qc_dload_map(dpld, 0); + qc_dload_map(pld, 0); while (1) { - if (dpld->udl_check.copy_start) { - dpld->udl_check.copy_start = 0; + if (pld->qc_udl_check.copy_start) { + pld->qc_udl_check.copy_start = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 1000) { @@ -102,42 +101,42 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void _qc_do_download(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static void _qc_do_download(struct pld_link_device *pld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; - if (param->size <= dpld->dpctl->max_boot_frame_size) { - iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), - dpld->address_buffer); - memcpy(dpld->dp_base, param->addr, param->size); + if (param->size <= pld->dpram->max_boot_frame_size) { + iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), + pld->address_buffer); + memcpy(pld->base, param->addr, param->size); - iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), - dpld->address_buffer); - iowrite16(param->size, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), + pld->address_buffer); + iowrite16(param->size, pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), - dpld->address_buffer); - iowrite16(param->tag, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), + pld->address_buffer); + iowrite16(param->tag, pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), - dpld->address_buffer); - iowrite16(param->count, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), + pld->address_buffer); + iowrite16(param->count, pld->base); - dpld->send_intr(dpld, 0xDB12); + pld->send_intr(pld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int _qc_download(struct dpram_link_device *dpld, void *arg, +static int _qc_download(struct pld_link_device *pld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -153,24 +152,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, memset(img, 0, param.size); memcpy(img, param.addr, param.size); - dpld->udl_check.total_size = param.size; - dpld->udl_check.rest_size = param.size; - dpld->udl_check.send_size = 0; - dpld->udl_check.copy_complete = 0; + pld->qc_udl_check.total_size = param.size; + pld->qc_udl_check.rest_size = param.size; + pld->qc_udl_check.send_size = 0; + pld->qc_udl_check.copy_complete = 0; - dpld->udl_param.addr = img; - dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + pld->qc_udl_param.addr = img; + pld->qc_udl_param.size = pld->dpram->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - dpld->udl_param.count = 1; + pld->qc_udl_param.count = 1; else - dpld->udl_param.count = param.count; - dpld->udl_param.tag = tag; + pld->qc_udl_param.count = param.count; + pld->qc_udl_param.tag = tag; - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) + pld->qc_udl_param.size = pld->qc_udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(dpld, &dpld->udl_param); + _qc_do_download(pld, &pld->qc_udl_param); /* Wait for completion */ @@ -180,13 +179,13 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (dpld->udl_check.copy_complete) { - dpld->udl_check.copy_complete = 0; + if (pld->qc_udl_check.copy_complete) { + pld->qc_udl_check.copy_complete = 0; retval = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > cnt_limit) { @@ -201,53 +200,53 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, return retval; } -static int qc_download_bin(struct dpram_link_device *dpld, void *arg) +static int qc_download_bin(struct pld_link_device *pld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + return _qc_download(pld, arg, QC_DLOAD_TAG_BIN); } -static int qc_download_nv(struct dpram_link_device *dpld, void *arg) +static int qc_download_nv(struct pld_link_device *pld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); + return _qc_download(pld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct pld_link_device *pld = (struct pld_link_device *)data; - dpld->udl_check.send_size += dpld->udl_param.size; - dpld->udl_check.rest_size -= dpld->udl_param.size; + pld->qc_udl_check.send_size += pld->qc_udl_param.size; + pld->qc_udl_check.rest_size -= pld->qc_udl_param.size; - dpld->udl_param.addr += dpld->udl_param.size; + pld->qc_udl_param.addr += pld->qc_udl_param.size; - if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { - dpld->udl_check.copy_complete = 1; - dpld->udl_param.tag = 0; + if (pld->qc_udl_check.send_size >= pld->qc_udl_check.total_size) { + pld->qc_udl_check.copy_complete = 1; + pld->qc_udl_param.tag = 0; return; } - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) + pld->qc_udl_param.size = pld->qc_udl_check.rest_size; - dpld->udl_param.count += 1; + pld->qc_udl_param.count += 1; - _qc_do_download(dpld, &dpld->udl_param); + _qc_do_download(pld, &pld->qc_udl_param); } -static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) +static void qc_dload_cmd_handler(struct pld_link_device *pld, u16 cmd) { switch (cmd) { case 0x1234: - dpld->udl_check.copy_start = 1; + pld->qc_udl_check.copy_start = 1; break; case 0xDBAB: - tasklet_schedule(&dpld->dl_tsk); + tasklet_schedule(&pld->dl_tsk); break; case 0xABCD: - mif_info("[%s] booting Start\n", dpld->ld.name); - dpld->udl_check.boot_complete = 1; + mif_info("[%s] booting Start\n", pld->ld.name); + pld->qc_udl_check.boot_complete = 1; break; default: @@ -255,22 +254,22 @@ static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) } } -static int qc_boot_start(struct dpram_link_device *dpld) +static int qc_boot_start(struct pld_link_device *pld) { u16 mask = 0; int count = 0; /* Send interrupt -> '0x4567' */ mask = 0x4567; - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); while (1) { - if (dpld->udl_check.boot_complete) { - dpld->udl_check.boot_complete = 0; + if (pld->qc_udl_check.boot_complete) { + pld->qc_udl_check.boot_complete = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -282,17 +281,17 @@ static int qc_boot_start(struct dpram_link_device *dpld) return 0; } -static int qc_boot_post_process(struct dpram_link_device *dpld) +static int qc_boot_post_process(struct pld_link_device *pld) { int count = 0; while (1) { - if (dpld->boot_start_complete) { - dpld->boot_start_complete = 0; + if (pld->boot_start_complete) { + pld->boot_start_complete = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -304,7 +303,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) return 0; } -static void qc_start_handler(struct dpram_link_device *dpld) +static void qc_start_handler(struct pld_link_device *pld) { /* * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT | @@ -312,38 +311,38 @@ static void qc_start_handler(struct dpram_link_device *dpld) */ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002); - dpld->boot_start_complete = 1; + pld->boot_start_complete = 1; /* Send INIT_END code to CP */ mif_info("send 0x%04X (INIT_END)\n", mask); - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); } -static void qc_crash_log(struct dpram_link_device *dpld) +static void qc_crash_log(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = dpld->get_rx_buff(dpld, IPC_FMT); + data = pld->get_rx_buff(pld, IPC_FMT); memcpy(buf, data, (sizeof(buf) - 1)); mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); mif_info("PHONE ERR MSG\t| %s\n", buf); } -static int _qc_data_upload(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static int _qc_data_upload(struct pld_link_device *pld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { - intval = dpld->recv_intr(dpld); + if (!gpio_get_value(pld->gpio_ipc_int2ap)) { + intval = pld->recv_intr(pld); if (intval == 0xDBAB) { break; } else { @@ -352,7 +351,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - msleep(20); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -361,43 +360,43 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), - dpld->address_buffer); - param->size = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), + pld->address_buffer); + param->size = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), - dpld->address_buffer); - param->tag = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), + pld->address_buffer); + param->tag = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), - dpld->address_buffer); - param->count = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), + pld->address_buffer); + param->count = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), - dpld->address_buffer); - memcpy(param->addr, dpld->dp_base, param->size); + iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), + pld->address_buffer); + memcpy(param->addr, pld->base, param->size); - dpld->send_intr(dpld, 0xDB12); + pld->send_intr(pld, 0xDB12); return retval; } -static int qc_uload_step1(struct dpram_link_device *dpld) +static int qc_uload_step1(struct pld_link_device *pld) { int retval = 0; int count = 0; u16 intval = 0; u16 mask = 0; - qc_dload_map(dpld, 1); + qc_dload_map(pld, 1); mif_info("+---------------------------------------------+\n"); mif_info("| UPLOAD PHONE SDRAM |\n"); mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { - intval = dpld->recv_intr(dpld); + if (!gpio_get_value(pld->gpio_ipc_int2ap)) { + intval = pld->recv_intr(pld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { break; @@ -407,11 +406,11 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - msleep(20); + usleep_range(1000, 2000); count++; if (count > 200) { - intval = dpld->recv_intr(dpld); + intval = pld->recv_intr(pld); mif_info("count %d, intr 0x%04x\n", count, intval); if (intval == 0x1234) break; @@ -420,15 +419,15 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } mask = 0xDEAD; - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); return retval; } -static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) +static int qc_uload_step2(struct pld_link_device *pld, void *arg) { int retval = 0; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -436,7 +435,7 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) return -1; } - retval = _qc_data_upload(dpld, ¶m); + retval = _qc_data_upload(pld, ¶m); if (retval < 0) { mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); return -1; @@ -446,7 +445,7 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) mif_info("param->count = %d\n", param.count); if (param.tag == 4) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("param->tag = %d\n", param.tag); } @@ -459,57 +458,57 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) return retval; } -static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, +static int qc_ioctl(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: - err = qc_prepare_download(dpld); + err = qc_prepare_download(pld); if (err < 0) mif_info("%s: ERR! prepare_download fail\n", ld->name); break; case IOCTL_DPRAM_PHONEIMG_LOAD: - err = qc_download_bin(dpld, (void *)arg); + err = qc_download_bin(pld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_bin fail\n", ld->name); break; case IOCTL_DPRAM_NVDATA_LOAD: - err = qc_download_nv(dpld, (void *)arg); + err = qc_download_nv(pld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_nv fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_BOOTSTART: - err = qc_boot_start(dpld); + err = qc_boot_start(pld); if (err < 0) { mif_info("%s: ERR! boot_start fail\n", ld->name); break; } - err = qc_boot_post_process(dpld); + err = qc_boot_post_process(pld); if (err < 0) mif_info("%s: ERR! boot_post_process fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_UPLOAD_STEP1: - disable_irq_nosync(dpld->irq); - err = qc_uload_step1(dpld); + disable_irq_nosync(pld->irq); + err = qc_uload_step1(pld); if (err < 0) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("%s: ERR! upload_step1 fail\n", ld->name); } break; case IOCTL_DPRAM_PHONE_UPLOAD_STEP2: - err = qc_uload_step2(dpld, (void *)arg); + err = qc_uload_step2(pld, (void *)arg); if (err < 0) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("%s: ERR! upload_step2 fail\n", ld->name); } break; @@ -524,7 +523,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, } #endif -static struct dpram_ext_op ext_op_set[] = { +static struct pld_ext_op ext_op_set[] = { #if defined(CONFIG_CDMA_MODEM_MDM6600) [QC_MDM6600] = { .exist = 1, @@ -547,7 +546,7 @@ static struct dpram_ext_op ext_op_set[] = { #endif }; -struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) +struct pld_ext_op *pld_get_ext_op(enum modem_t modem) { if (ext_op_set[modem].exist) return &ext_op_set[modem]; diff --git a/drivers/misc/modem_if/modem_link_device_shmem.h b/drivers/misc/modem_if/modem_link_device_shmem.h new file mode 100644 index 0000000..1f33c2a --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_shmem.h @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 __MODEM_LINK_DEVICE_SHMEM_H__ +#define __MODEM_LINK_DEVICE_SHMEM_H__ + +#include "modem_utils.h" +#include "modem_link_device_memory.h" + +#define SHM_BOOT_MAGIC 0x424F4F54 +#define SHM_DUMP_MAGIC 0x44554D50 +#define SHM_IPC_MAGIC 0xAA +#define SHM_PM_MAGIC 0x5F + +#define SHM_4M_RESERVED_SZ 4056 +#define SHM_4M_FMT_TX_BUFF_SZ 4096 +#define SHM_4M_FMT_RX_BUFF_SZ 4096 +#define SHM_4M_RAW_TX_BUFF_SZ 2084864 +#define SHM_4M_RAW_RX_BUFF_SZ 2097152 + +struct shmem_4mb_phys_map { + u32 magic; + u32 access; + + u32 fmt_tx_head; + u32 fmt_tx_tail; + + u32 fmt_rx_head; + u32 fmt_rx_tail; + + u32 raw_tx_head; + u32 raw_tx_tail; + + u32 raw_rx_head; + u32 raw_rx_tail; + + u8 reserved[SHM_4M_RESERVED_SZ]; + + u8 fmt_tx_buff[SHM_4M_FMT_TX_BUFF_SZ]; + u8 fmt_rx_buff[SHM_4M_FMT_RX_BUFF_SZ]; + + u8 raw_tx_buff[SHM_4M_RAW_TX_BUFF_SZ]; + u8 raw_rx_buff[SHM_4M_RAW_RX_BUFF_SZ]; +} __packed; + +struct shmem_circ { + u32 __iomem *head; + u32 __iomem *tail; + u8 __iomem *buff; + u32 size; +}; + +struct shmem_ipc_device { + char name[16]; + int id; + + struct shmem_circ txq; + struct shmem_circ rxq; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; + + int req_ack_rcvd; +}; + +struct shmem_ipc_map { + u32 __iomem *magic; + u32 __iomem *access; + + struct shmem_ipc_device dev[MAX_SIPC5_DEV]; + + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; +}; + +struct shmem_link_device { + struct link_device ld; + + enum shmem_type type; + + /* SHMEM (SHARED MEMORY) address and size */ + u32 start; /* physical "start" address of SHMEM */ + u32 size; /* size of SHMEM */ + u8 __iomem *base; /* virtual address of the "start" */ + + /* SHMEM GPIO & IRQ */ + unsigned gpio_pda_active; + + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* IPC device map */ + struct shmem_ipc_map ipc_map; + + /* Pointers (aliases) to IPC device map */ + u32 __iomem *magic; + u32 __iomem *access; + struct shmem_ipc_device *dev[MAX_SIPC5_DEV]; + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; + + /* Wakelock for SHMEM device */ + struct wake_lock wlock; + char wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock ap_wlock; + char ap_wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock cp_wlock; + char cp_wlock_name[MIF_MAX_NAME_LEN]; + + /* for UDL */ + struct completion udl_cmpl; + struct std_dload_info dl_info; + + /* for CP crash dump */ + bool forced_cp_crash; + struct timer_list crash_ack_timer; + + /* for locking TX process */ + spinlock_t tx_lock[MAX_SIPC5_DEV]; + + /* for retransmission under SHMEM flow control after TXQ full state */ + atomic_t res_required[MAX_SIPC5_DEV]; + struct completion req_ack_cmpl[MAX_SIPC5_DEV]; + + /* for efficient RX process */ + struct tasklet_struct rx_tsk; + struct delayed_work ipc_rx_dwork; + struct delayed_work udl_rx_dwork; + struct io_device *iod[MAX_SIPC5_DEV]; + + /* for logging SHMEM status */ + struct mem_status_queue stat_list; + + /* for logging SHMEM dump */ + struct trace_data_queue trace_list; +#ifdef DEBUG_MODEM_IF + struct delayed_work dump_dwork; + char dump_path[MIF_MAX_PATH_LEN]; +#endif + + /* to hold/release "cp_wakeup" for PM (power-management) */ + struct delayed_work cp_sleep_dwork; + struct delayed_work link_off_dwork; + atomic_t ref_cnt; + spinlock_t pm_lock; +}; + +/* converts from struct link_device* to struct xxx_link_device* */ +#define to_shmem_link_device(linkdev) \ + container_of(linkdev, struct shmem_link_device, ld) + +#if 1 +#endif + +/** + * get_magic + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "magic code" field. + */ +static inline u32 get_magic(struct shmem_link_device *shmd) +{ + return ioread32(shmd->magic); +} + +/** + * get_access + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "access enable" field. + */ +static inline u32 get_access(struct shmem_link_device *shmd) +{ + return ioread32(shmd->access); +} + +/** + * set_magic + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "magic code" field + */ +static inline void set_magic(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->magic); +} + +/** + * set_access + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "access enable" field + */ +static inline void set_access(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->access); +} + +/** + * get_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.head); +} + +/** + * get_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (out) pointer in a TX queue. + * + * It is useless for an AP to read a tail pointer in a TX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_txq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.tail); +} + +/** + * get_txq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.buff; +} + +/** + * get_txq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.size; +} + +/** + * get_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in an RX queue. + * + * It is useless for an AP to read a head pointer in an RX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_rxq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.head); +} + +/** + * get_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.tail); +} + +/** + * get_rxq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.buff; +} + +/** + * get_rxq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.size; +} + +/** + * set_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->txq.head); +} + +/** + * set_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->txq.tail); +} + +/** + * set_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->rxq.head); +} + +/** + * set_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->rxq.tail); +} + +/** + * get_mask_req_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_req_ack; +} + +/** + * get_mask_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_res_ack; +} + +/** + * get_mask_send + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_send; +} + +#ifndef CONFIG_LINK_DEVICE_C2C +/** + * read_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the AP-to-CP interrupt register. + */ +static inline u16 read_int2cp(struct shmem_link_device *shmd) +{ + if (shmd->mbx2cp) + return ioread16(shmd->mbx2cp); + else + return 0; +} +#endif + +/** + * reset_txq_circ + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties a TXQ by resetting the head (in) pointer with the value in the tail + * (out) pointer. + */ +static inline void reset_txq_circ(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_txq_head(shmd, dev); + u32 tail = get_txq_tail(shmd, dev); + + mif_err("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); + + set_txq_head(shmd, dev, tail); +} + +/** + * reset_rxq_circ + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties an RXQ by resetting the tail (out) pointer with the value in the head + * (in) pointer. + */ +static inline void reset_rxq_circ(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_rxq_head(shmd, dev); + u32 tail = get_rxq_tail(shmd, dev); + + mif_err("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); + + set_rxq_tail(shmd, dev, head); +} + +/** + * ipc_active + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns whether or not IPC via the shmem_link_device instance is possible. + */ +static bool ipc_active(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + u32 magic = get_magic(shmd); + u32 access = get_access(shmd); + + /* Check link mode */ + if (unlikely(ld->mode != LINK_MODE_IPC)) { + mif_err("%s: ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); + return false; + } + + /* Check "magic code" and "access enable" values */ + if (unlikely(magic != SHM_IPC_MAGIC || access != 1)) { + mif_err("%s: ERR! magic:0x%X access:%d\n", + ld->name, CALLER, magic, access); + return false; + } + + return true; +} + +/** + * get_rxq_rcvd + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a RXQ, size of the buffer, in & out + * pointer values, size of received data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_rxq_rcvd(struct shmem_link_device *shmd, int dev, + struct mem_status *mst, struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + + circ->buff = get_rxq_buff(shmd, dev); + circ->qsize = get_rxq_buff_size(shmd, dev); + circ->in = mst->head[dev][RX]; + circ->out = mst->tail[dev][RX]; + circ->size = circ_get_usage(circ->qsize, circ->in, circ->out); + + if (circ_valid(circ->qsize, circ->in, circ->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out, circ->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out); + return -EIO; + } +} + +/** + * get_txq_space + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of free space} into the 'circ' instance. + * + * Returns the size of free space in the buffer or an error code. + */ +static int get_txq_space(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int space; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + space = circ_get_space(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = space; + + return space; +} + +/** + * get_txq_saved + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of stored data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_txq_saved(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int saved; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + saved = circ_get_usage(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u saved:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, saved); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "saved:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + saved, cnt); + if (cnt >= MAX_RETRY_CNT) { + saved = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = saved; + + return saved; +} + +/** + * clear_shmem_map + * @shmd: pointer to an instance of shmem_link_device structure + * + * Clears all pointers in every circular queue. + */ +static void clear_shmem_map(struct shmem_link_device *shmd) +{ + set_txq_head(shmd, IPC_FMT, 0); + set_txq_tail(shmd, IPC_FMT, 0); + set_rxq_head(shmd, IPC_FMT, 0); + set_rxq_tail(shmd, IPC_FMT, 0); + + set_txq_head(shmd, IPC_RAW, 0); + set_txq_tail(shmd, IPC_RAW, 0); + set_rxq_head(shmd, IPC_RAW, 0); + set_rxq_tail(shmd, IPC_RAW, 0); +} + +/** + * reset_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Reset SHMEM with IPC map. + */ +static void reset_shmem_ipc(struct shmem_link_device *shmd) +{ + set_access(shmd, 0); + + clear_shmem_map(shmd); + + atomic_set(&shmd->res_required[IPC_FMT], 0); + atomic_set(&shmd->res_required[IPC_RAW], 0); + + atomic_set(&shmd->ref_cnt, 0); + + set_magic(shmd, SHM_IPC_MAGIC); + set_access(shmd, 1); +} + +/** + * init_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Initializes IPC via SHMEM. + */ +static int init_shmem_ipc(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode == LINK_MODE_IPC && + get_magic(shmd) == SHM_IPC_MAGIC && + get_access(shmd) == 1) { + mif_err("%s: IPC already initialized\n", ld->name); + return 0; + } + + /* Initialize variables for efficient TX/RX processing */ + shmd->iod[IPC_FMT] = link_get_iod_with_format(ld, IPC_FMT); + shmd->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + + reset_shmem_ipc(shmd); + + if (get_magic(shmd) != SHM_IPC_MAGIC || get_access(shmd) != 1) + return -EACCES; + + return 0; +} + +#endif + diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c index c4715e0..5a2d4b0 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.c +++ b/drivers/misc/modem_if/modem_link_device_spi.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -66,8 +67,7 @@ static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld) if (!spild->boot_done) return result; - if (!wake_lock_active(&spild->spi_wake_lock) - && spild->send_modem_spi != 1) { + if (!wake_lock_active(&spild->spi_wake_lock)) { wake_lock(&spild->spi_wake_lock); pr_debug("[SPI] [%s](%d) spi_wakelock locked . spild->spi_state[%d]\n", __func__, __LINE__, (int)spild->spi_state); @@ -311,7 +311,7 @@ static void spi_prepare_tx_packet(void) ld = &p_spild->ld; - for (i = 0; i < p_spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { while ((skb = skb_dequeue(ld->skb_txq[i]))) { ret = spi_buff_write(p_spild, i, skb->data, skb->len); if (!ret) { @@ -331,7 +331,7 @@ static void spi_start_data_send(void) ld = &p_spild->ld; - for (i = 0; i < p_spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { if (skb_queue_len(ld->skb_txq[i]) > 0) spi_send_work(SPI_WORK_SEND, SPI_WORK); } @@ -345,6 +345,14 @@ static void spi_tx_work(void) char *spi_sync_buf; spild = p_spild; + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + + if (iod->mc->phone_state == STATE_CRASH_EXIT) + return; /* check SUB SRDY, SRDY state */ if (gpio_get_value(spild->gpio_ipc_sub_srdy) == @@ -420,8 +428,6 @@ static void spi_tx_work(void) pr_err("[SPI] spi_dev_send fail\n"); /* add cp reset when spi sync fail */ - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (iod) iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -553,6 +559,15 @@ static void spi_rx_work(void) if (!spild) pr_err("[LNK/E] <%s> dpld == NULL\n", __func__); + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + + if (iod->mc->phone_state == STATE_CRASH_EXIT) + return; + if (!wake_lock_active(&spild->spi_wake_lock) || gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW || get_console_suspended() || @@ -606,7 +621,7 @@ static void spi_rx_work(void) /* parsing SPI packet */ if (spi_buff_read(spild) > 0) { /* call function for send data to IPC, RAW, RFS */ - for (i = 0; i < spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { iod = spild->iod[i]; while ((skb = skb_dequeue(&spild->skb_rxq[i])) != NULL) { @@ -623,8 +638,6 @@ static void spi_rx_work(void) "spi sync failed"); /* add cp reset when spi sync fail */ - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (iod) iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -639,6 +652,34 @@ static void spi_rx_work(void) spi_start_data_send(); } + +static int spi_init_ipc(struct spi_link_device *spild) +{ + struct link_device *ld = &spild->ld; + + int i; + + /* Make aliases to each IO device */ + for (i = 0; i < MAX_DEV_FORMAT; i++) + spild->iod[i] = link_get_iod_with_format(ld, i); + + spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; + + /* List up the IO devices connected to each IPC channel */ + for (i = 0; i < MAX_DEV_FORMAT; i++) { + if (spild->iod[i]) + pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", + __func__, ld->name, i, spild->iod[i]->name); + else + pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", + __func__, ld->name, i); + } + + ld->mode = LINK_MODE_IPC; + + return 0; +} + unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len) { unsigned int i; @@ -1239,10 +1280,19 @@ err3: static void spi_send_modem_bin(struct work_struct *send_modem_w) { + struct spi_link_device *spild; + struct io_device *iod; int retval; struct image_buf img; unsigned long tick1, tick2 = 0; + spild = p_spild; + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + tick1 = jiffies_to_msecs(jiffies); retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img); @@ -1285,16 +1335,25 @@ static void spi_send_modem_bin(struct work_struct *send_modem_w) tick2 = jiffies_to_msecs(jiffies); pr_info("Downloading takes %lu msec\n", (tick2-tick1)); - complete_all(&p_spild->ril_init); + spi_init_ipc(p_spild); + sprd_boot_done = 1; p_spild->ril_send_cnt = 0; + p_spild->spi_state = SPI_STATE_IDLE; + if (iod) + iod->modem_state_changed(iod, + STATE_ONLINE); + return; err: + if (iod) + iod->modem_state_changed(iod, + STATE_OFFLINE); return; } -static inline int _request_mem(struct ipc_spi *od) +static inline int _request_mem(struct spi_v_buff *od) { if (!p_spild->p_virtual_buff) { od->mmio = vmalloc(od->size); @@ -1320,7 +1379,7 @@ void spi_tx_timer_callback(unsigned long param) { if (p_spild->spi_state == SPI_STATE_TX_WAIT) { p_spild->spi_timer_tx_state = SPI_STATE_TIME_OVER; - pr_err("[SPI] spi_tx_timer_callback -timer expires\n"); + pr_debug("[SPI] spi_tx_timer_callback -timer expires\n"); } } @@ -1328,7 +1387,7 @@ void spi_rx_timer_callback(unsigned long param) { if (p_spild->spi_state == SPI_STATE_RX_WAIT) { p_spild->spi_timer_rx_state = SPI_STATE_TIME_OVER; - pr_err("[SPI] spi_rx_timer_callback -timer expires\n"); + pr_debug("[SPI] spi_rx_timer_callback -timer expires\n"); } } @@ -1381,96 +1440,37 @@ static void spi_work(struct work_struct *work) } } -static int spi_init_ipc(struct spi_link_device *spild) +static int link_pm_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) { - struct link_device *ld = &spild->ld; - - int i; - - /* Make aliases to each IO device */ - for (i = 0; i < MAX_DEV_FORMAT; i++) - spild->iod[i] = link_get_iod_with_format(ld, i); - - spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; - - /* List up the IO devices connected to each IPC channel */ - for (i = 0; i < MAX_DEV_FORMAT; i++) { - if (spild->iod[i]) - pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", - __func__, ld->name, i, spild->iod[i]->name); - else - pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", - __func__, ld->name, i); - } - - ld->mode = LINK_MODE_IPC; - - return 0; -} - -static int spi_thread(void *data) -{ - struct spi_link_device *spild = (struct spi_link_device *)data; + struct io_device *iod; + struct link_pm_data *pm_data = + container_of(this, struct link_pm_data, pm_notifier); - if (lpcharge == 1) { - pr_err("[LPM MODE] spi_thread_exit!\n"); - return 0; + iod = link_get_iod_with_format(&pm_data->spild->ld, IPC_FMT); + if (!iod) { + pr_err("no iodevice for modem control\n"); + return NOTIFY_BAD; } - daemonize("spi_thread"); - - pr_info("[%s] spi_thread start.\n", __func__); - p_spild->boot_done = 1; - - wait_for_completion(&p_spild->ril_init); - - pr_info("[%s] ril_init completed.\n", __func__); + if (!gpio_get_value(iod->mc->gpio_phone_active)) + return NOTIFY_DONE; - pr_info("<%s> wait 2 sec... srdy : %d\n", - __func__, gpio_get_value(spild->gpio_ipc_srdy)); - msleep_interruptible(1700); + switch (event) { + case PM_SUSPEND_PREPARE: + /* set TD PDA Active High if previous state was LPA */ + mif_info("TD PDA active low to LPA suspend spot\n"); + gpio_set_value(iod->mc->gpio_pda_active, 0); - while (gpio_get_value(spild->gpio_ipc_srdy)) - ; - pr_info("(%s) cp booting... Done.\n", __func__); - - spi_init_ipc(spild); - - pr_info("[spi_thread] Start IPC Communication. SRDY : %d\n", - gpio_get_value(spild->gpio_ipc_srdy)); - - /* CP booting is already completed, just set submrdy to high */ - if (gpio_get_value(spild->gpio_ipc_sub_srdy) == SPI_GPIOLEVEL_HIGH) { - gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); - pr_err("[spi_thread] CP booting is already completed\n"); + return NOTIFY_OK; + case PM_POST_SUSPEND: + /* LPA to Kernel suspend and User Freezing task fail resume, + restore to LPA GPIO states. */ + mif_info("TD PDA active High to LPA GPIO state\n"); + gpio_set_value(iod->mc->gpio_pda_active, 1); + return NOTIFY_OK; } - /* CP booting is not completed. - set submrdy to high and wait until subsrdy is high */ - else { - pr_err("[spi_thread] CP booting is not completed. wait...\n"); - - gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); - do { - msleep_interruptible(5); - } while (gpio_get_value(spild->gpio_ipc_sub_srdy) == - SPI_GPIOLEVEL_LOW); - - pr_err("[spi_thread] CP booting is done...\n"); - } - - if (p_spild->spi_is_restart) - msleep_interruptible(100); - else - msleep_interruptible(30); - - gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); - - pr_info("(%s) spi sync done.\n", __func__); - - spild->spi_state = SPI_STATE_IDLE; - p_spild->spi_is_restart = 0; - - return 0; + return NOTIFY_DONE; } static int spi_probe(struct spi_device *spi) @@ -1512,8 +1512,7 @@ static struct spi_driver spi_driver = { static int spi_link_init(void) { int ret; - struct ipc_spi *od; - struct task_struct *th; + struct spi_v_buff *od; struct link_device *ld = &p_spild->ld; p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy; @@ -1522,7 +1521,7 @@ static int spi_link_init(void) p_spild->gpio_ipc_mrdy, p_spild->gpio_modem_bin_srdy, gpio_get_value(p_spild->gpio_ipc_srdy)); - od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL); + od = kzalloc(sizeof(struct spi_v_buff), GFP_KERNEL); if (!od) { pr_err("(%d) failed to allocate device\n", __LINE__); ret = -ENOMEM; @@ -1537,7 +1536,6 @@ static int spi_link_init(void) if (ret) goto err; - init_completion(&p_spild->ril_init); sema_init(&p_spild->srdy_sem, 0); INIT_WORK(&p_spild->send_modem_w, @@ -1563,12 +1561,7 @@ static int spi_link_init(void) if (ret) goto err; - th = kthread_create(spi_thread, (void *)p_spild, "spi_thread"); - if (IS_ERR(th)) { - pr_err("kernel_thread() failed\n"); - goto err; - } - wake_up_process(th); + p_spild->boot_done = 1; pr_info("[%s] Done\n", __func__); return 0; @@ -1591,7 +1584,6 @@ void spi_set_restart(void) gpio_set_value(p_spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); p_spild->spi_state = SPI_STATE_END; - p_spild->spi_is_restart = 1; /* Flush SPI work queue */ flush_workqueue(p_spild->spi_wq); @@ -1635,6 +1627,35 @@ exit: } EXPORT_SYMBOL(spi_thread_restart); +static int spi_link_pm_init(struct spi_link_device *spild, + struct platform_device *pdev) +{ + struct modem_data *pdata = + (struct modem_data *)pdev->dev.platform_data; + struct modemlink_pm_data *pm_pdata; + struct link_pm_data *pm_data = + kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); + + if (!pdata || !pdata->link_pm_data) { + mif_err("platform data is NULL\n"); + return -EINVAL; + } + pm_pdata = pdata->link_pm_data; + + if (!pm_data) { + mif_err("link_pm_data is NULL\n"); + return -ENOMEM; + } + + pm_data->spild = spild; + spild->link_pm_data = pm_data; + + pm_data->pm_notifier.notifier_call = link_pm_notifier_event; + register_pm_notifier(&pm_data->pm_notifier); + + return 0; +} + struct link_device *spi_create_link_device(struct platform_device *pdev) { struct spi_link_device *spild = NULL; @@ -1689,9 +1710,9 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) } spild->spi_state = SPI_STATE_END; - spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */ + ld->max_ipc_dev = (IPC_RFS + 1); /* FMT, RAW, RFS */ - for (i = 0; i < spild->max_ipc_dev; i++) + for (i = 0; i < ld->max_ipc_dev; i++) skb_queue_head_init(&spild->skb_rxq[i]); /* Prepare a clean buffer for SPI access */ @@ -1738,6 +1759,11 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) goto err; } + /* create link pm device */ + ret = spi_link_pm_init(spild, pdev); + if (ret) + goto err; + /* Create SPI device */ ret = spi_link_init(); if (ret) diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h index 210d815..a8f9cd5 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.h +++ b/drivers/misc/modem_if/modem_link_device_spi.h @@ -135,20 +135,24 @@ struct spi_data_packet_header { unsigned long more:1; }; +struct link_pm_data { + struct miscdevice miscdev; + struct spi_link_device *spild; + + struct notifier_block pm_notifier; +}; + struct spi_link_device { struct link_device ld; - /* Link to SPI control functions dependent on each platform */ - int max_ipc_dev; - /* Wakelock for SPI device */ struct wake_lock spi_wake_lock; + /* Workqueue for modem bin transfers */ struct workqueue_struct *ipc_spi_wq; /* SPI state */ int spi_state; - int spi_is_restart; /* SPI Timer state */ int spi_timer_tx_state; @@ -182,11 +186,13 @@ struct spi_link_device { struct io_device *iod[MAX_DEV_FORMAT]; struct sk_buff_head skb_rxq[MAX_DEV_FORMAT]; + /* LINK PM DEVICE DATA */ + struct link_pm_data *link_pm_data; + /* Multi-purpose miscellaneous buffer */ u8 *buff; u8 *sync_buff; - struct completion ril_init; struct semaphore srdy_sem; int send_modem_spi; @@ -202,6 +208,12 @@ struct spi_link_device { #define to_spi_link_device(linkdev) \ container_of(linkdev, struct spi_link_device, ld) +struct spi_v_buff { + unsigned long base; + unsigned long size; + void __iomem *mmio; +}; + extern unsigned int lpcharge; extern int get_console_suspended(void); static void spi_work(struct work_struct *work); diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c index 14aee9f..0fd779c 100644 --- a/drivers/misc/modem_if/modem_link_device_usb.c +++ b/drivers/misc/modem_if/modem_link_device_usb.c @@ -13,7 +13,7 @@ * */ -/* #define DEBUG */ +#define DEBUG #include #include @@ -297,10 +297,15 @@ static void usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state) { struct io_device *iod; + struct io_device *bootd; iod = link_get_iod_with_format(&usb_ld->ld, IPC_FMT); if (iod) iod->modem_state_changed(iod, state); + + bootd = usb_ld->ld.mc->bootd; + if (bootd) + bootd->modem_state_changed(bootd, state); } static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld, @@ -624,7 +629,12 @@ static void if_usb_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work); usb_put_dev(usbdev); usb_ld->usbdev = NULL; - pm_runtime_forbid(pm_data->root_hub); + if (!has_hub(usb_ld)) { + if (pm_data->root_hub) + pm_runtime_forbid(pm_data->root_hub); + schedule_delayed_work(&usb_ld->wait_enumeration, + WAIT_ENUMURATION_TIMEOUT_JIFFIES); + } } } diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c index 75ad970..c63f08e 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.c +++ b/drivers/misc/modem_if/modem_link_pm_usb.c @@ -12,7 +12,7 @@ * */ -/* #define DEBUG */ +#define DEBUG #include #include @@ -27,12 +27,15 @@ #include "modem_link_pm_usb.h" +int during_hub_resume; + static inline void start_hub_work(struct link_pm_data *pm_data, int delay) { if (pm_data->hub_work_running == false) { pm_data->hub_work_running = true; wake_lock(&pm_data->hub_lock); mif_debug("link_pm_hub_work is started\n"); + during_hub_resume = 1; } schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay)); @@ -81,12 +84,13 @@ void link_pm_preactive(struct link_pm_data *pm_data) static void link_pm_hub_work(struct work_struct *work) { - int err; + int err, cnt; struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_hub.work); if (pm_data->hub_status == HUB_STATE_ACTIVE) { end_hub_work(pm_data); + during_hub_resume = 0; return; } @@ -111,7 +115,16 @@ static void link_pm_hub_work(struct work_struct *work) /* skip 1st time before first probe */ if (pm_data->root_hub) pm_runtime_get_sync(pm_data->root_hub); - err = pm_data->port_enable(2, 1); + + for (cnt=0;cnt<5;cnt++) { + err = pm_data->port_enable(2, 1); + if (err >= 0) { + mif_err("hub on success\n"); + break; + } + mif_err("hub on fail %d th\n", cnt); + msleep(100); + } if (err < 0) { mif_err("hub on fail err=%d\n", err); err = pm_data->port_enable(2, 0); @@ -132,6 +145,8 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); + + mif_err("USB Hub resume fail !!!\n"); end_hub_work(pm_data); } else { mif_info("hub resumming: %d\n", @@ -164,9 +179,6 @@ static int link_pm_hub_standby(void *args) /* this function is atomic. * make force disconnect in workqueue.. */ - if (pm_data->usb_ld->if_usb_connected) - schedule_work(&usb_ld->disconnect_work); - return err; } @@ -210,6 +222,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, sizeof(int))) return -EFAULT; gpio_set_value(pm_data->gpio_link_active, value); + mif_info("> H-ACT %d\n", value); break; case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); @@ -233,7 +246,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, case IOCTL_LINK_PORT_OFF: err = link_pm_hub_standby(pm_data); if (err < 0) { - mif_err("usb3503 active fail\n"); + mif_err("usb3503 standby fail\n"); goto exit; } pm_data->hub_init_lock = 1; @@ -363,6 +376,8 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->miscdev.name = "link_pm"; pm_data->miscdev.fops = &link_pm_fops; + during_hub_resume = 0; + err = misc_register(&pm_data->miscdev); if (err < 0) { mif_err("fail to register pm device(%d)\n", err); diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c index 2617be8..1686043 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_cbp7.1.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -31,7 +29,6 @@ #define PIF_TIMEOUT (180 * HZ) #define DPRAM_INIT_TIMEOUT (30 * HZ) - static irqreturn_t phone_active_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; @@ -163,13 +160,13 @@ static int cbp72_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - struct dpram_link_device *dpld = to_dpram_link_device(ld); + mif_debug("\n"); /* Wait here until the PHONE is up. * Waiting as the this called from IOCTL->UM thread */ mif_info("Waiting for INT_CMD_PHONE_START\n"); - ret = wait_for_completion_interruptible_timeout( - &dpld->dpram_init_cmd, DPRAM_INIT_TIMEOUT); + ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, + DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ mif_err("Timeout!!! (PHONE_START was not arrived.)\n"); @@ -177,8 +174,8 @@ static int cbp72_boot_off(struct modem_ctl *mc) } mif_info("Waiting for INT_CMD_PIF_INIT_DONE\n"); - ret = wait_for_completion_interruptible_timeout( - &dpld->modem_pif_init_done, PIF_TIMEOUT); + ret = wait_for_completion_interruptible_timeout(&ld->pif_cmpl, + PIF_TIMEOUT); if (!ret) { mif_err("Timeout!!! (PIF_INIT_DONE was not arrived.)\n"); return -ENXIO; @@ -199,10 +196,6 @@ static int cbp72_force_crash_exit(struct modem_ctl *mc) /* Make DUMP start */ ld->force_dump(ld, mc->bootd); - msleep_interruptible(1000); - - mc->bootd->modem_state_changed(mc->bootd, STATE_CRASH_EXIT); - return 0; } @@ -221,16 +214,15 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - struct platform_device *pdev = NULL; - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_off = pdata->gpio_cp_off; - mc->gpio_reset_req_n = pdata->gpio_reset_req_n; - mc->gpio_cp_reset = pdata->gpio_cp_reset; - mc->gpio_pda_active = pdata->gpio_pda_active; - mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; - mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_reset_req_n = pdata->gpio_reset_req_n; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset; if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { @@ -245,10 +237,9 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cbp72_get_ops(mc); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; if (!mc->irq_phone_active) { - mif_err("get irq fail\n"); + mif_err("get irq_phone_active fail\n"); return -1; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp82.c b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c new file mode 100644 index 0000000..ee90232 --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem_prj.h" +#include "modem_utils.h" + +#define DPRAM_INIT_TIMEOUT (30 * HZ) +#define PIF_TIMEOUT (180 * HZ) + +static irqreturn_t phone_active_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int old_state = mc->phone_state; + int new_state = mc->phone_state; + + mif_info("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(old_state), cp_on, cp_reset, cp_active); + + if (cp_reset && cp_active) { + if (mc->phone_state == STATE_BOOTING) { + new_state = STATE_ONLINE; + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + } else if (cp_reset && !cp_active) { + if (mc->phone_state == STATE_ONLINE) { + new_state = STATE_CRASH_EXIT; + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + } else { + new_state = STATE_OFFLINE; + if (mc->bootd && mc->bootd->modem_state_changed) + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + + if (new_state != old_state) { + mif_err("%s: phone_state changed (%s -> %s\n)", + mc->name, get_cp_state_str(old_state), + get_cp_state_str(new_state)); + } + + return IRQ_HANDLED; +} + +static int cbp82_on(struct modem_ctl *mc) +{ + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_off = gpio_get_value(mc->gpio_cp_off); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("+++\n"); + + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + /* prevent sleep during bootloader downloading */ + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_cp_off, 1); + gpio_set_value(mc->gpio_cp_reset, 0); + + msleep(500); + + cp_on = gpio_get_value(mc->gpio_cp_on); + cp_off = gpio_get_value(mc->gpio_cp_off); + cp_reset = gpio_get_value(mc->gpio_cp_reset); + cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + gpio_set_value(mc->gpio_cp_off, 0); + gpio_set_value(mc->gpio_cp_on, 1); + + msleep(100); + + gpio_set_value(mc->gpio_cp_reset, 1); + + msleep(300); + + cp_on = gpio_get_value(mc->gpio_cp_on); + cp_off = gpio_get_value(mc->gpio_cp_off); + cp_reset = gpio_get_value(mc->gpio_cp_reset); + cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + if (mc->gpio_pda_active) + gpio_set_value(mc->gpio_pda_active, 1); + + if (mc->bootd) + mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); + else + mif_err("no mc->bootd\n"); + + mif_err("---\n"); + return 0; +} + +static int cbp82_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_err("+++\n"); + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_cp_off, 1); + + mc->bootd->modem_state_changed(mc->bootd, STATE_OFFLINE); + ld->mode = LINK_MODE_OFFLINE; + + mif_err("---\n"); + return 0; +} + +static int cbp82_reset(struct modem_ctl *mc) +{ + int ret = 0; + + mif_debug("cbp82_reset()\n"); + + ret = cbp82_off(mc); + if (ret) + return -ENXIO; + + msleep(100); + + ret = cbp82_on(mc); + if (ret) + return -ENXIO; + + return 0; +} + +static int cbp82_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_info("+++\n"); + + ld->mode = LINK_MODE_BOOT; + + mif_info("---\n"); + return 0; +} + +static int cbp82_boot_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + int ret; + mif_err("+++\n"); + + /* Wait here until the PHONE is up. + * Waiting as the this called from IOCTL->UM thread */ + mif_err("Waiting for PHONE_START\n"); + ret = wait_for_completion_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); + if (!ret) { + /* ret == 0 on timeout */ + mif_err("T-I-M-E-O-U-T (PHONE_START)\n"); + cbp82_off(mc); + ret = -EIO; + goto exit; + } + mif_err("recv PHONE_START\n"); + + mif_err("Waiting for PIF_INIT_DONE\n"); + ret = wait_for_completion_timeout(&ld->pif_cmpl, PIF_TIMEOUT); + if (!ret) { + /* ret == 0 on timeout */ + mif_err("T-I-M-E-O-U-T (PIF_INIT_DONE)!!!\n"); + cbp82_off(mc); + ret = -EIO; + goto exit; + } + mif_err("recv PIF_INIT_DONE\n"); + + mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE); + ret = 0; + +exit: + wake_unlock(&mc->mc_wake_lock); + mif_err("---\n"); + return ret; +} + +static int cbp82_force_crash_exit(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + + mif_err("device = %s\n", mc->bootd->name); + + /* Make DUMP start */ + ld->force_dump(ld, mc->bootd); + + return 0; +} + +static void cbp82_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = cbp82_on; + mc->ops.modem_off = cbp82_off; + mc->ops.modem_reset = cbp82_reset; + mc->ops.modem_boot_on = cbp82_boot_on; + mc->ops.modem_boot_off = cbp82_boot_off; + mc->ops.modem_force_crash_exit = cbp82_force_crash_exit; +} + +int cbp82_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + int irq = 0; + unsigned long flag = 0; + mif_err("+++\n"); + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_phone_active = pdata->gpio_phone_active; + + if (!mc->gpio_cp_on || !mc->gpio_cp_off || !mc->gpio_cp_reset + || !mc->gpio_phone_active) { + mif_err("no GPIO data\n"); + mif_err("---\n"); + return -ENXIO; + } + + mc->gpio_pda_active = pdata->gpio_pda_active; + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_off, 1); + gpio_set_value(mc->gpio_cp_on, 0); + + cbp82_get_ops(mc); + + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp82_wake_lock"); + + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("get irq fail\n"); + mif_err("---\n"); + return -1; + } + mif_info("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); + + irq = mc->irq_phone_active; + flag = IRQF_NO_SUSPEND | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + ret = request_irq(irq, phone_active_handler, flag, "cdma_active", mc); + if (ret) { + mif_err("request_irq fail (%d)\n", ret); + mif_err("---\n"); + return ret; + } + + ret = enable_irq_wake(irq); + if (ret) + mif_err("enable_irq_wake fail (%d)\n", ret); + + mif_err("---\n"); + return 0; +} + diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c index fe8ce69..df18c83 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_cmc221.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -13,15 +11,15 @@ * GNU General Public License for more details. * */ -#include +#include #include #include #include #include #include - #include + #include "modem_prj.h" #include "modem_link_device_usb.h" #include "modem_link_device_dpram.h" @@ -77,7 +75,7 @@ static irqreturn_t phone_active_handler(int irq, void *arg) static irqreturn_t dynamic_switching_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; - int txpath = gpio_get_value(mc->gpio_dynamic_switching); + int txpath = gpio_get_value(mc->gpio_link_switch); bool enumerated = usb_is_enumerated(mc->msd); mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated); @@ -94,6 +92,52 @@ static irqreturn_t dynamic_switching_handler(int irq, void *arg) return IRQ_HANDLED; } +#ifdef CONFIG_EXYNOS4_CPUFREQ /* Set cpu clock to 800MHz for high TP */ +static void cmc221_cpufreq_lock(struct work_struct *work) +{ + struct modem_ctl *mc; + + mc = container_of(work, struct modem_ctl, work_cpu_lock.work); + if (mc->mdm_data->link_pm_data->freq_lock) { + mif_debug("Call freq lock func.\n"); + mc->mdm_data->link_pm_data->freq_lock(mc->dev); + + cancel_delayed_work(&mc->work_cpu_unlock); + schedule_delayed_work(&mc->work_cpu_unlock, + msecs_to_jiffies(5000)); + } +} + +static void cmc221_cpufreq_unlock(struct work_struct *work) +{ + struct modem_ctl *mc; + int tp_level; + + mc = container_of(work, struct modem_ctl, work_cpu_unlock.work); + tp_level = gpio_get_value(mc->gpio_cpufreq_lock); + + mif_debug("TP Level is (%d)\n", tp_level); + if (tp_level) { + mif_debug("maintain cpufreq lock !!!\n"); + schedule_delayed_work(&mc->work_cpu_unlock, + msecs_to_jiffies(5000)); + } else { + if (mc->mdm_data->link_pm_data->freq_unlock) { + mif_debug("Call freq unlock func.\n"); + mc->mdm_data->link_pm_data->freq_unlock(mc->dev); + } + } +} + +static irqreturn_t cpufreq_lock_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + + schedule_delayed_work(&mc->work_cpu_lock, 0); + return IRQ_HANDLED; +} +#endif + static int cmc221_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); @@ -204,11 +248,10 @@ static int cmc221_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - struct dpram_link_device *dpld = to_dpram_link_device(ld); mif_err("%s\n", mc->name); - ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd, + ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ @@ -249,22 +292,23 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - struct platform_device *pdev = NULL; - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_reset = pdata->gpio_cp_reset; mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_pda_active = pdata->gpio_pda_active; #if 0 /*TODO: check the GPIO map*/ - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup; - mc->gpio_host_active = pdata->gpio_host_active; - mc->gpio_host_wakeup = pdata->gpio_host_wakeup; + mc->gpio_host_active = pdata->gpio_host_active; + mc->gpio_host_wakeup = pdata->gpio_host_wakeup; #endif - mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching; + mc->gpio_link_switch = pdata->gpio_link_switch; mc->need_switch_to_usb = false; - +#ifdef CONFIG_EXYNOS4_CPUFREQ + mc->gpio_cpufreq_lock = pdata->gpio_cpufreq_lock; +#endif if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { mif_err("%s: ERR! no GPIO data\n", mc->name); return -ENXIO; @@ -276,10 +320,9 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cmc221_get_ops(mc); dev_set_drvdata(mc->dev, mc); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; if (!mc->irq_phone_active) { - mif_err("%s: ERR! get cp_active_irq fail\n", mc->name); + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); return -1; } mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active); @@ -301,8 +344,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } flag = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND; - if (mc->gpio_dynamic_switching) { - irq = gpio_to_irq(mc->gpio_dynamic_switching); + if (mc->gpio_link_switch) { + irq = gpio_to_irq(mc->gpio_link_switch); mif_err("%s: DYNAMIC_SWITCH IRQ# = %d\n", mc->name, irq); ret = request_irq(irq, dynamic_switching_handler, flag, "dynamic_switching", mc); @@ -313,5 +356,23 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } } +#ifdef CONFIG_EXYNOS4_CPUFREQ + INIT_DELAYED_WORK(&mc->work_cpu_lock, cmc221_cpufreq_lock); + INIT_DELAYED_WORK(&mc->work_cpu_unlock, cmc221_cpufreq_unlock); + + flag = IRQF_TRIGGER_RISING; + if (mc->gpio_cpufreq_lock) { + irq = gpio_to_irq(mc->gpio_cpufreq_lock); + mif_err("%s: CPUFREQ_LOCK_CNT IRQ# = %d\n", mc->name, irq); + ret = request_irq(irq, cpufreq_lock_handler, flag, + "cpufreq_lock", mc); + if (ret) { + mif_err("%s: ERR! request_irq(#%d) fail (err %d)\n", + mc->name, irq, ret); + return ret; + } + } +#endif + return 0; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c index 5a42755..c5835cb 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c +++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c @@ -115,6 +115,13 @@ static int esc6270_reset(struct modem_ctl *mc) int esc6270_boot_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); +#if defined(CONFIG_LINK_DEVICE_DPRAM) + /* clear intr */ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u16 recv_msg = dpld->recv_intr(dpld); + + pr_info("[MODEM_IF:ESC] dpram intr: %x\n", recv_msg); +#endif pr_info("[MODEM_IF:ESC] <%s>\n", __func__); @@ -273,8 +280,11 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -302,7 +312,7 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + mc->irq_sim_detect = pdata->irq_sim_detect; pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c index ad44579..41ead40 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c +++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c @@ -30,6 +30,8 @@ #include +#include "modem_link_device_pld.h" + #if defined(CONFIG_MACH_M0_CTC) #include #endif @@ -39,6 +41,8 @@ static int mdm6600_on(struct modem_ctl *mc) { + struct link_device *ld = get_current_link(mc->iod); + pr_info("[MODEM_IF] mdm6600_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_reset_msm || !mc->gpio_cp_on) { @@ -58,6 +62,7 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_pda_active, 1); mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; return 0; } @@ -82,7 +87,8 @@ static int mdm6600_off(struct modem_ctl *mc) static int mdm6600_reset(struct modem_ctl *mc) { - int ret; + struct link_device *ld = get_current_link(mc->iod); + /* int ret; */ pr_info("[MODEM_IF] mdm6600_reset()\n"); @@ -109,6 +115,9 @@ static int mdm6600_reset(struct modem_ctl *mc) msleep(40); /* > 37.2 + 2 msec */ } + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; + return 0; } @@ -159,6 +168,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int cp_dump_value = 0; int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; + struct link_device *ld; if (!mc->gpio_cp_reset || !mc->gpio_phone_active /*|| !mc->gpio_cp_dump_int */) { @@ -179,11 +189,6 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) } else if (phone_reset && !phone_active_value) { if (count == 1) { phone_state = STATE_CRASH_EXIT; - if (mc->iod) { - ld = get_current_link(mc->iod); - if (ld->terminate_comm) - ld->terminate_comm(ld, mc->iod); - } if (mc->iod && mc->iod->modem_state_changed) mc->iod->modem_state_changed (mc->iod, phone_state); @@ -226,8 +231,11 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->vbus_on = pdata->vbus_on; mc->vbus_off = pdata->vbus_off; - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MODEM_IF] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -332,7 +340,7 @@ static int mdm6600_on(struct modem_ctl *mc) return -ENXIO; } - gpio_set_value(mc->gpio_pda_active, 0); + gpio_set_value(mc->gpio_pda_active, 1); gpio_set_value(mc->gpio_cp_on, 1); msleep(500); @@ -346,8 +354,6 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_on, 0); msleep(500); - gpio_set_value(mc->gpio_pda_active, 1); - #if defined(CONFIG_LINK_DEVICE_PLD) gpio_set_value(mc->gpio_fpga_cs_n, 1); #endif @@ -420,9 +426,13 @@ static int mdm6600_reset(struct modem_ctl *mc) static int mdm6600_boot_on(struct modem_ctl *mc) { struct regulator *regulator; + struct link_device *ld = get_current_link(mc->iod); + struct pld_link_device *dpld = to_pld_link_device(ld); pr_info("[MSM] <%s>\n", __func__); + dpld->recv_intr(dpld); + if (!mc->gpio_flm_uart_sel) { pr_err("[MSM] no gpio data\n"); return -ENXIO; @@ -729,8 +739,11 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MSM] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -754,7 +767,7 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + mc->irq_sim_detect = pdata->irq_sim_detect; pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c new file mode 100644 index 0000000..6078916 --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c @@ -0,0 +1,218 @@ +/* /linux/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c + * + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem_prj.h" +#include "modem_link_device_dpram.h" +#include "modem_utils.h" + +#define IDPRAM_NORMAL_BOOT_MAGIC 0x4D4E + +static irqreturn_t phone_active_irq_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int phone_reset = gpio_get_value(mc->gpio_cp_reset); + int phone_active = gpio_get_value(mc->gpio_phone_active); + int phone_state = mc->phone_state; + + pr_info("MIF: <%s> state = %d, phone_reset = %d, phone_active = %d\n", + __func__, phone_state, phone_reset, phone_active); + + if (phone_reset && phone_active) { + phone_state = STATE_ONLINE; + if (mc->phone_state != STATE_BOOTING) + mc->iod->modem_state_changed(mc->iod, phone_state); + } else if (phone_reset && !phone_active) { + if (mc->phone_state == STATE_ONLINE) { + phone_state = STATE_CRASH_EXIT; + mc->iod->modem_state_changed(mc->iod, phone_state); + } + } else { + phone_state = STATE_OFFLINE; + if (mc->iod && mc->iod->modem_state_changed) + mc->iod->modem_state_changed(mc->iod, phone_state); + } + + if (phone_active) + irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW); + else + irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH); + + pr_info("MIF: <%s> phone_state = %d\n", __func__, phone_state); + + return IRQ_HANDLED; +} + +static void set_idpram_boot_magic(struct dpram_link_device *dpld) +{ + dpld->set_access(dpld, 0); + dpld->set_magic(dpld, IDPRAM_NORMAL_BOOT_MAGIC); + dpld->set_access(dpld, 1); +} + +static int qsc6085_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + mif_err("+++\n"); + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset) { + mif_err("no gpio_cp_on or no gpio_cp_reset\n"); + return -ENXIO; + } + + set_idpram_boot_magic(dpld); + + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + + gpio_set_value(mc->gpio_cp_reset, 1); + gpio_set_value(mc->gpio_cp_on, 0); + + msleep(100); + + gpio_set_value(mc->gpio_cp_on, 1); + + msleep(400); + msleep(400); + msleep(200); + + gpio_set_value(mc->gpio_cp_on, 0); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_off(struct modem_ctl *mc) +{ + int phone_wait_cnt = 0; + + pr_info("MIF: <%s+>\n", __func__); + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || + !mc->gpio_phone_active) { + pr_err("MIF: <%s> no gpio data\n", __func__); + return -ENXIO; + } + + gpio_set_value(mc->gpio_cp_on, 0); + + /* confirm phone off */ + while (1) { + if (gpio_get_value(mc->gpio_phone_active)) { + pr_err("MIF: <%s> Try to Turn Phone Off by CP_RST\n", + __func__); + gpio_set_value(mc->gpio_cp_reset, 0); + if (phone_wait_cnt > 10) { + pr_emerg("MIF: <%s> OFF Failed\n", __func__); + break; + } + phone_wait_cnt++; + mdelay(100); + } else { + pr_emerg("MIF: <%s> OFF Success\n", __func__); + break; + } + } + + mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); + + pr_info("MIF: <%s->\n", __func__); + + return 0; +} + +static int qsc6085_reset(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + mif_err("+++\n"); + + set_idpram_boot_magic(dpld); + + gpio_set_value(mc->gpio_cp_reset, 0); + msleep(100); + gpio_set_value(mc->gpio_cp_reset, 1); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_modem_dump_reset(struct modem_ctl *mc) +{ + pr_info("MIF: <%s>\n", __func__); + panic("CP Crashed"); +} + +static void qsc6085_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = qsc6085_on; + mc->ops.modem_off = qsc6085_off; + mc->ops.modem_reset = qsc6085_reset; + mc->ops.modem_dump_reset = qsc6085_modem_dump_reset; +} + +int qsc6085_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + unsigned long flag = 0; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { + mif_err("no GPIO data\n"); + return -ENXIO; + } + + mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); + pr_err("MIF: <%s> PHONE_ACTIVE IRQ# = %d\n", + __func__, mc->irq_phone_active); + + qsc6085_get_ops(mc); + + /*register phone_active_handler*/ + flag = IRQF_TRIGGER_HIGH; + + ret = request_irq(mc->irq_phone_active, + phone_active_irq_handler, + flag, "phone_active", mc); + if (ret) { + pr_err("MIF: failed to irq_phone_active request_irq: %d\n" + , ret); + return ret; + } + + ret = enable_irq_wake(mc->irq_phone_active); + if (ret) { + pr_err("MIF: <%s> failed to enable_irq_wake:%d\n", + __func__, ret); + free_irq(mc->irq_phone_active, mc); + return ret; + } + return ret; +} diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c index cfa2896..9f97dfd 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c +++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c @@ -26,6 +26,9 @@ #include "modem_prj.h" #include +spinlock_t irq_lock; +int irq_lock_flag; + int sprd_boot_done; extern int spi_thread_restart(void); @@ -45,10 +48,20 @@ static int sprd8803_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_ctrl2, 1); #endif msleep(100); -// pr_info("[MODEM_IF] %s\n", __func__); // Kill spam + pr_info("[MODEM_IF] %s\n", __func__); gpio_set_value(mc->gpio_cp_on, 1); gpio_set_value(mc->gpio_pda_active, 1); + spin_lock(&irq_lock); + if (!irq_lock_flag) { + enable_irq(mc->irq_phone_active); + enable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + enable_irq_wake(mc->irq_phone_active); + enable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 1; + } + spin_unlock(&irq_lock); + mc->phone_state = STATE_BOOTING; return 0; @@ -56,7 +69,7 @@ static int sprd8803_on(struct modem_ctl *mc) static int sprd8803_off(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); if (!mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -64,6 +77,17 @@ static int sprd8803_off(struct modem_ctl *mc) } gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_pda_active, 0); + + spin_lock(&irq_lock); + if (irq_lock_flag) { + disable_irq(mc->irq_phone_active); + disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + disable_irq_wake(mc->irq_phone_active); + disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 0; + } + spin_unlock(&irq_lock); mc->phone_state = STATE_OFFLINE; @@ -72,7 +96,7 @@ static int sprd8803_off(struct modem_ctl *mc) static int sprd8803_reset(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); spi_thread_restart(); @@ -81,20 +105,20 @@ static int sprd8803_reset(struct modem_ctl *mc) static int sprd8803_boot_on(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done); - return sprd_boot_done; + pr_info("[MODEM_IF] %s %d\n", __func__, mc->phone_state); + return mc->phone_state; } static int sprd8803_boot_off(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); spi_sema_init(); return 0; } static int sprd8803_dump_reset(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); if (!mc->gpio_ap_cp_int2) return -ENXIO; @@ -115,6 +139,8 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; + disable_irq_nosync(mc->irq_phone_active); + if (!mc->gpio_phone_active || !mc->gpio_cp_dump_int) { pr_err("[MODEM_IF] no gpio data\n"); @@ -135,7 +161,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) else phone_state = STATE_OFFLINE; - if (cp_dump_value) + if (phone_active_value && cp_dump_value) phone_state = STATE_CRASH_EXIT; if (mc->iod && mc->iod->modem_state_changed) @@ -145,6 +171,8 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) mc->bootd->modem_state_changed(mc->bootd, phone_state); exit: + enable_irq(mc->irq_phone_active); + return IRQ_HANDLED; } @@ -217,5 +245,19 @@ int sprd8803_init_modemctl_device(struct modem_ctl *mc, __func__, ret); free_irq(irq_cp_dump_int, mc); } + + irq_lock_flag = 1; + spin_lock_init(&irq_lock); + + spin_lock(&irq_lock); + if (irq_lock_flag) { + disable_irq(mc->irq_phone_active); + disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + disable_irq_wake(mc->irq_phone_active); + disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 0; + } + spin_unlock(&irq_lock); + return ret; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_ss222.c b/drivers/misc/modem_if/modem_modemctl_device_ss222.c new file mode 100644 index 0000000..1132d7a --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_ss222.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem_prj.h" +#include "modem_utils.h" + +#define MIF_INIT_TIMEOUT (30 * HZ) + +static void ss222_mc_state_fsm(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int old_state = mc->phone_state; + int new_state = mc->phone_state; + + mif_err("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(old_state), cp_on, cp_reset, cp_active); + + if (cp_active) { + if (!cp_on) { + new_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + } else if (old_state == STATE_ONLINE) { + new_state = STATE_CRASH_EXIT; + ld->mode = LINK_MODE_ULOAD; + } else { + mif_err("don't care!!!\n"); + } + } + + if (old_state != new_state) { + mif_err("new_state = %s\n", get_cp_state_str(new_state)); + mc->bootd->modem_state_changed(mc->bootd, new_state); + mc->iod->modem_state_changed(mc->iod, new_state); + } +} + +static irqreturn_t phone_active_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + + if (cp_reset) + ss222_mc_state_fsm(mc); + + return IRQ_HANDLED; +} + +static inline void make_gpio_floating(int gpio, bool floating) +{ + if (floating) + gpio_direction_input(gpio); + else + gpio_direction_output(gpio, 0); +} + +static int ss222_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_off = gpio_get_value(mc->gpio_cp_off); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int cp_status = gpio_get_value(mc->gpio_cp_status); + mif_err("+++\n"); + mif_err("cp_on:%d cp_reset:%d ps_hold:%d cp_active:%d cp_status:%d\n", + cp_on, cp_reset, cp_off, cp_active, cp_status); + + gpio_set_value(mc->gpio_pda_active, 1); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + + /* Make PS_HOLD floating (Hi-Z) for CP ON */ + make_gpio_floating(mc->gpio_cp_off, true); + + gpio_set_value(mc->gpio_cp_on, 0); + msleep(100); + + gpio_set_value(mc->gpio_cp_reset, 0); + msleep(500); + + gpio_set_value(mc->gpio_cp_on, 1); + msleep(100); + + c2c_reload(); + gpio_set_value(mc->gpio_cp_reset, 1); + msleep(300); + + mif_err("---\n"); + return 0; +} + +static int ss222_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + mif_err("+++\n"); + + if (mc->phone_state == STATE_OFFLINE || cp_on == 0) + return 0; + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + + gpio_set_value(mc->gpio_cp_reset, 0); + + /* Make PS_HOLD LOW for CP OFF */ + make_gpio_floating(mc->gpio_cp_off, false); + gpio_set_value(mc->gpio_cp_on, 0); + + mif_err("---\n"); + return 0; +} + +static int ss222_reset(struct modem_ctl *mc) +{ + mif_err("+++\n"); + + if (ss222_off(mc)) + return -EIO; + + msleep(100); + + if (ss222_on(mc)) + return -EIO; + + mif_err("---\n"); + return 0; +} + +static int ss222_force_crash_exit(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_err("+++\n"); + + /* Make DUMP start */ + ld->force_dump(ld, mc->bootd); + + mif_err("---\n"); + return 0; +} + +static int ss222_dump_reset(struct modem_ctl *mc) +{ + unsigned int gpio_cp_reset = mc->gpio_cp_reset; + mif_err("+++\n"); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + gpio_set_value(gpio_cp_reset, 0); + udelay(200); + + c2c_reload(); + gpio_set_value(gpio_cp_reset, 1); + msleep(300); + + gpio_set_value(mc->gpio_ap_status, 1); + + mif_err("---\n"); + return 0; +} + +static int ss222_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_debug("+++\n"); + + disable_irq_nosync(mc->irq_phone_active); + + gpio_set_value(mc->gpio_ap_status, 1); + + ld->mode = LINK_MODE_BOOT; + + mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + + INIT_COMPLETION(ld->init_cmpl); + + mif_debug("---\n"); + return 0; +} + +static int ss222_boot_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + unsigned long remain; + mif_debug("+++\n"); + + ld->mode = LINK_MODE_IPC; + + remain = wait_for_completion_timeout(&ld->init_cmpl, MIF_INIT_TIMEOUT); + if (remain == 0) { + mif_err("T-I-M-E-O-U-T\n"); + mif_err("xxx\n"); + return -EAGAIN; + } + + mif_debug("---\n"); + return 0; +} + +static int ss222_boot_done(struct modem_ctl *mc) +{ + mif_debug("+++\n"); + + if (wake_lock_active(&mc->mc_wake_lock)) + wake_unlock(&mc->mc_wake_lock); + + enable_irq(mc->irq_phone_active); + + mif_debug("---\n"); + return 0; +} + +static void ss222_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = ss222_on; + mc->ops.modem_off = ss222_off; + mc->ops.modem_reset = ss222_reset; + mc->ops.modem_boot_on = ss222_boot_on; + mc->ops.modem_boot_off = ss222_boot_off; + mc->ops.modem_boot_done = ss222_boot_done; + mc->ops.modem_force_crash_exit = ss222_force_crash_exit; + mc->ops.modem_dump_reset = ss222_dump_reset; +} + +int ss222_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + int irq = 0; + unsigned long flag = 0; + mif_debug("+++\n"); + + if (!pdata->gpio_cp_on || !pdata->gpio_cp_off || !pdata->gpio_cp_reset + || !pdata->gpio_pda_active || !pdata->gpio_phone_active + || !pdata->gpio_ap_wakeup || !pdata->gpio_ap_status + || !pdata->gpio_cp_wakeup || !pdata->gpio_cp_status) { + mif_err("ERR! no GPIO data\n"); + mif_err("xxx\n"); + return -ENXIO; + } + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_ap_wakeup = pdata->gpio_ap_wakeup; + mc->gpio_ap_status = pdata->gpio_ap_status; + mc->gpio_cp_wakeup = pdata->gpio_cp_wakeup; + mc->gpio_cp_status = pdata->gpio_cp_status; + + gpio_set_value(mc->gpio_cp_reset, 0); + + gpio_set_value(mc->gpio_cp_on, 0); + + ss222_get_ops(mc); + dev_set_drvdata(mc->dev, mc); + + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "umts_wake_lock"); + + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("ERR! no irq_phone_active\n"); + mif_err("xxx\n"); + return -1; + } + mif_err("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); + + irq = mc->irq_phone_active; + flag = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND; + ret = request_irq(irq, phone_active_handler, flag, "umts_active", mc); + if (ret) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, ret); + mif_err("xxx\n"); + return ret; + } + ret = enable_irq_wake(irq); + if (ret) + mif_err("enable_irq_wake(#%d) fail (err %d)\n", irq, ret); + + mif_debug("---\n"); + return 0; +} + diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c index d2fcf5b..c2d5067 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c @@ -27,7 +27,7 @@ static int xmm6260_on(struct modem_ctl *mc) { - mif_debug("xmm6260_on()\n"); + mif_info("xmm6260_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) { mif_err("no gpio data\n"); @@ -66,7 +66,7 @@ static int xmm6260_on(struct modem_ctl *mc) static int xmm6260_off(struct modem_ctl *mc) { - mif_debug("xmm6260_off()\n"); + mif_info("xmm6260_off()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -85,7 +85,7 @@ static int xmm6260_off(struct modem_ctl *mc) static int xmm6260_reset(struct modem_ctl *mc) { - mif_debug("xmm6260_reset()\n"); + mif_info("xmm6260_reset()\n"); if (!mc->gpio_cp_reset || !mc->gpio_reset_req_n) return -ENXIO; @@ -122,7 +122,7 @@ static int xmm6260_reset(struct modem_ctl *mc) static int xmm6260_boot_on(struct modem_ctl *mc) { - mif_debug("xmm6260_boot_on()\n"); + mif_info("xmm6260_boot_on()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); @@ -136,7 +136,7 @@ static int xmm6260_boot_on(struct modem_ctl *mc) static int xmm6260_boot_off(struct modem_ctl *mc) { - mif_debug("xmm6260_boot_off()\n"); + mif_info("xmm6260_boot_off()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c index 4d0b69c..fcc4042 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c @@ -26,7 +26,9 @@ #include #include #include "modem_prj.h" - +#ifdef CONFIG_FAST_BOOT +#include +#endif static int xmm6262_on(struct modem_ctl *mc) { mif_info("\n"); @@ -59,11 +61,13 @@ static int xmm6262_on(struct modem_ctl *mc) udelay(60); gpio_set_value(mc->gpio_cp_on, 0); msleep(20); + + mc->phone_state = STATE_BOOTING; + if (mc->gpio_revers_bias_restore) mc->gpio_revers_bias_restore(); gpio_set_value(mc->gpio_pda_active, 1); - mc->phone_state = STATE_BOOTING; return 0; } @@ -120,6 +124,20 @@ static int xmm6262_reset(struct modem_ctl *mc) return 0; } +static int xmm6262_force_crash_exit(struct modem_ctl *mc) +{ + mif_info("\n"); + + if (!mc->gpio_ap_dump_int) + return -ENXIO; + + gpio_set_value(mc->gpio_ap_dump_int, 1); + mif_info("set ap_dump_int(%d) to high=%d\n", + mc->gpio_ap_dump_int, gpio_get_value(mc->gpio_ap_dump_int)); + return 0; +} + + static irqreturn_t phone_active_irq_handler(int irq, void *_mc) { int phone_reset = 0; @@ -172,14 +190,72 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) return IRQ_HANDLED; } +#ifdef CONFIG_FAST_BOOT +#include + +static void mif_sim_detect_complete(struct modem_ctl *mc) +{ + if (mc->sim_shutdown_req) { + mif_info("fake shutdown sim changed shutdown\n"); + kernel_power_off(); + /*kernel_restart(NULL);*/ + mc->sim_shutdown_req = false; + } +} + +static int mif_init_sim_shutdown(struct modem_ctl *mc) +{ + mc->sim_shutdown_req = false; + mc->modem_complete = mif_sim_detect_complete; + + return 0; +} + +static void mif_check_fake_shutdown(struct modem_ctl *mc, bool online) +{ + if (fake_shut_down && mc->sim_state.online != online) + mc->sim_shutdown_req = true; +} + +#else +static inline int mif_init_sim_shutdown(struct modem_ctl *mc) { return 0; } +#define mif_check_fake_shutdown(a, b) do {} while (0) +#endif + + +#define SIM_DETECT_DEBUG static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) { struct modem_ctl *mc = (struct modem_ctl *)_mc; +#ifdef SIM_DETECT_DEBUG + int val = gpio_get_value(mc->gpio_sim_detect); + static int unchange; + static int prev_val; + + if (mc->phone_state == STATE_BOOTING) { + mif_info("BOOTING, reset unchange\n"); + unchange = 0; + } - if (mc->iod && mc->iod->sim_state_changed) + if (prev_val == val) { + if (unchange++ > 50) { + mif_err("Abnormal SIM detect GPIO irqs"); + disable_irq_nosync(mc->gpio_sim_detect); + panic("SIM detect IRQ Error"); + } + } else { + unchange = 0; + } + prev_val = val; +#endif + if (mc->iod && mc->iod->sim_state_changed) { + mif_check_fake_shutdown(mc, + gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity + ); mc->iod->sim_state_changed(mc->iod, gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity ); + } return IRQ_HANDLED; } @@ -189,6 +265,7 @@ static void xmm6262_get_ops(struct modem_ctl *mc) mc->ops.modem_on = xmm6262_on; mc->ops.modem_off = xmm6262_off; mc->ops.modem_reset = xmm6262_reset; + mc->ops.modem_force_crash_exit = xmm6262_force_crash_exit; } int xmm6262_init_modemctl_device(struct modem_ctl *mc, @@ -218,6 +295,7 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2; #endif + pdev = to_platform_device(mc->dev); mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); @@ -261,6 +339,12 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, /* initialize sim_state => insert: gpio=0, remove: gpio=1 */ mc->sim_state.online = gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity; + + ret = mif_init_sim_shutdown(mc); + if (ret) { + mif_err("failed to sim fake shutdown init: %d\n", ret); + goto err_sim_detect_set_wake_irq; + } } return ret; diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h index f85596f..537220a 100644 --- a/drivers/misc/modem_if/modem_prj.h +++ b/drivers/misc/modem_if/modem_prj.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -19,12 +18,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include + +#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP +#define DEBUG_MODEM_IF +#endif #define MAX_CPINFO_SIZE 512 @@ -33,7 +38,10 @@ #define MAX_FMT_DEVS 10 #define MAX_RAW_DEVS 32 #define MAX_RFS_DEVS 10 -#define MAX_NUM_IO_DEV (MAX_FMT_DEVS + MAX_RAW_DEVS + MAX_RFS_DEVS) +#define MAX_BOOT_DEVS 10 +#define MAX_DUMP_DEVS 10 + +#define MAX_IOD_RXQ_LEN 2048 #define IOCTL_MODEM_ON _IO('o', 0x19) #define IOCTL_MODEM_OFF _IO('o', 0x20) @@ -62,22 +70,40 @@ #define IOCTL_MODEM_SWITCH_MODEM _IO('o', 0x37) #endif -#define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40) -#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) - -/* ioctl command definitions. */ -#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xd0) -#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xd1) -#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xd2) -#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xd3) +#define IOCTL_MODEM_RAMDUMP_START _IO('o', 0xCE) +#define IOCTL_MODEM_RAMDUMP_STOP _IO('o', 0xCF) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf) +#define IOCTL_MODEM_XMIT_BOOT _IO('o', 0x40) +#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) /* ioctl command for IPC Logger */ #define IOCTL_MIF_LOG_DUMP _IO('o', 0x51) #define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52) +/* ioctl command definitions. */ +#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xD0) +#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xD1) +#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xD2) +#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xD3) + +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xDE) +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xDF) + +#define CPBOOT_DIR_MASK 0xF000 +#define CPBOOT_STAGE_MASK 0x0F00 +#define CPBOOT_CMD_MASK 0x000F +#define CPBOOT_REQ_RESP_MASK 0x0FFF + +#define CPBOOT_DIR_AP2CP 0x9000 +#define CPBOOT_DIR_CP2AP 0xA000 + +#define CPBOOT_STAGE_SHIFT 8 + +#define CPBOOT_STAGE_START 0x0000 +#define CPBOOT_CRC_SEND 0x000C +#define CPBOOT_STAGE_DONE 0x000D +#define CPBOOT_STAGE_FAIL 0x000F + /* modem status */ #define MODEM_OFF 0 #define MODEM_CRASHED 1 @@ -93,83 +119,50 @@ #define PSD_DATA_CHID_BEGIN 0x2A #define PSD_DATA_CHID_END 0x38 -#define PS_DATA_CH_0 10 -#define PS_DATA_CH_LAST 24 +#define PS_DATA_CH_0 10 +#define PS_DATA_CH_LAST 24 +#define RMNET0_CH_ID PS_DATA_CH_0 #define IP6VERSION 6 #define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC} -/* loopback: CP -> AP -> CP */ -#define CP2AP_LOOPBACK_CHANNEL 30 - -/* ip loopback */ -#define RMNET0_CH_ID 10 +/* IP loopback */ +#define DATA_DRAIN_CHANNEL 30 /* Drain channel to drop RX packets */ #define DATA_LOOPBACK_CHANNEL 31 /* Debugging features */ -#define MAX_MIF_LOG_PATH_LEN 128 -#define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */ - -#define MAX_MIF_EVT_BUFF_SIZE 256 -#define MAX_MIF_TIME_LEN 32 -#define MAX_MIF_NAME_LEN 16 -#define MAX_MIF_STR_LEN 127 -#define MAX_MIF_LOG_LEN 128 - -enum mif_event_id { - MIF_IRQ_EVT = 0, - MIF_LNK_RX_EVT, - MIF_MUX_RX_EVT, - MIF_IOD_RX_EVT, - MIF_IOD_TX_EVT, - MIF_MUX_TX_EVT, - MIF_LNK_TX_EVT, - MAX_MIF_EVT -}; - -struct dpram_queue_status { - unsigned in; - unsigned out; -}; - -struct dpram_queue_status_pair { - struct dpram_queue_status txq; - struct dpram_queue_status rxq; -}; - -struct dpram_irq_buff { - unsigned magic; - unsigned access; - struct dpram_queue_status_pair qsp[MAX_IPC_DEV]; - unsigned int2ap; - unsigned int2cp; -}; - -/* Not use */ -struct mif_event_buff { - char time[MAX_MIF_TIME_LEN]; - - struct timeval tv; - enum mif_event_id evt; - - char mc[MAX_MIF_NAME_LEN]; - - char iod[MAX_MIF_NAME_LEN]; - - char ld[MAX_MIF_NAME_LEN]; - enum modem_link link_type; - - unsigned rcvd; - unsigned len; - union { - u8 data[MAX_MIF_LOG_LEN]; - struct dpram_irq_buff dpram_irqb; - }; +#define MIF_LOG_DIR "/sdcard/log" +#define MIF_MAX_PATH_LEN 256 +#define MIF_MAX_NAME_LEN 64 +#define MIF_MAX_STR_LEN 32 + +#define CP_CRASH_TAG "CP Crash " + +static const char const *dev_format_str[] = { + [IPC_FMT] = "FMT", + [IPC_RAW] = "RAW", + [IPC_RFS] = "RFS", + [IPC_MULTI_RAW] = "MULTI_RAW", + [IPC_CMD] = "CMD", + [IPC_BOOT] = "BOOT", + [IPC_RAMDUMP] = "RAMDUMP", + [IPC_DEBUG] = "DEBUG", }; -#define MIF_LOG_DIR "/sdcard" -#define MIF_LOG_LV_FILE "/data/.mif_log_level" +/** + * get_dev_name + * @dev: IPC device (enum dev_format) + * + * Returns IPC device name as a string. + */ +static const inline char *get_dev_name(unsigned int dev) +{ + if (unlikely(dev >= MAX_DEV_FORMAT)) + return "INVALID"; + else + return dev_format_str[dev]; +} /* Does modem ctl structure will use state ? or status defined below ?*/ enum modem_state { @@ -187,6 +180,26 @@ enum modem_state { #endif }; +static const char const *cp_state_str[] = { + [STATE_OFFLINE] = "OFFLINE", + [STATE_CRASH_RESET] = "CRASH_RESET", + [STATE_CRASH_EXIT] = "CRASH_EXIT", + [STATE_BOOTING] = "BOOTING", + [STATE_ONLINE] = "ONLINE", + [STATE_NV_REBUILDING] = "NV_REBUILDING", + [STATE_LOADER_DONE] = "LOADER_DONE", + [STATE_SIM_ATTACH] = "SIM_ATTACH", + [STATE_SIM_DETACH] = "SIM_DETACH", +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + [STATE_MODEM_SWITCH] = "MODEM_SWITCH", +#endif +}; + +static const inline char *get_cp_state_str(int state) +{ + return cp_state_str[state]; +} + enum com_state { COM_NONE, COM_ONLINE, @@ -208,6 +221,17 @@ struct sim_state { bool changed; /* online is changed? */ }; +enum cp_boot_mode { + CP_BOOT_MODE_NORMAL, + CP_BOOT_MODE_DUMP, + MAX_CP_BOOT_MODE +}; + +struct modem_firmware { + char *binary; + u32 size; +}; + #define HDLC_START 0x7F #define HDLC_END 0x7E #define SIZE_OF_HDLC_START 1 @@ -216,8 +240,8 @@ struct sim_state { struct header_data { char hdr[HDLC_HEADER_MAX_SIZE]; - unsigned len; - unsigned frag_len; + u32 len; + u32 frag_len; char start; /*hdlc start header 0x7F*/ }; @@ -255,10 +279,14 @@ struct sipc_fmt_hdr { #define SIPC5_EXT_FIELD_EXIST 0b00000010 #define SIPC5_CTL_FIELD_EXIST 0b00000001 -#define SIPC5_MAX_HEADER_SIZE 6 -#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 +#define SIPC5_EXT_LENGTH_MASK SIPC5_EXT_FIELD_EXIST +#define SIPC5_CTL_FIELD_MASK (SIPC5_EXT_FIELD_EXIST | SIPC5_CTL_FIELD_EXIST) + +#define SIPC5_MIN_HEADER_SIZE 4 #define SIPC5_HEADER_SIZE_WITH_CTL_FLD 5 -#define SIPC5_MIN_HEADER_SIZE 4 +#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 +#define SIPC5_MAX_HEADER_SIZE SIPC5_HEADER_SIZE_WITH_EXT_LEN + #define SIPC5_CONFIG_SIZE 1 #define SIPC5_CH_ID_SIZE 1 @@ -267,13 +295,18 @@ struct sipc_fmt_hdr { #define SIPC5_LEN_OFFSET 2 #define SIPC5_CTL_OFFSET 4 -#define SIPC5_CH_ID_RAW_0 0 #define SIPC5_CH_ID_PDP_0 10 #define SIPC5_CH_ID_PDP_LAST 24 +#define SIPC5_CH_ID_BOOT0 215 +#define SIPC5_CH_ID_DUMP0 225 #define SIPC5_CH_ID_FMT_0 235 #define SIPC5_CH_ID_RFS_0 245 #define SIPC5_CH_ID_MAX 255 +#define SIPC5_CH_ID_FLOW_CTRL 255 +#define FLOW_CTRL_SUSPEND ((u8)(0xCA)) +#define FLOW_CTRL_RESUME ((u8)(0xCB)) + /* If iod->id is 0, do not need to store to `iodevs_tree_fmt' in SIPC4 */ #define sipc4_is_not_reserved_channel(ch) ((ch) != 0) @@ -295,20 +328,6 @@ struct sipc5_link_hdr { } __packed; struct sipc5_frame_data { - /* Config octet */ - u8 config; - - /* Channel ID */ - u8 ch_id; - - /* Control for multiple FMT frame */ - u8 control; - - /* Frame configuration set by header analysis */ - bool padding; - bool ctl_fld; - bool ext_len; - /* Frame length calculated from the length fields */ unsigned len; @@ -318,11 +337,17 @@ struct sipc5_frame_data { /* The length of received header */ unsigned hdr_rcvd; - /* The length of data payload */ - unsigned data_len; + /* The length of link layer payload */ + unsigned pay_len; /* The length of received data */ - unsigned data_rcvd; + unsigned pay_rcvd; + + /* The length of link layer padding */ + unsigned pad_len; + + /* The length of received padding */ + unsigned pad_rcvd; /* Header buffer */ u8 hdr[SIPC5_MAX_HEADER_SIZE]; @@ -349,8 +374,9 @@ struct skbuff_private { struct io_device *iod; struct link_device *ld; struct io_device *real_iod; /* for rx multipdp */ - u8 ch_id; - u8 control; + + /* for indicating that thers is only one IPC frame in an skb */ + bool single_frame; } __packed; static inline struct skbuff_private *skbpriv(struct sk_buff *skb) @@ -359,6 +385,35 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb) return (struct skbuff_private *)&skb->cb; } +enum iod_rx_state { + IOD_RX_ON_STANDBY = 0, + IOD_RX_HEADER, + IOD_RX_PAYLOAD, + IOD_RX_PADDING, + MAX_IOD_RX_STATE +}; + +static const char const *rx_state_str[] = { + [IOD_RX_ON_STANDBY] = "RX_ON_STANDBY", + [IOD_RX_HEADER] = "RX_HEADER", + [IOD_RX_PAYLOAD] = "RX_PAYLOAD", + [IOD_RX_PADDING] = "RX_PADDING", +}; + +/** + * get_dev_name + * @dev: IPC device (enum dev_format) + * + * Returns IPC device name as a string. + */ +static const inline char *get_rx_state_str(unsigned int state) +{ + if (unlikely(state >= MAX_IOD_RX_STATE)) + return "INVALID_STATE"; + else + return rx_state_str[state]; +} + struct io_device { /* rb_tree node for an io device */ struct rb_node node_chan; @@ -367,6 +422,7 @@ struct io_device { /* Name of the IO device */ char *name; + /* Reference count */ atomic_t opened; /* Wait queue for the IO device */ @@ -383,7 +439,11 @@ struct io_device { enum modem_io io_typ; enum modem_network net_typ; - bool use_handover; /* handover 2+ link devices */ + /* The name of the application that will use this IO device */ + char *app; + + /* Whether or not handover among 2+ link devices */ + bool use_handover; /* SIPC version */ enum sipc_ver ipc_version; @@ -391,6 +451,10 @@ struct io_device { /* Rx queue of sk_buff */ struct sk_buff_head sk_rx_q; + /* RX state used in RX FSM */ + enum iod_rx_state curr_rx_state; + enum iod_rx_state next_rx_state; + /* ** work for each io device, when delayed work needed ** use this for private io device rx action @@ -447,6 +511,9 @@ struct link_device { /* SIPC version */ enum sipc_ver ipc_version; + /* Maximum IPC device = the last IPC device (e.g. IPC_RFS) + 1 */ + int max_ipc_dev; + /* Modem data */ struct modem_data *mdm_data; @@ -459,6 +526,12 @@ struct link_device { /* Operation mode of the link device */ enum link_mode mode; + /* completion for waiting for link initialization */ + struct completion init_cmpl; + + /* completion for waiting for PIF initialization in a CP */ + struct completion pif_cmpl; + struct io_device *fmt_iods[4]; /* TX queue of socket buffers */ @@ -468,13 +541,30 @@ struct link_device { struct sk_buff_head *skb_txq[MAX_IPC_DEV]; + /* RX queue of socket buffers */ + struct sk_buff_head sk_fmt_rx_q; + struct sk_buff_head sk_raw_rx_q; + struct sk_buff_head sk_rfs_rx_q; + + struct sk_buff_head *skb_rxq[MAX_IPC_DEV]; + bool raw_tx_suspended; /* for misc dev */ struct completion raw_tx_resumed_by_cp; + /** + * This flag is for TX flow control on network interface. + * This must be set and clear only by a flow control command from CP. + */ + bool suspend_netif_tx; + struct workqueue_struct *tx_wq; struct work_struct tx_work; struct delayed_work tx_delayed_work; - struct delayed_work tx_dwork; + + struct delayed_work *tx_dwork[MAX_IPC_DEV]; + struct delayed_work fmt_tx_dwork; + struct delayed_work raw_tx_dwork; + struct delayed_work rfs_tx_dwork; struct workqueue_struct *rx_wq; struct work_struct rx_work; @@ -495,20 +585,26 @@ struct link_device { int (*send)(struct link_device *ld, struct io_device *iod, struct sk_buff *skb); - int (*udl_start)(struct link_device *ld, struct io_device *iod); - - int (*force_dump)(struct link_device *ld, struct io_device *iod); - - int (*dump_start)(struct link_device *ld, struct io_device *iod); + /* method for CP booting */ + int (*xmit_boot)(struct link_device *ld, struct io_device *iod, + unsigned long arg); - int (*modem_update)(struct link_device *ld, struct io_device *iod, + /* methods for CP firmware upgrade */ + int (*dload_start)(struct link_device *ld, struct io_device *iod); + int (*firm_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); + /* methods for CP crash dump */ + int (*force_dump)(struct link_device *ld, struct io_device *iod); + int (*dump_start)(struct link_device *ld, struct io_device *iod); int (*dump_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); + int (*dump_finish)(struct link_device *ld, struct io_device *iod, + unsigned long arg); + /* IOCTL extension */ int (*ioctl)(struct link_device *ld, struct io_device *iod, - unsigned cmd, unsigned long _arg); + unsigned cmd, unsigned long arg); }; /** rx_alloc_skb - allocate an skbuff and set skb's iod, ld @@ -567,6 +663,9 @@ struct modem_shared { struct mif_storage storage; spinlock_t lock; + /* CP crash information */ + char cp_crash_info[530]; + /* loopbacked IP address * default is 0.0.0.0 (disabled) * after you setted this, you can use IP packet loopback using this IP. @@ -586,35 +685,52 @@ struct modem_ctl { struct sim_state sim_state; unsigned gpio_cp_on; + unsigned gpio_cp_off; unsigned gpio_reset_req_n; unsigned gpio_cp_reset; + + /* for broadcasting AP's PM state (active or sleep) */ unsigned gpio_pda_active; + + /* for checking aliveness of CP */ unsigned gpio_phone_active; + int irq_phone_active; + + /* for AP-CP power management (PM) handshaking */ + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* for USB/HSIC PM */ + unsigned gpio_host_wakeup; + int irq_host_wakeup; + unsigned gpio_host_active; + unsigned gpio_slave_wakeup; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + unsigned gpio_cpufreq_lock; + struct delayed_work work_cpu_lock; + struct delayed_work work_cpu_unlock; +#endif + unsigned gpio_cp_dump_int; unsigned gpio_ap_dump_int; unsigned gpio_flm_uart_sel; + unsigned gpio_cp_warm_reset; #if defined(CONFIG_MACH_M0_CTC) unsigned gpio_flm_uart_sel_rev06; #endif - unsigned gpio_cp_warm_reset; - unsigned gpio_cp_off; - unsigned gpio_sim_detect; - unsigned gpio_dynamic_switching; - int irq_phone_active; + unsigned gpio_sim_detect; int irq_sim_detect; -#ifdef CONFIG_LTE_MODEM_CMC221 - const struct attribute_group *group; - unsigned gpio_slave_wakeup; - unsigned gpio_host_wakeup; - unsigned gpio_host_active; - int irq_host_wakeup; - - struct delayed_work dwork; -#endif /*CONFIG_LTE_MODEM_CMC221*/ - - struct work_struct work; +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga_cs_n; +#endif #if defined(CONFIG_MACH_U1_KOR_LGT) unsigned gpio_cp_reset_msm; @@ -635,9 +751,13 @@ struct modem_ctl { unsigned gpio_cp_ctrl2; #endif -#ifdef CONFIG_LINK_DEVICE_PLD - unsigned gpio_fpga_cs_n; -#endif + /* Switch with 2 links in a modem */ + unsigned gpio_link_switch; + + const struct attribute_group *group; + + struct delayed_work dwork; + struct work_struct work; struct modemctl_ops ops; struct io_device *iod; @@ -651,119 +771,45 @@ struct modem_ctl { bool need_switch_to_usb; bool sim_polarity; + + bool sim_shutdown_req; + void (*modem_complete)(struct modem_ctl *mc); }; int sipc4_init_io_device(struct io_device *iod); int sipc5_init_io_device(struct io_device *iod); -/** - * sipc5_start_valid - * @cfg: configuration field of an SIPC5 link frame - * - * Returns TRUE if the start (configuration field) of an SIPC5 link frame - * is valid or returns FALSE if it is not valid. - * - */ -static inline int sipc5_start_valid(u8 cfg) -{ - return (cfg & SIPC5_START_MASK) == SIPC5_START_MASK; -} - -/** - * sipc5_get_hdr_len - * @cfg: configuration field of an SIPC5 link frame - * - * Returns the length of SIPC5 link layer header in an SIPC5 link frame - * - */ -static inline unsigned sipc5_get_hdr_len(u8 cfg) -{ - if (cfg & SIPC5_EXT_FIELD_EXIST) { - if (cfg & SIPC5_CTL_FIELD_EXIST) - return SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - return SIPC5_HEADER_SIZE_WITH_EXT_LEN; - } else { - return SIPC5_MIN_HEADER_SIZE; - } -} - -/** - * sipc5_get_ch_id - * @frm: pointer to an SIPC5 frame - * - * Returns the channel ID in an SIPC5 link frame - * - */ -static inline u8 sipc5_get_ch_id(u8 *frm) -{ - return *(frm + SIPC5_CH_ID_OFFSET); -} - -/** - * sipc5_get_frame_sz16 - * @frm: pointer to an SIPC5 link frame - * - * Returns the length of an SIPC5 link frame without the extended length field - * - */ -static inline unsigned sipc5_get_frame_sz16(u8 *frm) -{ - return *((u16 *)(frm + SIPC5_LEN_OFFSET)); -} - -/** - * sipc5_get_frame_sz32 - * @frm: pointer to an SIPC5 frame - * - * Returns the length of an SIPC5 link frame with the extended length field - * - */ -static inline unsigned sipc5_get_frame_sz32(u8 *frm) -{ - return *((u32 *)(frm + SIPC5_LEN_OFFSET)); -} - -/** - * sipc5_calc_padding_size - * @len: length of an SIPC5 link frame - * - * Returns the padding size for an SIPC5 link frame - * - */ -static inline unsigned sipc5_calc_padding_size(unsigned len) -{ - unsigned residue = len & 0x3; - return residue ? (4 - residue) : 0; -} - -extern void set_sromc_access(bool access); +bool sipc5_start_valid(u8 *frm); +bool sipc5_padding_exist(u8 *frm); +bool sipc5_multi_frame(u8 *frm); +bool sipc5_ext_len(u8 *frm); +int sipc5_get_hdr_len(u8 *frm); +u8 sipc5_get_ch_id(u8 *frm); +u8 sipc5_get_ctrl_field(u8 *frm); +int sipc5_get_frame_len(u8 *frm); +int sipc5_calc_padding_size(int len); +int sipc5_get_total_len(u8 *frm); + +u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count); +void sipc5_build_header(struct io_device *iod, struct link_device *ld, + u8 *buff, u8 cfg, u8 ctrl, u32 count); #if defined(CONFIG_TDSCDMA_MODEM_SPRD8803) && defined(CONFIG_LINK_DEVICE_SPI) extern int spi_sema_init(void); extern int sprd_boot_done; -struct ipc_spi { - struct class *class; - struct device *dev; - struct cdev cdev; - dev_t devid; - - wait_queue_head_t waitq; - struct fasync_struct *async_queue; - u32 mailbox; - - unsigned long base; - unsigned long size; - void __iomem *mmio; +#endif - int irq; +#define STD_UDL_STEP_MASK 0x0000000F +#define STD_UDL_SEND 0x1 +#define STD_UDL_CRC 0xC - struct completion comp; - atomic_t ref_sem; - unsigned long flags; +struct std_dload_info { + u32 size; + u32 mtu; + u32 num_frames; +} __packed; - const struct attribute_group *group; -}; -#endif +u32 std_udl_get_cmd(u8 *frm); +bool std_udl_with_payload(u32 cmd); #endif diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c index c8e83ed..366d0fa 100644 --- a/drivers/misc/modem_if/modem_sim_slot_switch.c +++ b/drivers/misc/modem_if/modem_sim_slot_switch.c @@ -17,7 +17,7 @@ static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr //return '0' slot path is '||', return '1' slot path is 'X' value = gpio_get_value(GPIO_UIM_SIM_SEL); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) value = (~value & 0x1); #endif printk("Current Slot is %x\n", value); @@ -34,7 +34,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr switch(value) { case 0: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif @@ -43,7 +43,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr break; case 1: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 0); else #endif @@ -57,7 +57,8 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr return size; } -static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch); +static DEVICE_ATTR(slot_sel, S_IRUGO | S_IWUSR | S_IWGRP, + get_slot_switch, set_slot_switch); static int __init slot_switch_manager_init(void) { @@ -75,7 +76,7 @@ static int __init slot_switch_manager_init(void) gpio_direction_output(GPIO_UIM_SIM_SEL, 1); s3c_gpio_setpull(GPIO_UIM_SIM_SEL, S3C_GPIO_PULL_NONE); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c index d62aaa6..0bdd3f0 100644 --- a/drivers/misc/modem_if/modem_utils.c +++ b/drivers/misc/modem_if/modem_utils.c @@ -12,18 +12,35 @@ * */ -#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 "modem_prj.h" +#include "modem_variation.h" #include "modem_utils.h" #define CMD_SUSPEND ((unsigned short)(0x00CA)) @@ -35,42 +52,29 @@ "mif: ------------------------------------------------------------" #define LINE_BUFF_SIZE 80 -#ifdef CONFIG_LINK_DEVICE_DPRAM -#include "modem_link_device_dpram.h" -int mif_dump_dpram(struct io_device *iod) -{ - struct link_device *ld = get_current_link(iod); - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u32 size = dpld->dp_size; - unsigned long read_len = 0; - struct sk_buff *skb; - char *buff; +static const char *hex = "0123456789abcdef"; - buff = kzalloc(size, GFP_ATOMIC); - if (!buff) { - pr_err("[MIF] <%s> alloc dpram buff failed\n", __func__); - return -ENOMEM; - } else { - dpld->dpram_dump(ld, buff); - } +void ts2utc(struct timespec *ts, struct utc_time *utc) +{ + struct tm tm; + + time_to_tm((ts->tv_sec - (sys_tz.tz_minuteswest * 60)), 0, &tm); + utc->year = 1900 + tm.tm_year; + utc->mon = 1 + tm.tm_mon; + utc->day = tm.tm_mday; + utc->hour = tm.tm_hour; + utc->min = tm.tm_min; + utc->sec = tm.tm_sec; + utc->msec = ns2ms(ts->tv_nsec); +} - while (read_len < size) { - skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); - if (!skb) { - pr_err("[MIF] <%s> alloc skb failed\n", __func__); - kfree(buff); - return -ENOMEM; - } - memcpy(skb_put(skb, MAX_IPC_SKB_SIZE), - buff + read_len, MAX_IPC_SKB_SIZE); - skb_queue_tail(&iod->sk_rx_q, skb); - read_len += MAX_IPC_SKB_SIZE; - wake_up(&iod->wq); - } - kfree(buff); - return 0; +void get_utc_time(struct utc_time *utc) +{ + struct timespec ts; + getnstimeofday(&ts); + ts2utc(&ts, utc); } -#endif +EXPORT_SYMBOL(get_utc_time); int mif_dump_log(struct modem_shared *msd, struct io_device *iod) { @@ -82,7 +86,7 @@ int mif_dump_log(struct modem_shared *msd, struct io_device *iod) while (read_len < MAX_MIF_BUFF_SIZE) { skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); if (!skb) { - pr_err("[MIF] <%s> alloc skb failed\n", __func__); + mif_err("ERR! alloc_skb fail\n"); spin_unlock_irqrestore(&msd->lock, flags); return -ENOMEM; } @@ -164,7 +168,6 @@ void _mif_com_log(enum mif_log_id id, struct mif_common_block *block; unsigned long int flags; va_list args; - int ret; spin_lock_irqsave(&msd->lock, flags); @@ -179,7 +182,7 @@ void _mif_com_log(enum mif_log_id id, block->time = get_kernel_time(); va_start(args, format); - ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); + vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); va_end(args); } @@ -209,13 +212,12 @@ void _mif_time_log(enum mif_log_id id, struct modem_shared *msd, /* dump2hex * dump data to hex as fast as possible. - * the length of @buf must be greater than "@len * 3" + * the length of @buff must be greater than "@len * 3" * it need 3 bytes per one data byte to print. */ -static inline int dump2hex(char *buf, const char *data, size_t len) +static inline int dump2hex(char *buff, const char *data, size_t len) { - static const char *hex = "0123456789abcdef"; - char *dest = buf; + char *dest = buff; int i; for (i = 0; i < len; i++) { @@ -228,24 +230,26 @@ static inline int dump2hex(char *buf, const char *data, size_t len) *dest = '\0'; - return dest - buf; + return dest - buff; } -int pr_ipc(const char *str, const char *data, size_t len) +void pr_ipc(int level, const char *tag, const char *data, size_t len) { - struct timeval tv; - struct tm date; - unsigned char hexstr[128]; - - do_gettimeofday(&tv); - time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); + struct utc_time utc; + unsigned char str[128]; - dump2hex(hexstr, data, (len > 40 ? 40 : len)); + if (level < 0) + return; - return pr_info("mif: %s: [%02d-%02d %02d:%02d:%02d.%03ld] %s\n", - str, date.tm_mon + 1, date.tm_mday, - date.tm_hour, date.tm_min, date.tm_sec, - (tv.tv_usec > 0 ? tv.tv_usec / 1000 : 0), hexstr); + get_utc_time(&utc); + dump2hex(str, data, (len > 32 ? 32 : len)); + if (level > 0) { + pr_err("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, + utc.hour, utc.min, utc.sec, utc.msec, tag, str); + } else { + pr_info("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, + utc.hour, utc.min, utc.sec, utc.msec, tag, str); + } } /* print buffer as hex string */ @@ -253,12 +257,12 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len) { size_t len = min(data_len, max_len); - unsigned char hexstr[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ - dump2hex(hexstr, data, len); + unsigned char str[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ + dump2hex(str, data, len); /* don't change this printk to mif_debug for print this as level7 */ - return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr, - len == data_len ? "" : " ..."); + return printk(KERN_INFO "%s: %s(%u): %s%s\n", MIF_TAG, tag, data_len, + str, (len == data_len) ? "" : " ..."); } /* flow control CM from CP, it use in serial devices */ @@ -285,7 +289,7 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) break; default: - mif_err("flowctl BACMD: %04X\n", *cmd); + mif_err("flowctl BAD CMD: %04X\n", *cmd); break; } } @@ -411,6 +415,42 @@ void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type) iodevs_for_each(msd, iodev_set_tx_link, ld); } +void mif_netif_stop(struct link_device *ld) +{ + struct io_device *iod; + + if (ld->ipc_version < SIPC_VER_50) + iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); + else + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + + if (iod) + iodevs_for_each(iod->msd, iodev_netif_stop, 0); +} + +void mif_netif_wake(struct link_device *ld) +{ + struct io_device *iod; + + /** + * If ld->suspend_netif_tx is true, this means that there was a SUSPEND + * flow control command from CP so MIF must wait for a RESUME command + * from CP. + */ + if (ld->suspend_netif_tx) { + mif_info("%s: waiting for FLOW_CTRL_RESUME\n", ld->name); + return; + } + + if (ld->ipc_version < SIPC_VER_50) + iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); + else + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + + if (iod) + iodevs_for_each(iod->msd, iodev_netif_wake, 0); +} + /** * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer) * @return: return zero when errors occurred @@ -447,7 +487,7 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, add_timer(timer); } -void mif_print_data(char *buf, int len) +void mif_print_data(const char *buff, int len) { int words = len >> 4; int residue = len - (words << 4); @@ -458,11 +498,8 @@ void mif_print_data(char *buf, int len) /* Make the last line, if ((len % 16) > 0) */ if (residue > 0) { - memset(last, 0, sizeof(last)); - memset(tb, 0, sizeof(tb)); - b = buf + (words << 4); - sprintf(last, "%04X: ", (words << 4)); + b = (char *)buff + (words << 4); for (i = 0; i < residue; i++) { sprintf(tb, "%02x ", b[i]); strcat(last, tb); @@ -474,7 +511,7 @@ void mif_print_data(char *buf, int len) } for (i = 0; i < words; i++) { - b = buf + (i << 4); + b = (char *)buff + (i << 4); mif_err("%04X: " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -488,13 +525,141 @@ void mif_print_data(char *buf, int len) mif_err("%s\n", last); } +void mif_dump2format16(const char *data, int len, char *buff, char *tag) +{ + char *d; + int i; + int words = len >> 4; + int residue = len - (words << 4); + char line[LINE_BUFF_SIZE]; + char tb[8]; + + for (i = 0; i < words; i++) { + memset(line, 0, LINE_BUFF_SIZE); + d = (char *)data + (i << 4); + + if (tag) + sprintf(line, "%s%04X| " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + tag, (i << 4), + d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], + d[12], d[13], d[14], d[15]); + else + sprintf(line, "%04X| " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + (i << 4), + d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], + d[12], d[13], d[14], d[15]); + + strcat(buff, line); + } + + /* Make the last line, if (len % 16) > 0 */ + if (residue > 0) { + memset(line, 0, LINE_BUFF_SIZE); + memset(tb, 0, sizeof(tb)); + d = (char *)data + (words << 4); + + if (tag) + sprintf(line, "%s%04X|", tag, (words << 4)); + else + sprintf(line, "%04X|", (words << 4)); + + for (i = 0; i < residue; i++) { + sprintf(tb, " %02x", d[i]); + strcat(line, tb); + if ((i & 0x3) == 0x3) { + sprintf(tb, " "); + strcat(line, tb); + } + } + strcat(line, "\n"); + + strcat(buff, line); + } +} + +void mif_dump2format4(const char *data, int len, char *buff, char *tag) +{ + char *d; + int i; + int words = len >> 2; + int residue = len - (words << 2); + char line[LINE_BUFF_SIZE]; + char tb[8]; + + for (i = 0; i < words; i++) { + memset(line, 0, LINE_BUFF_SIZE); + d = (char *)data + (i << 2); + + if (tag) + sprintf(line, "%s%04X| %02x %02x %02x %02x\n", + tag, (i << 2), d[0], d[1], d[2], d[3]); + else + sprintf(line, "%04X| %02x %02x %02x %02x\n", + (i << 2), d[0], d[1], d[2], d[3]); + + strcat(buff, line); + } + + /* Make the last line, if (len % 4) > 0 */ + if (residue > 0) { + memset(line, 0, LINE_BUFF_SIZE); + memset(tb, 0, sizeof(tb)); + d = (char *)data + (words << 2); + + if (tag) + sprintf(line, "%s%04X|", tag, (words << 2)); + else + sprintf(line, "%04X|", (words << 2)); + + for (i = 0; i < residue; i++) { + sprintf(tb, " %02x", d[i]); + strcat(line, tb); + } + strcat(line, "\n"); + + strcat(buff, line); + } +} + +void mif_print_dump(const char *data, int len, int width) +{ + char *buff; + + buff = kzalloc(len << 3, GFP_ATOMIC); + if (!buff) { + mif_err("ERR! kzalloc fail\n"); + return; + } + + if (width == 16) + mif_dump2format16(data, len, buff, LOG_TAG); + else + mif_dump2format4(data, len, buff, LOG_TAG); + + pr_info("%s", buff); + + kfree(buff); +} + void print_sipc4_hdlc_fmt_frame(const u8 *psrc) { u8 *frm; /* HDLC Frame */ struct fmt_hdr *hh; /* HDLC Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - u16 hh_len = sizeof(struct fmt_hdr); - u16 fh_len = sizeof(struct sipc_fmt_hdr); + int hh_len = sizeof(struct fmt_hdr); + int fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -531,7 +696,7 @@ void print_sipc4_hdlc_fmt_frame(const u8 *psrc) void print_sipc4_fmt_frame(const u8 *psrc) { struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)psrc; - u16 fh_len = sizeof(struct sipc_fmt_hdr); + int fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -558,8 +723,8 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) u8 *lf; /* Link Frame */ struct sipc5_link_hdr *lh; /* Link Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - u16 lh_len; - u16 fh_len; + int lh_len; + int fh_len; u8 *data; int dlen; @@ -567,10 +732,7 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) /* Point HDLC header and IPC header */ lh = (struct sipc5_link_hdr *)lf; - if (lh->cfg & SIPC5_CTL_FIELD_EXIST) - lh_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - lh_len = SIPC5_MIN_HEADER_SIZE; + lh_len = (u16)sipc5_get_hdr_len((u8 *)lh); fh = (struct sipc_fmt_hdr *)(lf + lh_len); fh_len = sizeof(struct sipc_fmt_hdr); @@ -601,8 +763,8 @@ static void strcat_tcp_header(char *buff, u8 *pkt) { struct tcphdr *tcph = (struct tcphdr *)pkt; int eol; - char line[LINE_BUFF_SIZE]; - char flag_str[32]; + char line[LINE_BUFF_SIZE] = {0, }; + char flag_str[32] = {0, }; /*------------------------------------------------------------------------- @@ -630,21 +792,17 @@ static void strcat_tcp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: TCP:: Src.Port %u, Dst.Port %u\n", - ntohs(tcph->source), ntohs(tcph->dest)); + "%s: TCP:: Src.Port %u, Dst.Port %u\n", + MIF_TAG, ntohs(tcph->source), ntohs(tcph->dest)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", - ntohs(tcph->seq), ntohs(tcph->seq), + "%s: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", + MIF_TAG, ntohs(tcph->seq), ntohs(tcph->seq), ntohs(tcph->ack_seq), ntohs(tcph->ack_seq)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); - memset(flag_str, 0, sizeof(flag_str)); if (tcph->cwr) strcat(flag_str, "CWR "); if (tcph->ece) @@ -664,12 +822,12 @@ static void strcat_tcp_header(char *buff, u8 *pkt) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str); + snprintf(line, LINE_BUFF_SIZE, "%s: TCP:: Flags {%s}\n", + MIF_TAG, flag_str); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n", + "%s: TCP:: Window %u, Checksum 0x%04X, Urgent %u\n", MIF_TAG, ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr)); strcat(buff, line); } @@ -677,7 +835,7 @@ static void strcat_tcp_header(char *buff, u8 *pkt) static void strcat_udp_header(char *buff, u8 *pkt) { struct udphdr *udph = (struct udphdr *)pkt; - char line[LINE_BUFF_SIZE]; + char line[LINE_BUFF_SIZE] = {0, }; /*------------------------------------------------------------------------- @@ -693,41 +851,39 @@ static void strcat_udp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: UDP:: Src.Port %u, Dst.Port %u\n", - ntohs(udph->source), ntohs(udph->dest)); + "%s: UDP:: Src.Port %u, Dst.Port %u\n", + MIF_TAG, ntohs(udph->source), ntohs(udph->dest)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: UDP:: Length %u, Checksum 0x%04X\n", - ntohs(udph->len), ntohs(udph->check)); + "%s: UDP:: Length %u, Checksum 0x%04X\n", + MIF_TAG, ntohs(udph->len), ntohs(udph->check)); strcat(buff, line); if (ntohs(udph->dest) == 53) { - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n"); + snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS query!!!\n", + MIF_TAG); strcat(buff, line); } if (ntohs(udph->source) == 53) { - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n"); + snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS response!!!\n", + MIF_TAG); strcat(buff, line); } } -void print_ip4_packet(u8 *ip_pkt, bool tx) +void print_ip4_packet(const u8 *ip_pkt, bool tx) { char *buff; struct iphdr *iph = (struct iphdr *)ip_pkt; - u8 *pkt = ip_pkt + (iph->ihl << 2); + u8 *pkt = (u8 *)ip_pkt + (iph->ihl << 2); u16 flags = (ntohs(iph->frag_off) & 0xE000); u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF); int eol; - char line[LINE_BUFF_SIZE]; - char flag_str[16]; + char line[LINE_BUFF_SIZE] = {0, }; + char flag_str[16] = {0, }; /*--------------------------------------------------------------------------- IPv4 Header Format @@ -764,32 +920,25 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) if (!buff) return; - - memset(line, 0, LINE_BUFF_SIZE); if (tx) snprintf(line, LINE_BUFF_SIZE, "\n%s\n", TX_SEPARATOR); else snprintf(line, LINE_BUFF_SIZE, "\n%s\n", RX_SEPARATOR); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", - iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len)); + "%s: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", + MIF_TAG, iph->version, (iph->ihl << 2), iph->tos, + ntohs(iph->tot_len)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: ID %u, Fragment Offset %u\n", - ntohs(iph->id), frag_off); + snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: ID %u, Fragment Offset %u\n", + MIF_TAG, ntohs(iph->id), frag_off); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); - memset(flag_str, 0, sizeof(flag_str)); if (flags & IP_CE) strcat(flag_str, "CE "); if (flags & IP_DF) @@ -799,19 +948,18 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str); + snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: Flags {%s}\n", + MIF_TAG, flag_str); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", - iph->ttl, iph->protocol, ntohs(iph->check)); + "%s: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", + MIF_TAG, iph->ttl, iph->protocol, ntohs(iph->check)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", - ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], + "%s: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", + MIF_TAG, ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]); strcat(buff, line); @@ -828,7 +976,6 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) break; } - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); @@ -837,7 +984,7 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) kfree(buff); } -bool is_dns_packet(u8 *ip_pkt) +bool is_dns_packet(const u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2)); @@ -852,7 +999,7 @@ bool is_dns_packet(u8 *ip_pkt) return false; } -bool is_syn_packet(u8 *ip_pkt) +bool is_syn_packet(const u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2)); @@ -867,124 +1014,248 @@ bool is_syn_packet(u8 *ip_pkt) return false; } -int memcmp16_to_io(const void __iomem *to, void *from, int size) +/** + * mif_register_isr + * @irq: IRQ number for a DPRAM interrupt + * @isr: function pointer to an interrupt service routine + * @flags: set of interrupt flags + * @name: name of the interrupt + * @data: pointer to a data for @isr + * + * Registers the ISR for the IRQ number. + */ +int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, + const char *name, void *data) { - u16 *d = (u16 *)to; - u16 *s = (u16 *)from; - int count = size >> 1; - int diff = 0; - int i; - u16 d1; - u16 s1; - - for (i = 0; i < count; i++) { - d1 = ioread16(d); - s1 = *s; - if (d1 != s1) { - diff++; - mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); - } - d++; - s++; + int ret; + + ret = request_irq(irq, isr, flags, name, data); + if (ret) { + mif_err("%s: ERR! request_irq fail (err %d)\n", name, ret); + return ret; } - return diff; + ret = enable_irq_wake(irq); + if (ret) + mif_err("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); + + mif_err("%s (#%d) handler registered\n", name, irq); + + return 0; } -int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size) +int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes) { - u8 __iomem *dst; - int i; + u16 i; + u16 words = bytes >> 1; + u16 __iomem *dst = (u16 __iomem *)start; u16 val; + int err_cnt = 0; - mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size); + mif_err("%s: start 0x%p, bytes %d\n", dp_name, start, bytes); - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16((i & 0xFFFF), dst); - dst += 2; + mif_err("%s: 0/6 stage ...\n", dp_name); + for (i = 1; i <= 100; i++) { + iowrite16(0x1234, dst); + val = ioread16(dst); + if (val != 0x1234) { + mif_err("%s: [0x0000] read 0x%04X != written 0x1234 " + "(try# %d)\n", dp_name, val, i); + err_cnt++; + } + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 1/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0, dst); + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); - if (val != (i & 0xFFFF)) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n", - dp_name, i, val, (i & 0xFFFF)); - return -EINVAL; + if (val != 0x0000) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x0000\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + mif_err("%s: 2/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0xFFFF, dst); + dst++; + } + + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + val = ioread16(dst); + if (val != 0xFFFF) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0xFFFF\n", dp_name, i, val); + err_cnt++; + } + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 3/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0x00FF, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0x00FF) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x00FF\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 4/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0x0FF0, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0x0FF0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x0FF0\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + mif_err("%s: 5/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0xFF00, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0xFF00) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0xFF00\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0, dst); - dst += 2; + mif_err("%s: 6/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16((i & 0xFFFF), dst); + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); - if (val != 0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0\n", - dp_name, i, val); - return -EINVAL; + if (val != (i & 0xFFFF)) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x%04X\n", dp_name, i, val, (i & 0xFFFF)); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: PASS!!!\n", dp_name); + + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0, dst); + dst++; } - mif_info("%s: PASS!!!\n", dp_name); return 0; } +struct file *mif_open_file(const char *path) +{ + struct file *fp; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + fp = filp_open(path, O_RDWR|O_CREAT|O_APPEND, 0666); + + set_fs(old_fs); + + if (IS_ERR(fp)) + return NULL; + + return fp; +} + +void mif_save_file(struct file *fp, const char *buff, size_t size) +{ + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + ret = fp->f_op->write(fp, buff, size, &fp->f_pos); + if (ret < 0) + mif_err("ERR! write fail\n"); + + set_fs(old_fs); +} + +void mif_close_file(struct file *fp) +{ + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + filp_close(fp, NULL); + + set_fs(old_fs); +} + diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h index 7225ec2..15edb43 100644 --- a/drivers/misc/modem_if/modem_utils.h +++ b/drivers/misc/modem_if/modem_utils.h @@ -16,6 +16,7 @@ #define __MODEM_UTILS_H__ #include +#include "modem_prj.h" #define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type)) @@ -26,6 +27,8 @@ #define MAX_IPC_SKB_SIZE 4096 #define MAX_LOG_SIZE 64 +#define MIF_TAG "mif" + #define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE) #define MIF_ID_SIZE sizeof(enum mif_log_id) @@ -96,7 +99,19 @@ struct mif_time_block { char buff[MAX_TIM_LOG_SIZE]; }; -int mif_dump_dpram(struct io_device *); +static inline int ns2us(long ns) +{ + return (ns > 0) ? (ns / 1000) : 0; +} + +static inline int ns2ms(long ns) +{ + return (ns > 0) ? (ns / 1000000) : 0; +} + +void ts2utc(struct timespec *ts, struct utc_time *utc); +void get_utc_time(struct utc_time *utc); + int mif_dump_log(struct modem_shared *, struct io_device *); #define mif_irq_log(msd, map, data, len) \ @@ -141,7 +156,7 @@ static inline unsigned int countbits(unsigned int n) } /* print IPC message as hex string with UTC time */ -int pr_ipc(const char *str, const char *data, size_t len); +void pr_ipc(int level, const char *tag, const char *data, size_t len); /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, @@ -156,10 +171,13 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, pr_buffer(tag, (char *)((urb)->transfer_buffer), \ (size_t)((urb)->actual_length), (size_t)16) +/* Stop/wake all TX queues in network interfaces */ +void mif_netif_stop(struct link_device *ld); +void mif_netif_wake(struct link_device *ld); + /* flow control CMD from CP, it use in serial devices */ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len); - /* get iod from tree functions */ struct io_device *get_iod_with_format(struct modem_shared *msd, @@ -204,12 +222,14 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, void (*function)(unsigned long), unsigned long data); /* debug helper functions for sipc4, sipc5 */ -void mif_print_data(char *buf, int len); +void mif_print_data(const char *buff, int len); +void mif_dump2format16(const char *data, int len, char *buff, char *tag); +void mif_dump2format4(const char *data, int len, char *buff, char *tag); +void mif_print_dump(const char *data, int len, int width); void print_sipc4_hdlc_fmt_frame(const u8 *psrc); void print_sipc4_fmt_frame(const u8 *psrc); void print_sipc5_link_fmt_frame(const u8 *psrc); - /*--------------------------------------------------------------------------- IPv4 Header Format @@ -282,23 +302,17 @@ void print_sipc5_link_fmt_frame(const u8 *psrc); -------------------------------------------------------------------------*/ #define UDP_HDR_SIZE 8 -void print_ip4_packet(u8 *ip_pkt, bool tx); -bool is_dns_packet(u8 *ip_pkt); -bool is_syn_packet(u8 *ip_pkt); +void print_ip4_packet(const u8 *ip_pkt, bool tx); +bool is_dns_packet(const u8 *ip_pkt); +bool is_syn_packet(const u8 *ip_pkt); -int memcmp16_to_io(const void __iomem *to, void *from, int size); -int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size); +int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, + const char *name, void *data); +int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes); -static const inline char *get_dev_name(int dev) -{ - if (dev == IPC_FMT) - return "FMT"; - else if (dev == IPC_RAW) - return "RAW"; - else if (dev == IPC_RFS) - return "RFS"; - else - return "NONE"; -} +struct file *mif_open_file(const char *path); +void mif_save_file(struct file *fp, const char *buff, size_t size); +void mif_close_file(struct file *fp); #endif/*__MODEM_UTILS_H__*/ + diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h index b5ec61b..8799246 100644 --- a/drivers/misc/modem_if/modem_variation.h +++ b/drivers/misc/modem_if/modem_variation.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -61,12 +60,30 @@ DECLARE_MODEM_INIT(cbp72); DECLARE_MODEM_INIT_DUMMY(cbp72) #endif +#ifdef CONFIG_CDMA_MODEM_CBP82 +DECLARE_MODEM_INIT(cbp82); +#else +DECLARE_MODEM_INIT_DUMMY(cbp82) +#endif + +#ifdef CONFIG_LTE_MODEM_CMC220 +DECLARE_MODEM_INIT(cmc220); +#else +DECLARE_MODEM_INIT_DUMMY(cmc220) +#endif + #ifdef CONFIG_LTE_MODEM_CMC221 DECLARE_MODEM_INIT(cmc221); #else DECLARE_MODEM_INIT_DUMMY(cmc221) #endif +#ifdef CONFIG_UMTS_MODEM_SS222 +DECLARE_MODEM_INIT(ss222); +#else +DECLARE_MODEM_INIT_DUMMY(ss222) +#endif + #ifdef CONFIG_CDMA_MODEM_MDM6600 DECLARE_MODEM_INIT(mdm6600); #else @@ -79,6 +96,12 @@ DECLARE_MODEM_INIT(esc6270); DECLARE_MODEM_INIT_DUMMY(esc6270) #endif +#ifdef CONFIG_CDMA_MODEM_QSC6085 +DECLARE_MODEM_INIT(qsc6085); +#else +DECLARE_MODEM_INIT_DUMMY(qsc6085) +#endif + #ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 DECLARE_MODEM_INIT(sprd8803); #else @@ -94,6 +117,18 @@ DECLARE_LINK_INIT(mipi); DECLARE_LINK_INIT_DUMMY(mipi) #endif +#ifdef CONFIG_LINK_DEVICE_USB +DECLARE_LINK_INIT(usb); +#else +DECLARE_LINK_INIT_DUMMY(usb) +#endif + +#ifdef CONFIG_LINK_DEVICE_HSIC +DECLARE_LINK_INIT(hsic); +#else +DECLARE_LINK_INIT_DUMMY(hsic) +#endif + #ifdef CONFIG_LINK_DEVICE_DPRAM DECLARE_LINK_INIT(dpram); #else @@ -106,53 +141,52 @@ DECLARE_LINK_INIT(pld); DECLARE_LINK_INIT_DUMMY(pld) #endif -#ifdef CONFIG_LINK_DEVICE_SPI -DECLARE_LINK_INIT(spi); -#else -DECLARE_LINK_INIT_DUMMY(spi) -#endif - -#ifdef CONFIG_LINK_DEVICE_USB -DECLARE_LINK_INIT(usb); +#ifdef CONFIG_LINK_DEVICE_C2C +DECLARE_LINK_INIT(c2c); #else -DECLARE_LINK_INIT_DUMMY(usb) +DECLARE_LINK_INIT_DUMMY(c2c) #endif -#ifdef CONFIG_LINK_DEVICE_HSIC -DECLARE_LINK_INIT(hsic); +#ifdef CONFIG_LINK_DEVICE_SHMEM +DECLARE_LINK_INIT(shmem); #else -DECLARE_LINK_INIT_DUMMY(hsic) +DECLARE_LINK_INIT_DUMMY(shmem) #endif -#ifdef CONFIG_LINK_DEVICE_C2C -DECLARE_LINK_INIT(c2c); +#ifdef CONFIG_LINK_DEVICE_SPI +DECLARE_LINK_INIT(spi); #else -DECLARE_LINK_INIT_DUMMY(c2c) +DECLARE_LINK_INIT_DUMMY(spi) #endif typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *); -static modem_init_call modem_init_func[] = { - MODEM_INIT_CALL(xmm6260), - MODEM_INIT_CALL(xmm6262), - MODEM_INIT_CALL(cbp71), - MODEM_INIT_CALL(cbp72), - MODEM_INIT_CALL(cmc221), - MODEM_INIT_CALL(mdm6600), - MODEM_INIT_CALL(esc6270), - MODEM_INIT_CALL(sprd8803), - MODEM_INIT_CALL(dummy), +static modem_init_call modem_init_func[MAX_MODEM_TYPE] = { + [IMC_XMM6260] = MODEM_INIT_CALL(xmm6260), + [IMC_XMM6262] = MODEM_INIT_CALL(xmm6262), + [VIA_CBP71] = MODEM_INIT_CALL(cbp71), + [VIA_CBP72] = MODEM_INIT_CALL(cbp72), + [VIA_CBP82] = MODEM_INIT_CALL(cbp82), + [SEC_CMC220] = MODEM_INIT_CALL(cmc220), + [SEC_CMC221] = MODEM_INIT_CALL(cmc221), + [SEC_SS222] = MODEM_INIT_CALL(ss222), + [QC_MDM6600] = MODEM_INIT_CALL(mdm6600), + [QC_ESC6270] = MODEM_INIT_CALL(esc6270), + [QC_QSC6085] = MODEM_INIT_CALL(qsc6085), + [SPRD_SC8803] = MODEM_INIT_CALL(sprd8803), + [DUMMY] = MODEM_INIT_CALL(dummy), }; typedef struct link_device *(*link_init_call)(struct platform_device *); -static link_init_call link_init_func[] = { - LINK_INIT_CALL(undefined), - LINK_INIT_CALL(mipi), - LINK_INIT_CALL(dpram), - LINK_INIT_CALL(spi), - LINK_INIT_CALL(usb), - LINK_INIT_CALL(hsic), - LINK_INIT_CALL(c2c), - LINK_INIT_CALL(pld), +static link_init_call link_init_func[LINKDEV_MAX] = { + [LINKDEV_UNDEFINED] = LINK_INIT_CALL(undefined), + [LINKDEV_MIPI] = LINK_INIT_CALL(mipi), + [LINKDEV_USB] = LINK_INIT_CALL(usb), + [LINKDEV_HSIC] = LINK_INIT_CALL(hsic), + [LINKDEV_DPRAM] = LINK_INIT_CALL(dpram), + [LINKDEV_PLD] = LINK_INIT_CALL(pld), + [LINKDEV_C2C] = LINK_INIT_CALL(c2c), + [LINKDEV_SHMEM] = LINK_INIT_CALL(shmem), + [LINKDEV_SPI] = LINK_INIT_CALL(spi), }; static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata) diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c index 28f95f7..21adf9e 100644 --- a/drivers/misc/modem_if/sipc4_io_device.c +++ b/drivers/misc/modem_if/sipc4_io_device.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_io_device.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -27,9 +25,6 @@ #include #include -#ifdef CONFIG_LINK_DEVICE_C2C -#include -#endif #include "modem_prj.h" #include "modem_utils.h" @@ -40,7 +35,8 @@ * So, give restriction to allocation size below 1 page to prevent * big pages broken. */ -#define MAX_RXDATA_SIZE 0x0E00 /* 4 * 1024 - 512 */ +#define MAX_RXDATA_SIZE (4096 - 512) +#define MAX_BOOTDATA_SIZE 0x4008 /* EBL package format*/ #define MAX_MULTI_FMT_SIZE 0x4000 /* 16 * 1024 */ static const char hdlc_start[1] = { HDLC_START }; @@ -250,22 +246,6 @@ static int rx_hdlc_head_check(struct io_device *iod, struct link_device *ld, hdr->start = HDLC_START; hdr->len = 0; - /* debug print */ - switch (iod->format) { - case IPC_FMT: - case IPC_RAW: - case IPC_MULTI_RAW: - case IPC_RFS: - /* TODO: print buf... */ - break; - - case IPC_CMD: - case IPC_BOOT: - case IPC_RAMDUMP: - default: - break; - } - buf += len; done_len += len; rest -= len; /* rest, call by value */ @@ -409,12 +389,6 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, rcvd); - print_sipc4_fmt_frame(data); - mif_err("\n"); -#endif skb_queue_tail(&iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -449,12 +423,6 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_info("The Last (ID %d, %d bytes received)\n", id, skb->len); -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_fmt_frame(skb->data); - mif_err("\n"); -#endif skb_queue_tail(&iod->sk_rx_q, skb); iod->skb[id] = NULL; mif_info("wake up wq of %s\n", iod->name); @@ -491,12 +459,6 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, rcvd); - print_sipc4_fmt_frame(data); - mif_err("\n"); -#endif skb_queue_tail(&real_iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -530,12 +492,6 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_err("The Last (ID %d, %d bytes received)\n", id, skb->len); -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_fmt_frame(skb->data); - mif_err("\n"); -#endif skb_queue_tail(&real_iod->sk_rx_q, skb); real_iod->skb[id] = NULL; mif_info("wake up wq of %s\n", real_iod->name); @@ -819,55 +775,6 @@ exit: return err; } -static int rx_rfs_packet(struct io_device *iod, struct link_device *ld, - const char *data, unsigned size) -{ - int err = 0; - int pad = 0; - int rcvd = 0; - struct sk_buff *skb; - - if (data[0] != HDLC_START) { - mif_err("Dropping RFS packet ... " - "size = %d, start = %02X %02X %02X %02X\n", - size, - data[0], data[1], data[2], data[3]); - return -EINVAL; - } - - if (data[size-1] != HDLC_END) { - for (pad = 1; pad < 4; pad++) - if (data[(size-1)-pad] == HDLC_END) - break; - - if (pad >= 4) { - char *b = (char *)data; - unsigned sz = size; - mif_err("size %d, No END_FLAG!!!\n", size); - mif_err("end = %02X %02X %02X %02X\n", - b[sz-4], b[sz-3], b[sz-2], b[sz-1]); - return -EINVAL; - } else { - mif_info("padding = %d\n", pad); - } - } - - skb = rx_alloc_skb(size, iod, ld); - if (unlikely(!skb)) { - mif_err("alloc_skb fail\n"); - return -ENOMEM; - } - - /* copy the RFS haeder to skb->data */ - rcvd = size - sizeof(hdlc_start) - sizeof(hdlc_end) - pad; - memcpy(skb_put(skb, rcvd), ((char *)data + sizeof(hdlc_start)), rcvd); - - fragdata(iod, ld)->skb_recv = skb; - err = rx_iodev_skb(fragdata(iod, ld)->skb_recv); - - return err; -} - /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) @@ -891,14 +798,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, */ switch (iod->format) { - case IPC_RFS: -#ifdef CONFIG_IPC_CMC22x_OLD_RFS - err = rx_rfs_packet(iod, ld, data, len); - return err; -#endif - case IPC_FMT: case IPC_RAW: + case IPC_RFS: case IPC_MULTI_RAW: if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); @@ -961,12 +863,17 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - iod->mc->phone_state = state; - mif_err("modem state changed. (iod: %s, state: %d)\n", - iod->name, state); + struct modem_ctl *mc = iod->mc; + int old_state = mc->phone_state; - if ((state == STATE_CRASH_RESET) || (state == STATE_CRASH_EXIT) - || (state == STATE_NV_REBUILDING)) + if (old_state != state) { + mc->phone_state = state; + mif_err("%s state changed (%s -> %s)\n", mc->name, + get_cp_state_str(old_state), get_cp_state_str(state)); + } + + if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || + state == STATE_NV_REBUILDING) wake_up(&iod->wq); } @@ -977,10 +884,24 @@ static void io_dev_modem_state_changed(struct io_device *iod, */ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) { + +#if defined(CONFIG_MACH_KONA) && defined(CONFIG_UMTS_MODEM_XMM6262) + mif_err("modem_current_state is %d\n", iod->mc->phone_state); +#endif + if (atomic_read(&iod->opened) == 0) { - mif_err("iod is not opened: %s\n", - iod->name); - } else if (iod->mc->sim_state.online == sim_online) { + mif_err("iod is not opened: %s\n", iod->name); + /* update latest sim status */ + iod->mc->sim_state.online = sim_online; + } +#if defined(CONFIG_LINK_DEVICE_HSIC) && defined(CONFIG_UMTS_MODEM_XMM6262) // fixed modem unknown issue (kina 3G) + else if (iod->mc->phone_state == STATE_BOOTING) { + mif_err("modem_current_state is STATE_BOOTING\n"); + /* update latest sim status */ + iod->mc->sim_state.online = sim_online; + } +#endif + else if (iod->mc->sim_state.online == sim_online) { mif_err("sim state not changed.\n"); } else { iod->mc->sim_state.online = sim_online; @@ -995,6 +916,7 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) } } + static int misc_open(struct inode *inode, struct file *filp) { struct io_device *iod = to_io_device(filp->private_data); @@ -1078,32 +1000,32 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case IOCTL_MODEM_ON: - mif_debug("misc_ioctl : IOCTL_MODEM_ON\n"); + mif_debug("%s: IOCTL_MODEM_ON\n", iod->name); return iod->mc->ops.modem_on(iod->mc); case IOCTL_MODEM_OFF: - mif_debug("misc_ioctl : IOCTL_MODEM_OFF\n"); + mif_debug("%s: IOCTL_MODEM_OFF\n", iod->name); return iod->mc->ops.modem_off(iod->mc); case IOCTL_MODEM_RESET: - mif_debug("misc_ioctl : IOCTL_MODEM_RESET\n"); + mif_debug("%s: IOCTL_MODEM_RESET\n", iod->name); return iod->mc->ops.modem_reset(iod->mc); case IOCTL_MODEM_BOOT_ON: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_ON\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); return iod->mc->ops.modem_boot_on(iod->mc); case IOCTL_MODEM_BOOT_OFF: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_OFF\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); return iod->mc->ops.modem_boot_off(iod->mc); /* TODO - will remove this command after ril updated */ case IOCTL_MODEM_BOOT_DONE: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); return 0; case IOCTL_MODEM_STATUS: - mif_debug("misc_ioctl : IOCTL_MODEM_STATUS\n"); + mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); p_state = iod->mc->phone_state; if ((p_state == STATE_CRASH_RESET) || @@ -1126,7 +1048,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return p_state; case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_SUSPEND\n"); + mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1134,8 +1056,16 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) iodevs_for_each(iod->msd, iodev_netif_stop, 0); return 0; + case IOCTL_MODEM_DL_START: + mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); + return ld->dload_start(ld, iod); + + case IOCTL_MODEM_FW_UPDATE: + mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); + return ld->firm_update(ld, iod, arg); + case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_RESUME\n"); + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1144,21 +1074,29 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_START: - mif_err("misc_ioctl : IOCTL_MODEM_DUMP_START\n"); + mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + + case IOCTL_MODEM_RAMDUMP_START: + mif_err("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); return ld->dump_start(ld, iod); case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("misc_ioctl : IOCTL_MODEM_DUMP_UPDATE\n"); + mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); return ld->dump_update(ld, iod, arg); + case IOCTL_MODEM_RAMDUMP_STOP: + mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); + return ld->dump_finish(ld, iod, arg); + case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_debug("misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n"); + mif_debug("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); if (iod->mc->ops.modem_force_crash_exit) return iod->mc->ops.modem_force_crash_exit(iod->mc); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: - mif_err("misc_ioctl : IOCTL_MODEM_CP_UPLOAD\n"); + mif_err("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), (void __user *)arg, MAX_CPINFO_SIZE) != 0) panic("CP Crash"); @@ -1167,12 +1105,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_RESET: - mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n"); + mif_err("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); return iod->mc->ops.modem_dump_reset(iod->mc); #if defined(CONFIG_SEC_DUAL_MODEM_MODE) case IOCTL_MODEM_SWITCH_MODEM: - mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n"); + mif_err("%s: IOCTL_MODEM_SWITCH_MODEM\n", iod->name); iod->mc->phone_state = STATE_MODEM_SWITCH; wake_up(&iod->wq); return 0; @@ -1188,20 +1126,6 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_dump_log(iod->mc->msd, iod); return 0; - case IOCTL_MIF_DPRAM_DUMP: -#ifdef CONFIG_LINK_DEVICE_DPRAM - if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { - size = iod->mc->mdm_data->dpram_ctl->dp_size; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) - return -EFAULT; - mif_dump_dpram(iod); - return 0; - } -#endif - return -EINVAL; - default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1209,12 +1133,48 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_err("misc_ioctl : ioctl 0x%X is not defined.\n", cmd); + mif_err("%s: ioctl 0x%X is not defined\n", iod->name, cmd); return -EINVAL; } return 0; } +static size_t _boot_write(struct io_device *iod, const char __user *buf, + size_t count) +{ + int rest_len = count, frame_len = 0; + char *cur = (char *)buf; + struct sk_buff *skb = NULL; + struct link_device *ld = get_current_link(iod); + int ret; + + while (rest_len) { + frame_len = min(rest_len, MAX_BOOTDATA_SIZE); + skb = alloc_skb(frame_len, GFP_KERNEL); + if (!skb) { + mif_err("fail alloc skb (%d)\n", __LINE__); + return -ENOMEM; + } + if (copy_from_user( + skb_put(skb, frame_len), cur, frame_len) != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + rest_len -= frame_len; + cur += frame_len; + + skbpriv(skb)->iod = iod; + skbpriv(skb)->ld = ld; + + ret = ld->send(ld, iod, skb); + if (ret < 0) { + dev_kfree_skb_any(skb); + return ret; + } + } + return count; +} + static ssize_t misc_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { @@ -1237,6 +1197,10 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb = alloc_skb(frame_len, GFP_KERNEL); if (!skb) { + if (frame_len > MAX_BOOTDATA_SIZE && iod->format == IPC_BOOT) { + mif_info("large alloc fail\n"); + return _boot_write(iod, buf, count); + } mif_err("fail alloc skb (%d)\n", __LINE__); return -ENOMEM; } @@ -1278,31 +1242,6 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb_put(skb, calc_padding_size(iod, ld, skb->len)); -#if 0 - if (iod->format == IPC_FMT) { - mif_err("\n<%s> Tx HDLC FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_hdlc_fmt_frame(skb->data); - mif_err("\n"); - } -#endif -#if 0 - if (iod->format == IPC_RAW) { - mif_err("\n<%s> Tx HDLC RAW frame (len %d)\n", - iod->name, skb->len); - mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); - mif_err("\n"); - } -#endif -#if 0 - if (iod->format == IPC_RFS) { - mif_err("\n<%s> Tx HDLC RFS frame (len %d)\n", - iod->name, skb->len); - mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); - mif_err("\n"); - } -#endif - /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data */ @@ -1409,43 +1348,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return pktsize; } -#ifdef CONFIG_LINK_DEVICE_C2C -static int misc_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int r = 0; - unsigned long size = 0; - unsigned long pfn = 0; - unsigned long offset = 0; - struct io_device *iod = (struct io_device *)filp->private_data; - - if (!vma) - return -EFAULT; - - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { - mif_err("offset + size > C2C_CP_RGN_SIZE\n"); - return -EINVAL; - } - - /* Set the noncacheable property to the region */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_RESERVED | VM_IO; - - pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); - r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); - if (r) { - mif_err("Failed in remap_pfn_range()!!!\n"); - return -EAGAIN; - } - - mif_err("VA = 0x%08lx, offset = 0x%lx, size = %lu\n", - vma->vm_start, offset, size); - - return 0; -} -#endif - static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1454,9 +1356,6 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, -#ifdef CONFIG_LINK_DEVICE_C2C - .mmap = misc_mmap, -#endif }; static int vnet_open(struct net_device *ndev) diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c index 59e2de9..542b482 100644 --- a/drivers/misc/modem_if/sipc4_modem.c +++ b/drivers/misc/modem_if/sipc4_modem.c @@ -328,9 +328,22 @@ static int modem_resume(struct device *pdev) return 0; } +#ifdef CONFIG_FAST_BOOT +static void modem_complete(struct device *pdev) +{ + struct modem_ctl *mc = dev_get_drvdata(pdev); + + if (mc->modem_complete) + mc->modem_complete(mc); +} +#endif + static const struct dev_pm_ops modem_pm_ops = { .suspend = modem_suspend, .resume = modem_resume, +#ifdef CONFIG_FAST_BOOT + .complete = modem_complete, +#endif }; static struct platform_driver modem_driver = { diff --git a/drivers/misc/modem_if/sipc5_common.c b/drivers/misc/modem_if/sipc5_common.c new file mode 100644 index 0000000..277f669 --- /dev/null +++ b/drivers/misc/modem_if/sipc5_common.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem_prj.h" +#include "modem_utils.h" + +/** + * sipc5_start_valid + * @cfg: configuration field of an SIPC5 link frame + * + * Returns TRUE if the start (configuration field) of an SIPC5 link frame + * is valid or returns FALSE if it is not valid. + * + */ +bool sipc5_start_valid(u8 *frm) +{ + return (*frm & SIPC5_START_MASK) == SIPC5_START_MASK; +} + +bool sipc5_padding_exist(u8 *frm) +{ + return (*frm & SIPC5_PADDING_EXIST) ? true : false; +} + +bool sipc5_multi_frame(u8 *frm) +{ + return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_CTL_FIELD_MASK; +} + +bool sipc5_ext_len(u8 *frm) +{ + return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_EXT_LENGTH_MASK; +} + +/** + * sipc5_get_hdr_len + * @cfg: configuration field of an SIPC5 link frame + * + * Returns the length of SIPC5 link layer header in an SIPC5 link frame + * + */ +int sipc5_get_hdr_len(u8 *frm) +{ + if (*frm & SIPC5_EXT_FIELD_EXIST) { + if (*frm & SIPC5_CTL_FIELD_EXIST) + return SIPC5_HEADER_SIZE_WITH_CTL_FLD; + else + return SIPC5_HEADER_SIZE_WITH_EXT_LEN; + } else { + return SIPC5_MIN_HEADER_SIZE; + } +} + +/** + * sipc5_get_ch_id + * @frm: pointer to an SIPC5 frame + * + * Returns the channel ID in an SIPC5 link frame + * + */ +u8 sipc5_get_ch_id(u8 *frm) +{ + return *(frm + SIPC5_CH_ID_OFFSET); +} + +/** + * sipc5_get_ctrl_field + * @frm: pointer to an SIPC5 frame + * + * Returns the control field in an SIPC5 link frame + * + */ +u8 sipc5_get_ctrl_field(u8 *frm) +{ + return *(frm + SIPC5_CTL_OFFSET); +} + +/** + * sipc5_get_frame_len + * @frm: pointer to an SIPC5 link frame + * + * Returns the length of an SIPC5 link frame + * + */ +int sipc5_get_frame_len(u8 *frm) +{ + u8 cfg = frm[0]; + u16 *sz16 = (u16 *)(frm + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(frm + SIPC5_LEN_OFFSET); + + if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { + if (cfg & SIPC5_CTL_FIELD_EXIST) + return (int)(*sz16); + else + return (int)(*sz32); + } else { + return (int)(*sz16); + } +} + +/** + * sipc5_calc_padding_size + * @len: length of an SIPC5 link frame + * + * Returns the padding size for an SIPC5 link frame + * + */ +int sipc5_calc_padding_size(int len) +{ + int residue = len & 0x3; + return residue ? (4 - residue) : 0; +} + +/** + * sipc5_get_total_len + * @frm: pointer to an SIPC5 link frame + * + * Returns the total length of an SIPC5 link frame with padding + * + */ +int sipc5_get_total_len(u8 *frm) +{ + int len = sipc5_get_frame_len(frm); + int pad = sipc5_padding_exist(frm) ? sipc5_calc_padding_size(len) : 0; + return len + pad; +} + +/** + * sipc5_build_config + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @count: length of the data to be transmitted + * + * Builds a config value for an SIPC5 link frame header + * + * Returns the config value for the header or 0 for non-SIPC formats + */ +u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count) +{ + u8 cfg = SIPC5_START_MASK; + + if (iod->format > IPC_MULTI_RAW && iod->id == 0) + return 0; + + if (ld->aligned) + cfg |= SIPC5_PADDING_EXIST; + +#if 0 + if ((count + SIPC5_MIN_HEADER_SIZE) > ld->mtu[dev]) + cfg |= SIPC5_CTL_FIELD_MASK; + else +#endif + if ((count + SIPC5_MIN_HEADER_SIZE) > 0xFFFF) + cfg |= SIPC5_EXT_LENGTH_MASK; + + return cfg; +} + +/** + * sipc5_build_header + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @buff: pointer to a buffer in which an SIPC5 link frame header will be stored + * @cfg: value for the config field in the header + * @ctrl: value for the control field in the header + * @count: length of data in the SIPC5 link frame to be transmitted + * + * Builds the link layer header of an SIPC5 frame + */ +void sipc5_build_header(struct io_device *iod, struct link_device *ld, + u8 *buff, u8 cfg, u8 ctrl, u32 count) +{ + u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + u32 hdr_len = sipc5_get_hdr_len(&cfg); + + /* Store the config field and the channel ID field */ + buff[SIPC5_CONFIG_OFFSET] = cfg; + buff[SIPC5_CH_ID_OFFSET] = iod->id; + + /* Store the frame length field */ + if (sipc5_ext_len(buff)) + *sz32 = (u32)(hdr_len + count); + else + *sz16 = (u16)(hdr_len + count); + + /* Store the control field */ + if (sipc5_multi_frame(buff)) + buff[SIPC5_CTL_OFFSET] = ctrl; +} + +/** + * std_udl_get_cmd + * @frm: pointer to an SIPC5 link frame + * + * Returns the standard BOOT/DUMP (STD_UDL) command in an SIPC5 BOOT/DUMP frame. + */ +u32 std_udl_get_cmd(u8 *frm) +{ + u8 *cmd = frm + sipc5_get_hdr_len(frm); + return *((u32 *)cmd); +} + +/** + * std_udl_with_payload + * @cmd: standard BOOT/DUMP command + * + * Returns true if the STD_UDL command has a payload. + */ +bool std_udl_with_payload(u32 cmd) +{ + u32 mask = cmd & STD_UDL_STEP_MASK; + return (mask && mask < STD_UDL_CRC) ? true : false; +} + diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c index a9932c1..a0174fd 100644 --- a/drivers/misc/modem_if/sipc5_io_device.c +++ b/drivers/misc/modem_if/sipc5_io_device.c @@ -1,5 +1,4 @@ -/* /linux/drivers/misc/modem_if/sipc5_io_device.c - * +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -24,11 +23,9 @@ #include #include #include +#include #include -#ifdef CONFIG_LINK_DEVICE_C2C -#include -#endif #include "modem_prj.h" #include "modem_utils.h" @@ -104,7 +101,7 @@ static void iodev_showtxlink(struct io_device *iod, void *args) struct link_device *ld = get_current_link(iod); if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) - *p += sprintf(*p, "%s: %s\n", iod->name, ld->name); + *p += sprintf(*p, "%s<->%s\n", iod->name, ld->name); } static ssize_t show_txlink(struct device *dev, @@ -130,181 +127,107 @@ static ssize_t store_txlink(struct device *dev, static struct device_attribute attr_txlink = __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink); -/** - * rx_check_frame_cfg - * @cfg: configuration field of a link layer header - * @frm: pointer to the sipc5_frame_data buffer - * - * 1) Checks whether or not an extended field exists - * 2) Calculates the length of a link layer header - * - * Returns the size of a link layer header - * - * Must be invoked only when the configuration field of the link layer header - * is validated with sipc5_start_valid() function - */ -static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm) +static int netif_flow_ctrl(struct link_device *ld, struct sk_buff *skb) { - frm->config = cfg; - - if (likely(cfg & SIPC5_PADDING_EXIST)) - frm->padding = true; - - if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { - if (cfg & SIPC5_CTL_FIELD_EXIST) { - frm->ctl_fld = true; - frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; - } else { - frm->ext_len = true; - frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN; - } + u8 cmd = skb->data[0]; + + if (cmd == FLOW_CTRL_SUSPEND) { + if (ld->suspend_netif_tx) + goto exit; + ld->suspend_netif_tx = true; + mif_netif_stop(ld); + mif_info("%s: FLOW_CTRL_SUSPEND\n", ld->name); + } else if (cmd == FLOW_CTRL_RESUME) { + if (!ld->suspend_netif_tx) + goto exit; + ld->suspend_netif_tx = false; + mif_netif_wake(ld); + mif_info("%s: FLOW_CTRL_RESUME\n", ld->name); } else { - frm->hdr_len = SIPC5_MIN_HEADER_SIZE; + mif_info("%s: ERR! invalid command %02X\n", ld->name, cmd); } - return frm->hdr_len; -} - -/** - * rx_build_meta_data - * @ld: pointer to the link device - * @frm: pointer to the sipc5_frame_data buffer - * - * Fills each field of sipc5_frame_data from a link layer header - * 1) Extracts the channel ID - * 2) Calculates the length of a link layer frame - * 3) Extracts a control field if exists - * 4) Calculates the length of an IPC message packet in the link layer frame - * - */ -static void rx_build_meta_data(struct link_device *ld, - struct sipc5_frame_data *frm) -{ - u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET); - - frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET]; - - if (unlikely(frm->ext_len)) - frm->len = *sz32; - else - frm->len = *sz16; - - if (unlikely(frm->ctl_fld)) - frm->control = frm->hdr[SIPC5_CTL_OFFSET]; - - frm->data_len = frm->len - frm->hdr_len; - - mif_debug("%s: FRM ch:%d len:%d ctl:%02X data.len:%d\n", - ld->name, frm->ch_id, frm->len, frm->control, frm->data_len); +exit: + dev_kfree_skb_any(skb); + return 0; } -/** - * tx_build_link_header - * @frm: pointer to the sipc5_frame_data buffer - * @iod: pointer to the IO device - * @ld: pointer to the link device - * @count: length of the data to be transmitted - * - * Builds the meta data for an SIPC5 frame and the link layer header of it - * Returns the link layer header length for an SIPC5 frame or 0 for other frame - */ -static unsigned tx_build_link_header(struct sipc5_frame_data *frm, - struct io_device *iod, struct link_device *ld, ssize_t count) +static inline int queue_skb_to_iod(struct sk_buff *skb, struct io_device *iod) { - u8 *buff = frm->hdr; - u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + struct sk_buff_head *rxq = &iod->sk_rx_q; - memset(frm, 0, sizeof(struct sipc5_frame_data)); + skb_queue_tail(rxq, skb); - if (iod->format == IPC_CMD || - iod->format == IPC_BOOT || - iod->format == IPC_RAMDUMP) { - frm->len = count; + if (iod->format < IPC_MULTI_RAW && rxq->qlen > MAX_IOD_RXQ_LEN) { + struct sk_buff *victim = skb_dequeue(rxq); + mif_err("%s: %s application may be dead (rxq->qlen %d > %d)\n", + iod->name, iod->app ? iod->app : "corresponding", + rxq->qlen, MAX_IOD_RXQ_LEN); + if (victim) + dev_kfree_skb_any(victim); + return -ENOSPC; + } else { + mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); return 0; } +} - frm->config = SIPC5_START_MASK; +static int rx_drain(struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} - if (iod->format == IPC_FMT && count > 2048) { - frm->ctl_fld = true; - frm->config |= SIPC5_EXT_FIELD_EXIST; - frm->config |= SIPC5_CTL_FIELD_EXIST; - } +static int rx_loopback(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = skbpriv(skb)->ld; + int ret; - if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) { - frm->ext_len = true; - frm->config |= SIPC5_EXT_FIELD_EXIST; + ret = ld->send(ld, iod, skb); + if (ret < 0) { + mif_err("%s->%s: ERR! ld->send fail (err %d)\n", + iod->name, ld->name, ret); } - if (ld->aligned) - frm->config |= SIPC5_PADDING_EXIST; - - frm->ch_id = iod->id; - - frm->hdr_len = sipc5_get_hdr_len(frm->config); - frm->data_len = count; - frm->len = frm->hdr_len + frm->data_len; - - buff[SIPC5_CONFIG_OFFSET] = frm->config; - buff[SIPC5_CH_ID_OFFSET] = frm->ch_id; - - if (unlikely(frm->ext_len)) - *sz32 = (u32)frm->len; - else - *sz16 = (u16)frm->len; - - if (unlikely(frm->ctl_fld)) - buff[SIPC5_CTL_OFFSET] = frm->control; - - return frm->hdr_len; + return ret; } static int rx_fmt_frame(struct sk_buff *skb) { - struct io_device *iod = skbpriv(skb)->iod; struct link_device *ld = skbpriv(skb)->ld; - struct sk_buff_head *rxq = &iod->sk_rx_q; - struct sipc_fmt_hdr *fh; + struct io_device *iod = skbpriv(skb)->iod; struct sk_buff *rx_skb; - u8 ctrl = skbpriv(skb)->control; - unsigned id = ctrl & 0x7F; + int hdr_len = sipc5_get_hdr_len(skb->data); + u8 ctrl; + u8 id; - if (iod->skb[id] == NULL) { - /* - ** There has been no multiple frame with this ID. - */ - if ((ctrl & 0x80) == 0) { - /* - ** It is a single frame because the "more" bit is 0. - */ - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: WARNING! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen = %d\n", - iod->name, rxq->qlen); - } + if (!sipc5_multi_frame(skb->data)) { + skb_pull(skb, hdr_len); + queue_skb_to_iod(skb, iod); + wake_up(&iod->wq); + return 0; + } - wake_up(&iod->wq); - return 0; - } + /* Get the control field */ + ctrl = sipc5_get_ctrl_field(skb->data); - /* - ** The start of multiple frames - */ - fh = (struct sipc_fmt_hdr *)skb->data; - mif_debug("%s: start multi-frame (ID:%d len:%d)\n", - iod->name, id, fh->len); + /* Extract the control ID from the control field */ + id = ctrl & 0x7F; + + /* Remove SIPC5 link header */ + skb_pull(skb, hdr_len); + + /* If there has been no multiple frame with this ID, ... */ + if (iod->skb[id] == NULL) { + struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)skb->data; + + mif_err("%s->%s: start of multi-frame (ID:%d len:%d)\n", + ld->name, iod->name, id, fh->len); rx_skb = rx_alloc_skb(fh->len, iod, ld); if (!rx_skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name); + mif_err("%s: ERR! rx_alloc_skb fail\n", iod->name); return -ENOMEM; } @@ -313,31 +236,19 @@ static int rx_fmt_frame(struct sk_buff *skb) rx_skb = iod->skb[id]; } - /* - ** Start multi-frame processing - */ + /* Perform multi-frame processing */ memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); dev_kfree_skb_any(skb); if (ctrl & 0x80) { /* The last frame has not arrived yet. */ - mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n", - iod->name, id, rx_skb->len); + mif_info("%s->%s: recv multi-frame (ID:%d rcvd:%d)\n", + ld->name, iod->name, id, rx_skb->len); } else { /* It is the last frame because the "more" bit is 0. */ - mif_debug("%s: end multi-frame (ID:%d rcvd:%d)\n", - iod->name, id, rx_skb->len); - skb_queue_tail(rxq, rx_skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: WARNING! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); - } - + mif_err("%s->%s: end of multi-frame (ID:%d rcvd:%d)\n", + ld->name, iod->name, id, rx_skb->len); + queue_skb_to_iod(rx_skb, iod); iod->skb[id] = NULL; wake_up(&iod->wq); } @@ -345,76 +256,14 @@ static int rx_fmt_frame(struct sk_buff *skb) return 0; } -static int rx_rfs_frame(struct sk_buff *skb) -{ - struct io_device *iod = skbpriv(skb)->iod; - struct sk_buff_head *rxq = &iod->sk_rx_q; - - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); - } - - wake_up(&iod->wq); - - return 0; -} - -static int rx_loopback(struct sk_buff *skb) -{ - struct io_device *iod = skbpriv(skb)->iod; - struct link_device *ld = get_current_link(iod); - struct sipc5_frame_data frm; - unsigned headroom; - unsigned tailroom = 0; - int ret; - - headroom = tx_build_link_header(&frm, iod, ld, skb->len); - - if (ld->aligned) - tailroom = sipc5_calc_padding_size(headroom + skb->len); - - /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb) - * already alloc 32bytes padding in headroom. 32bytes are enough. - */ - - /* store IPC link header to start of skb - * this is skb_push not skb_put. different with misc_write. - */ - memcpy(skb_push(skb, headroom), frm.hdr, headroom); - - /* store padding */ - if (tailroom) - skb_put(skb, tailroom); - - /* forward */ - ret = ld->send(ld, iod, skb); - if (ret < 0) - mif_err("%s->%s: ld->send fail: %d\n", iod->name, - ld->name, ret); - return ret; -} - static int rx_raw_misc(struct sk_buff *skb) { - struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ - struct sk_buff_head *rxq = &iod->sk_rx_q; + struct io_device *iod = skbpriv(skb)->iod; - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); - } + /* Remove the SIPC5 link header */ + skb_pull(skb, sipc5_get_hdr_len(skb->data)); + queue_skb_to_iod(skb, iod); wake_up(&iod->wq); return 0; @@ -422,12 +271,11 @@ static int rx_raw_misc(struct sk_buff *skb) static int rx_multi_pdp(struct sk_buff *skb) { - struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ + struct link_device *ld = skbpriv(skb)->ld; + struct io_device *iod = skbpriv(skb)->iod; struct net_device *ndev; struct iphdr *iphdr; - struct ethhdr *ehdr; int ret; - const char source[ETH_ALEN] = SOURCE_MAC_ADDR; ndev = iod->ndev; if (!ndev) { @@ -435,6 +283,9 @@ static int rx_multi_pdp(struct sk_buff *skb) return -ENODEV; } + /* Remove the SIPC5 link header */ + skb_pull(skb, sipc5_get_hdr_len(skb->data)); + skb->dev = ndev; ndev->stats.rx_packets++; ndev->stats.rx_bytes += skb->len; @@ -447,14 +298,15 @@ static int rx_multi_pdp(struct sk_buff *skb) skb->protocol = htons(ETH_P_IP); if (iod->use_handover) { - skb_push(skb, sizeof(struct ethhdr)); - ehdr = (void *)skb->data; + struct ethhdr *ehdr; + const char source[ETH_ALEN] = SOURCE_MAC_ADDR; + + ehdr = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr)); memcpy(ehdr->h_dest, ndev->dev_addr, ETH_ALEN); memcpy(ehdr->h_source, source, ETH_ALEN); ehdr->h_proto = skb->protocol; skb->ip_summed = CHECKSUM_UNNECESSARY; skb_reset_mac_header(skb); - skb_pull(skb, sizeof(struct ethhdr)); } @@ -463,48 +315,82 @@ static int rx_multi_pdp(struct sk_buff *skb) else ret = netif_rx_ni(skb); - if (ret != NET_RX_SUCCESS) - mif_info("%s: ERR! netif_rx fail (err %d)\n", iod->name, ret); + if (ret != NET_RX_SUCCESS) { + mif_err("%s->%s: ERR! netif_rx fail (err %d)\n", + ld->name, iod->name, ret); + } return ret; } static int rx_demux(struct link_device *ld, struct sk_buff *skb) { - struct io_device *iod = NULL; - char *link = ld->name; - u8 ch = skbpriv(skb)->ch_id; + struct io_device *iod; + u8 ch = sipc5_get_ch_id(skb->data); +#ifdef DEBUG_MODEM_IF + struct modem_ctl *mc = ld->mc; + size_t len = (skb->len > 20) ? 20 : skb->len; + char tag[MIF_MAX_STR_LEN]; +#endif - if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) { - mif_info("%s: ERR! invalid ch# %d\n", link, ch); + if (unlikely(ch == 0)) { + mif_err("%s: ERR! invalid ch# %d\n", ld->name, ch); return -ENODEV; } + if (unlikely(ch == SIPC5_CH_ID_FLOW_CTRL)) + return netif_flow_ctrl(ld, skb); + /* IP loopback */ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr) ch = RMNET0_CH_ID; iod = link_get_iod_with_channel(ld, ch); if (unlikely(!iod)) { - mif_info("%s: ERR! no iod for ch# %d\n", link, ch); + mif_err("%s: ERR! no iod with ch# %d\n", ld->name, ch); return -ENODEV; } skbpriv(skb)->ld = ld; skbpriv(skb)->iod = iod; - skbpriv(skb)->real_iod = iod; - /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */ - if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL)) + /* Don't care whether or not DATA_DRAIN_CHANNEL is opened */ + if (iod->id == DATA_DRAIN_CHANNEL) + return rx_drain(skb); + + /* Don't care whether or not DATA_LOOPBACK_CHANNEL is opened */ + if (iod->id == DATA_LOOPBACK_CHANNEL) return rx_loopback(skb); +#ifdef DEBUG_MODEM_IF + snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", mc->name, iod->name); + if (unlikely(iod->format == IPC_FMT)) + pr_ipc(1, tag, skb->data, len); +#if 0 + if (iod->format == IPC_RAW) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (iod->format == IPC_BOOT) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (iod->format == IPC_RAMDUMP) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (ch == 28) + pr_ipc(0, tag, skb->data, len); +#endif +#endif /*DEBUG_MODEM_IF*/ + if (atomic_read(&iod->opened) <= 0) { - mif_info("%s: ERR! %s is not opened\n", link, iod->name); + mif_err("%s: ERR! %s is not opened\n", ld->name, iod->name); return -ENODEV; } if (ch >= SIPC5_CH_ID_RFS_0) - return rx_rfs_frame(skb); + return rx_raw_misc(skb); else if (ch >= SIPC5_CH_ID_FMT_0) return rx_fmt_frame(skb); else if (iod->io_typ == IODEV_MISC) @@ -513,342 +399,337 @@ static int rx_demux(struct link_device *ld, struct sk_buff *skb) return rx_multi_pdp(skb); } -/* Check and store link layer header, then alloc an skb */ -static int rx_header_from_serial(struct io_device *iod, struct link_device *ld, - u8 *buff, unsigned size, struct sipc5_frame_data *frm) +/** + * rx_frame_config + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Checks a config field + * 2) Calculates the length of link layer header in an incoming frame and stores + * the value to "frm->hdr_len" + * 3) Stores the config field to "frm->hdr" and add the size of config field to + * "frm->hdr_rcvd" + * + * Returns the length of a config field that was copied to "frm" + */ +static int rx_frame_config(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) { - char *link = ld->name; - struct sk_buff *skb; - int len; - u8 cfg = buff[0]; - - mif_debug("%s: size %d\n", link, size); - - if (!frm->config) { - if (unlikely(!sipc5_start_valid(cfg))) { - mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); - return -EBADMSG; - } - rx_check_frame_cfg(cfg, frm); - - /* Copy the link layer header to the header buffer */ - len = min(frm->hdr_len, size); - memcpy(frm->hdr, buff, len); - } else { - /* Copy the link layer header to the header buffer */ - len = min((frm->hdr_len - frm->hdr_rcvd), size); - memcpy((frm->hdr + frm->hdr_rcvd), buff, len); - } + int rest; + int rcvd; - frm->hdr_rcvd += len; - - mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n", - link, frm->hdr_len, frm->hdr_rcvd); - - if (frm->hdr_rcvd >= frm->hdr_len) { - rx_build_meta_data(ld, frm); - skb = rx_alloc_skb(frm->data_len, iod, ld); - fragdata(iod, ld)->skb_recv = skb; - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + if (unlikely(!sipc5_start_valid(buff))) { + mif_err("%s->%s: ERR! INVALID config 0x%02x\n", + ld->name, iod->name, buff[0]); + return -EBADMSG; } - return len; -} - -/* copy data to skb */ -static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld, - u8 *buff, unsigned size, struct sipc5_frame_data *frm) -{ - struct sk_buff *skb = fragdata(iod, ld)->skb_recv; - char *link = ld->name; - unsigned rest = frm->data_len - frm->data_rcvd; - unsigned len; - - /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */ - rest = frm->data_len - frm->data_rcvd; - mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n", - link, frm->data_len, frm->data_rcvd, rest, size); - - /* If there is no skb, data must be dropped. */ - len = min(rest, size); - if (skb) - memcpy(skb_put(skb, len), buff, len); + frm->hdr_len = sipc5_get_hdr_len(buff); - frm->data_rcvd += len; + /* Calculate the size of a segment that will be copied */ + rest = frm->hdr_len; + rcvd = SIPC5_CONFIG_SIZE; + mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, + rcvd); - mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n", - link, frm->data_len, frm->data_rcvd); + /* Copy the config field of an SIPC5 link header to the header buffer */ + memcpy(frm->hdr, buff, rcvd); + frm->hdr_rcvd += rcvd; - return len; + return rcvd; } -static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld, - const char *data, unsigned size) +/** + * rx_frame_prepare_skb + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Extracts the length of a link frame from the link header in "frm->hdr" + * 2) Allocates an skb + * 3) Calculates the payload size in the link frame + * 4) Calculates the padding size in the link frame + * + * Returns the pointer to an skb + */ +static struct sk_buff *rx_frame_prepare_skb(struct io_device *iod, + struct link_device *ld, struct sipc5_frame_data *frm) { - struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; - char *link = ld->name; - u8 *buff = (u8 *)data; - int rest = (int)size; - int err = 0; - int done = 0; - mif_debug("%s: size = %d\n", link, size); + /* Get the frame length */ + frm->len = sipc5_get_frame_len(frm->hdr); - if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) { - /* - ** There is an skb that is waiting for more SIPC5 data. - ** In this case, rx_header_from_serial() must be skipped. - */ - mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n", - link, frm->data_len, frm->data_rcvd); - goto recv_data; + /* Allocate an skb */ + skb = rx_alloc_skb(frm->len, iod, ld); + if (!skb) { + mif_err("%s->%s: ERR! rx_alloc_skb fail (size %d)\n", + ld->name, iod->name, frm->len); + return NULL; } -next_frame: - /* Receive and analyze header, then prepare an akb */ - err = done = rx_header_from_serial(iod, ld, buff, rest, frm); - if (err < 0) - goto err_exit; - - buff += done; - rest -= done; - mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest); - if (rest < 0) - goto err_range; + /* Calculates the payload size */ + frm->pay_len = frm->len - frm->hdr_len; - if (rest == 0) - return size; + /* Calculates the padding size */ + if (sipc5_padding_exist(frm->hdr)) + frm->pad_len = sipc5_calc_padding_size(frm->len); -recv_data: - err = 0; + mif_debug("%s->%s: size %d (header:%d payload:%d padding:%d)\n", + ld->name, iod->name, frm->len, frm->hdr_len, frm->pay_len, + frm->pad_len); - mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest); - - done = rx_payload_from_serial(iod, ld, buff, rest, frm); - buff += done; - rest -= done; + return skb; +} - mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest); +/** + * rx_frame_header + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Stores a link layer header to "frm->hdr" temporarily while "frm->hdr_rcvd" + * is less than "frm->hdr_len" + * 2) Then, + * Allocates an skb + * Copies the link header from "frm" to "skb" + * Register the skb to receive payload + * + * Returns the size of a segment that was copied to "frm" + */ +static int rx_frame_header(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb; + int rest; + int rcvd; - if (rest == 0 && frm->data_rcvd < frm->data_len) { - /* - Data is being received and more data will come within the next - frame from the link device. - */ - return size; - } + /* Calculate the size of a segment that will be copied */ + rest = frm->hdr_len - frm->hdr_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, + rcvd); - /* At this point, one complete link layer frame has been received. */ + /* Copy a segment of an SIPC5 link header to "frm" */ + memcpy((frm->hdr + frm->hdr_rcvd), buff, rcvd); + frm->hdr_rcvd += rcvd; - /* A padding size is applied to access the next IPC frame. */ - if (frm->padding) { - done = sipc5_calc_padding_size(frm->len); - if (done > rest) { - mif_info("%s: ERR! padding %d > rest %d\n", - link, done, rest); - goto err_exit; + if (frm->hdr_rcvd >= frm->hdr_len) { + /* Prepare an skb with the information in {iod, ld, frm} */ + skb = rx_frame_prepare_skb(iod, ld, frm); + if (!skb) { + mif_err("%s->%s: ERR! rx_frame_prepare_skb fail\n", + ld->name, iod->name); + return -ENOMEM; } - buff += done; - rest -= done; - - mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest); - - if (rest < 0) - goto err_range; - - } - - skb = fragdata(iod, ld)->skb_recv; - if (likely(skb)) { - mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); - err = rx_demux(ld, skb); - if (err < 0) - dev_kfree_skb_any(skb); - } else { - mif_debug("%s: len:%d -> drop\n", link, skb->len); - } - - /* initialize the skb_recv and the frame_data buffer */ - fragdata(iod, ld)->skb_recv = NULL; - memset(frm, 0, sizeof(struct sipc5_frame_data)); + /* Copy an SIPC5 link header from "frm" to "skb" */ + memcpy(skb_put(skb, frm->hdr_len), frm->hdr, frm->hdr_len); - if (rest > 0) - goto next_frame; - - if (rest <= 0) - return size; - -err_exit: - if (fragdata(iod, ld)->skb_recv && - frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd >= frm->data_len) { - dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); - memset(frm, 0, sizeof(struct sipc5_frame_data)); - fragdata(iod, ld)->skb_recv = NULL; - mif_info("%s: ERR! clear frag\n", link); + /* Register the skb to receive payload */ + fragdata(iod, ld)->skb_recv = skb; } - return err; -err_range: - mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest); - return size; + return rcvd; } /** - * rx_header_from_mem - * @ld: pointer to the link device - * @buff: pointer to the frame - * @rest: size of the frame - * @frm: pointer to the sipc5_frame_data buffer + * rx_frame_payload + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure * - * 1) Verifies a link layer header configuration of a frame - * 2) Stores the link layer header to the header buffer - * 3) Builds and stores the meta data of the frame into a meta data buffer - * 4) Verifies the length of the frame + * Stores a link layer payload to "skb" * - * Returns SIPC5 header length + * Returns the size of a segment that was copied to "skb" */ -static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest, - struct sipc5_frame_data *frm) +static int rx_frame_payload(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) { - char *link = ld->name; - u8 cfg = buff[0]; + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + int rest; + int rcvd; - /* Verify link layer header configuration */ - if (unlikely(!sipc5_start_valid(cfg))) { - mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); - return -EBADMSG; - } - rx_check_frame_cfg(cfg, frm); + /* Calculate the size of a segment that will be copied */ + rest = frm->pay_len - frm->pay_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: pay_len:%d pay_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->pay_len, frm->pay_rcvd, rest, size, + rcvd); - /* Store the link layer header to the header buffer */ - memcpy(frm->hdr, buff, frm->hdr_len); - frm->hdr_rcvd = frm->hdr_len; + /* Copy an SIPC5 link payload to "skb" */ + memcpy(skb_put(skb, rcvd), buff, rcvd); + frm->pay_rcvd += rcvd; - /* Build and store the meta data of this frame */ - rx_build_meta_data(ld, frm); + return rcvd; +} - /* Verify frame length */ - if (unlikely(frm->len > rest)) { - mif_info("%s: ERR! frame length %d > rest %d\n", - link, frm->len, rest); - return -EBADMSG; - } +static int rx_frame_padding(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + int rest; + int rcvd; + + /* Calculate the size of a segment that will be dropped as padding */ + rest = frm->pad_len - frm->pad_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: pad_len:%d pad_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->pad_len, frm->pad_rcvd, rest, size, + rcvd); - return frm->hdr_rcvd; + /* Copy an SIPC5 link padding to "skb" */ + memcpy(skb_put(skb, rcvd), buff, rcvd); + frm->pad_rcvd += rcvd; + + return rcvd; } -/* copy data to skb */ -static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len) +static int rx_frame_done(struct io_device *iod, struct link_device *ld, + struct sk_buff *skb) { - /* If there is no skb, data must be dropped. */ - if (skb) - memcpy(skb_put(skb, len), buff, len); - return len; + /* Cut off the padding of the current frame */ + skb_trim(skb, sipc5_get_frame_len(skb->data)); + mif_debug("%s->%s: frame length = %d\n", ld->name, iod->name, skb->len); + + return rx_demux(ld, skb); } -static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld, +static int recv_frame_from_buff(struct io_device *iod, struct link_device *ld, const char *data, unsigned size) { struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; - char *link = ld->name; u8 *buff = (u8 *)data; int rest = (int)size; - int len; - int done; + int done = 0; + int err = 0; - mif_debug("%s: size = %d\n", link, size); + mif_debug("%s->%s: size %d (RX state = %s)\n", ld->name, iod->name, + size, get_rx_state_str(iod->curr_rx_state)); while (rest > 0) { - /* Initialize the frame data buffer */ - memset(frm, 0, sizeof(struct sipc5_frame_data)); - skb = NULL; + switch (iod->curr_rx_state) { + case IOD_RX_ON_STANDBY: + fragdata(iod, ld)->skb_recv = NULL; + memset(frm, 0, sizeof(struct sipc5_frame_data)); + + done = rx_frame_config(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* Receive and analyze link layer header */ - done = rx_header_from_mem(ld, buff, rest, frm); - if (unlikely(done < 0)) - return -EBADMSG; + iod->next_rx_state = IOD_RX_HEADER; - /* Verify rest size */ - rest -= done; - if (rest < 0) { - mif_info("%s: ERR! rx_header -> rest %d\n", link, rest); - return -ERANGE; - } + break; - /* Move buff pointer to the payload */ - buff += done; + case IOD_RX_HEADER: + done = rx_frame_header(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* Prepare an akb */ - len = frm->data_len; - skb = rx_alloc_skb(len, iod, ld); + if (frm->hdr_rcvd >= frm->hdr_len) + iod->next_rx_state = IOD_RX_PAYLOAD; + else + iod->next_rx_state = IOD_RX_HEADER; - /* Store channel ID and control fields to the CB of the skb */ - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + break; - /* Receive payload */ - mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n", - link, done, rest, len); - done = rx_payload_from_mem(skb, buff, len); - rest -= done; - if (rest < 0) { - mif_info("%s: ERR! rx_payload() -> rest %d\n", - link, rest); - if (skb) - dev_kfree_skb_any(skb); - return -ERANGE; - } - buff += done; + case IOD_RX_PAYLOAD: + done = rx_frame_payload(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* A padding size is applied to access the next IPC frame. */ - if (frm->padding) { - done = sipc5_calc_padding_size(frm->len); - if (done > rest) { - mif_info("%s: ERR! padding %d > rest %d\n", - link, done, rest); - if (skb) - dev_kfree_skb_any(skb); - return -ERANGE; + if (frm->pay_rcvd >= frm->pay_len) { + if (frm->pad_len > 0) + iod->next_rx_state = IOD_RX_PADDING; + else + iod->next_rx_state = IOD_RX_ON_STANDBY; + } else { + iod->next_rx_state = IOD_RX_PAYLOAD; } - buff += done; - rest -= done; + + break; + + case IOD_RX_PADDING: + done = rx_frame_padding(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } + + if (frm->pad_rcvd >= frm->pad_len) + iod->next_rx_state = IOD_RX_ON_STANDBY; + else + iod->next_rx_state = IOD_RX_PADDING; + + break; + + default: + mif_err("%s->%s: ERR! INVALID RX state %d\n", + ld->name, iod->name, iod->curr_rx_state); + err = -EINVAL; + goto err_exit; } - if (likely(skb)) { - mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); - if (rx_demux(ld, skb) < 0) - dev_kfree_skb_any(skb); - } else { - mif_debug("%s: len:%d -> drop\n", link, skb->len); + if (iod->next_rx_state == IOD_RX_ON_STANDBY) { + /* + ** A complete frame is in fragdata(iod, ld)->skb_recv. + */ + skb = fragdata(iod, ld)->skb_recv; + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto err_exit; } + + buff += done; + rest -= done; + if (rest < 0) + goto err_range; + + iod->curr_rx_state = iod->next_rx_state; } - return 0; + return size; + +err_exit: + if (fragdata(iod, ld)->skb_recv) { + mif_err("%s->%s: ERR! clear frag (size:%d done:%d rest:%d)\n", + ld->name, iod->name, size, done, rest); + dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); + fragdata(iod, ld)->skb_recv = NULL; + } + iod->curr_rx_state = IOD_RX_ON_STANDBY; + return err; + +err_range: + mif_err("%s->%s: ERR! size:%d done:%d rest:%d\n", + ld->name, iod->name, size, done, rest); + iod->curr_rx_state = IOD_RX_ON_STANDBY; + return size; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) { - struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; - char *link = ld->name; int err; - if (!data) { - mif_info("%s: ERR! !data\n", link); - return -EINVAL; - } - - if (len <= 0) { - mif_info("%s: ERR! len %d <= 0\n", link, len); - return -EINVAL; - } - switch (iod->format) { case IPC_FMT: case IPC_RAW: @@ -857,97 +738,151 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - if (ld->link_type == LINKDEV_DPRAM && ld->aligned) - err = rx_frame_from_mem(iod, ld, data, len); - else - err = rx_frame_from_serial(iod, ld, data, len); - - if (err < 0) - mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n", - link, err); + err = recv_frame_from_buff(iod, ld, data, len); + if (err < 0) { + mif_err("%s->%s: ERR! recv_frame_from_buff fail " + "(err %d)\n", ld->name, iod->name, err); + } return err; - case IPC_CMD: - case IPC_BOOT: - case IPC_RAMDUMP: + default: + mif_debug("%s->%s: len %d\n", ld->name, iod->name, len); + /* save packet to sk_buff */ skb = rx_alloc_skb(len, iod, ld); if (!skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", link); + mif_info("%s->%s: ERR! rx_alloc_skb fail\n", + ld->name, iod->name); return -ENOMEM; } - mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name); - memcpy(skb_put(skb, len), data, len); - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: ERR! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } + + queue_skb_to_iod(skb, iod); + wake_up(&iod->wq); return len; - - default: - mif_info("%s: ERR! unknown format %d\n", link, iod->format); - return -EINVAL; } } -static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld, +static int recv_frame_from_skb(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { - struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; - u8 cfg = skb->data[0]; + struct sk_buff *clone; + unsigned int rest; + unsigned int rcvd; + int tot; /* total length including padding */ + int err = 0; - /* Initialize the frame data buffer */ - memset(frm, 0, sizeof(struct sipc5_frame_data)); + /* + ** If there is only one SIPC5 frame in @skb, receive the SIPC5 frame and + ** return immediately. In this case, the frame verification must already + ** have been done at the link device. + */ + if (skbpriv(skb)->single_frame) { + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto exit; + return 0; + } /* - ** The start of a link layer header has already been checked in the - ** link device. + ** The routine from here is used only if there may be multiple SIPC5 + ** frames in @skb. */ - /* Analyze the configuration of the link layer header */ - rx_check_frame_cfg(cfg, frm); + /* Check the config field of the first frame in @skb */ + if (!sipc5_start_valid(skb->data)) { + mif_err("%s->%s: ERR! INVALID config 0x%02X\n", + ld->name, iod->name, skb->data[0]); + err = -EINVAL; + goto exit; + } + + /* Get the total length of the frame with a padding */ + tot = sipc5_get_total_len(skb->data); - /* Store the link layer header to the header buffer */ - memcpy(frm->hdr, skb->data, frm->hdr_len); - frm->hdr_rcvd = frm->hdr_len; + /* Verify the total length of the first frame */ + rest = skb->len; + if (unlikely(tot > rest)) { + mif_err("%s->%s: ERR! tot %d > skb->len %d)\n", + ld->name, iod->name, tot, rest); + err = -EINVAL; + goto exit; + } - /* Build and store the meta data of this frame */ - rx_build_meta_data(ld, frm); + /* If there is only one SIPC5 frame in @skb, */ + if (likely(tot == rest)) { + /* Receive the SIPC5 frame and return immediately */ + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto exit; + return 0; + } /* - ** The length of the frame has already been checked in the link device. + ** This routine is used only if there are multiple SIPC5 frames in @skb. */ + rcvd = 0; + while (rest > 0) { + clone = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!clone)) { + mif_err("%s->%s: ERR! skb_clone fail\n", + ld->name, iod->name); + err = -ENOMEM; + goto exit; + } - /* Trim the link layer header off the frame */ - skb_pull(skb, frm->hdr_len); + /* Get the start of an SIPC5 frame */ + skb_pull(clone, rcvd); + if (!sipc5_start_valid(clone->data)) { + mif_err("%s->%s: ERR! INVALID config 0x%02X\n", + ld->name, iod->name, clone->data[0]); + dev_kfree_skb_any(clone); + err = -EINVAL; + goto exit; + } - /* Store channel ID and control fields to the CB of the skb */ - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + /* Get the total length of the current frame with a padding */ + tot = sipc5_get_total_len(clone->data); + if (unlikely(tot > rest)) { + mif_err("%s->%s: ERR! dirty frame (tot %d > rest %d)\n", + ld->name, iod->name, tot, rest); + dev_kfree_skb_any(clone); + err = -EINVAL; + goto exit; + } - /* Demux the frame */ - if (rx_demux(ld, skb) < 0) { - mif_err("%s: ERR! rx_demux fail\n", ld->name); - return -EINVAL; + /* Cut off the padding of the current frame */ + skb_trim(clone, sipc5_get_frame_len(clone->data)); + + /* Demux the frame */ + err = rx_demux(ld, clone); + if (err < 0) { + mif_err("%s->%s: ERR! rx_demux fail (err %d)\n", + ld->name, iod->name, err); + dev_kfree_skb_any(clone); + goto exit; + } + + /* Calculate the start of the next frame */ + rcvd += tot; + + /* Calculate the rest size of data in @skb */ + rest -= tot; } - return 0; +exit: + dev_kfree_skb_any(skb); + return err; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { - char *link = ld->name; enum dev_format dev = iod->format; int err; @@ -959,17 +894,35 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - err = rx_frame_from_skb(iod, ld, skb); + err = recv_frame_from_skb(iod, ld, skb); if (err < 0) { - dev_kfree_skb_any(skb); - mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n", - link, err); + mif_err("%s->%s: ERR! recv_frame_from_skb fail " + "(err %d)\n", ld->name, iod->name, err); + } + + return err; + + case IPC_BOOT: + case IPC_RAMDUMP: + if (!iod->id) { + mif_err("%s->%s: ERR! invalid iod\n", + ld->name, iod->name); + return -ENODEV; + } + + if (iod->waketime) + wake_lock_timeout(&iod->wakelock, iod->waketime); + + err = recv_frame_from_skb(iod, ld, skb); + if (err < 0) { + mif_err("%s->%s: ERR! recv_frame_from_skb fail " + "(err %d)\n", ld->name, iod->name, err); } return err; default: - mif_info("%s: ERR! unknown device %d\n", link, dev); + mif_err("%s->%s: ERR! invalid iod\n", ld->name, iod->name); return -EINVAL; } } @@ -980,10 +933,14 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - mif_info("%s: %s state changed (state %d)\n", - iod->name, iod->mc->name, state); + struct modem_ctl *mc = iod->mc; + int old_state = mc->phone_state; - iod->mc->phone_state = state; + if (old_state != state) { + mc->phone_state = state; + mif_err("%s state changed (%s -> %s)\n", mc->name, + get_cp_state_str(old_state), get_cp_state_str(state)); + } if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || state == STATE_NV_REBUILDING) @@ -1024,23 +981,27 @@ static int misc_open(struct inode *inode, struct file *filp) struct io_device *iod = to_io_device(filp->private_data); struct modem_shared *msd = iod->msd; struct link_device *ld; + int ref_cnt; int ret; filp->private_data = (void *)iod; - atomic_inc(&iod->opened); - list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->init_comm) { ret = ld->init_comm(ld, iod); if (ret < 0) { - mif_info("%s: init_comm fail(%d)\n", - ld->name, ret); + mif_err("%s<->%s: ERR! init_comm fail(%d)\n", + iod->name, ld->name, ret); return ret; } } } - mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + ref_cnt = atomic_inc_return(&iod->opened); + + if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) + mif_err("%s (opened %d)\n", iod->name, ref_cnt); + else + mif_info("%s (opened %d)\n", iod->name, ref_cnt); return 0; } @@ -1050,8 +1011,8 @@ static int misc_release(struct inode *inode, struct file *filp) struct io_device *iod = (struct io_device *)filp->private_data; struct modem_shared *msd = iod->msd; struct link_device *ld; + int ref_cnt; - atomic_dec(&iod->opened); skb_queue_purge(&iod->sk_rx_q); list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -1059,7 +1020,12 @@ static int misc_release(struct inode *inode, struct file *filp) ld->terminate_comm(ld, iod); } - mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + ref_cnt = atomic_dec_return(&iod->opened); + + if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) + mif_err("%s (opened %d)\n", iod->name, ref_cnt); + else + mif_info("%s (opened %d)\n", iod->name, ref_cnt); return 0; } @@ -1067,20 +1033,23 @@ static int misc_release(struct inode *inode, struct file *filp) static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) { struct io_device *iod = (struct io_device *)filp->private_data; + struct modem_ctl *mc = iod->mc; poll_wait(filp, &iod->wq, wait); - if (!skb_queue_empty(&iod->sk_rx_q) && - iod->mc->phone_state != STATE_OFFLINE) { + if (!skb_queue_empty(&iod->sk_rx_q) && mc->phone_state != STATE_OFFLINE) return POLLIN | POLLRDNORM; - } else if ((iod->mc->phone_state == STATE_CRASH_RESET) || - (iod->mc->phone_state == STATE_CRASH_EXIT) || - (iod->mc->phone_state == STATE_NV_REBUILDING) || - (iod->mc->sim_state.changed)) { + + if (mc->phone_state == STATE_CRASH_RESET + || mc->phone_state == STATE_CRASH_EXIT + || mc->phone_state == STATE_NV_REBUILDING + || mc->sim_state.changed) { if (iod->format == IPC_RAW) { msleep(20); return 0; } + if (iod->format == IPC_RAMDUMP) + return 0; return POLLHUP; } else { return 0; @@ -1089,132 +1058,191 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int p_state; struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); - char cpinfo_buf[530] = "CP Crash "; + struct modem_ctl *mc = iod->mc; + int p_state; + char *buff; + void __user *user_buff; unsigned long size; - int ret; switch (cmd) { case IOCTL_MODEM_ON: - mif_info("%s: IOCTL_MODEM_ON\n", iod->name); - return iod->mc->ops.modem_on(iod->mc); + if (mc->ops.modem_on) { + mif_err("%s: IOCTL_MODEM_ON\n", iod->name); + return mc->ops.modem_on(mc); + } + mif_err("%s: !mc->ops.modem_on\n", iod->name); + return -EINVAL; case IOCTL_MODEM_OFF: - mif_info("%s: IOCTL_MODEM_OFF\n", iod->name); - return iod->mc->ops.modem_off(iod->mc); + if (mc->ops.modem_off) { + mif_err("%s: IOCTL_MODEM_OFF\n", iod->name); + return mc->ops.modem_off(mc); + } + mif_err("%s: !mc->ops.modem_off\n", iod->name); + return -EINVAL; case IOCTL_MODEM_RESET: - mif_info("%s: IOCTL_MODEM_RESET\n", iod->name); - return iod->mc->ops.modem_reset(iod->mc); + if (mc->ops.modem_reset) { + mif_err("%s: IOCTL_MODEM_RESET\n", iod->name); + return mc->ops.modem_reset(mc); + } + mif_err("%s: !mc->ops.modem_reset\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_ON: - mif_info("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); - return iod->mc->ops.modem_boot_on(iod->mc); + if (mc->ops.modem_boot_on) { + mif_err("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); + return mc->ops.modem_boot_on(mc); + } + mif_err("%s: !mc->ops.modem_boot_on\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_OFF: - mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); - return iod->mc->ops.modem_boot_off(iod->mc); + if (mc->ops.modem_boot_off) { + mif_err("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); + return mc->ops.modem_boot_off(mc); + } + mif_err("%s: !mc->ops.modem_boot_off\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_DONE: mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); - if (iod->mc->ops.modem_boot_done) - return iod->mc->ops.modem_boot_done(iod->mc); - else - return 0; + if (mc->ops.modem_boot_done) + return mc->ops.modem_boot_done(mc); + return 0; case IOCTL_MODEM_STATUS: mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); - p_state = iod->mc->phone_state; + p_state = mc->phone_state; if ((p_state == STATE_CRASH_RESET) || (p_state == STATE_CRASH_EXIT)) { - mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", - iod->name, p_state); - } else if (iod->mc->sim_state.changed) { - int s_state = iod->mc->sim_state.online ? + mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", + iod->name, get_cp_state_str(p_state)); + } else if (mc->sim_state.changed) { + int s_state = mc->sim_state.online ? STATE_SIM_ATTACH : STATE_SIM_DETACH; - iod->mc->sim_state.changed = false; + mc->sim_state.changed = false; return s_state; } else if (p_state == STATE_NV_REBUILDING) { - mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", - iod->name, p_state); - iod->mc->phone_state = STATE_ONLINE; + mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", + iod->name, get_cp_state_str(p_state)); + mc->phone_state = STATE_ONLINE; } return p_state; - case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_debug("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", - iod->name); - - if (iod->format != IPC_MULTI_RAW) - return -EINVAL; + case IOCTL_MODEM_XMIT_BOOT: + if (ld->xmit_boot) { + mif_info("%s: IOCTL_MODEM_XMIT_BOOT\n", iod->name); + return ld->xmit_boot(ld, iod, arg); + } + mif_err("%s: !ld->xmit_boot\n", iod->name); + return -EINVAL; - iodevs_for_each(iod->msd, iodev_netif_stop, 0); - return 0; + case IOCTL_MODEM_DL_START: + if (ld->dload_start) { + mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); + return ld->dload_start(ld, iod); + } + mif_err("%s: !ld->dload_start\n", iod->name); + return -EINVAL; - case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", - iod->name); + case IOCTL_MODEM_FW_UPDATE: + if (ld->firm_update) { + mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); + return ld->firm_update(ld, iod, arg); + } + mif_err("%s: !ld->firm_update\n", iod->name); + return -EINVAL; - if (iod->format != IPC_MULTI_RAW) - return -EINVAL; + case IOCTL_MODEM_FORCE_CRASH_EXIT: + if (mc->ops.modem_force_crash_exit) { + mif_err("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", + iod->name); + return mc->ops.modem_force_crash_exit(mc); + } + mif_err("%s: !mc->ops.modem_force_crash_exit\n", iod->name); + return -EINVAL; - iodevs_for_each(iod->msd, iodev_netif_wake, 0); - return 0; + case IOCTL_MODEM_DUMP_RESET: + if (mc->ops.modem_dump_reset) { + mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); + return mc->ops.modem_dump_reset(mc); + } + mif_err("%s: !mc->ops.modem_dump_reset\n", iod->name); + return -EINVAL; case IOCTL_MODEM_DUMP_START: - mif_info("%s: IOCTL_MODEM_DUMP_START\n", iod->name); - return ld->dump_start(ld, iod); + if (ld->dump_start) { + mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + } + mif_err("%s: !ld->dump_start\n", iod->name); + return -EINVAL; + + case IOCTL_MODEM_RAMDUMP_START: + if (ld->dump_start) { + mif_info("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + } + mif_err("%s: !ld->dump_start\n", iod->name); + return -EINVAL; case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); - return ld->dump_update(ld, iod, arg); + if (ld->dump_update) { + mif_info("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); + return ld->dump_update(ld, iod, arg); + } + mif_err("%s: !ld->dump_update\n", iod->name); + return -EINVAL; - case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_info("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); - if (iod->mc->ops.modem_force_crash_exit) - return iod->mc->ops.modem_force_crash_exit(iod->mc); + case IOCTL_MODEM_RAMDUMP_STOP: + if (ld->dump_finish) { + mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); + return ld->dump_finish(ld, iod, arg); + } + mif_err("%s: !ld->dump_finish\n", iod->name); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: mif_info("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); - if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), - (void __user *)arg, MAX_CPINFO_SIZE) != 0) - return -EFAULT; - panic(cpinfo_buf); + strcpy(iod->msd->cp_crash_info, CP_CRASH_TAG); + if (arg) { + buff = iod->msd->cp_crash_info + strlen(CP_CRASH_TAG); + user_buff = (void __user *)arg; + if (copy_from_user(buff, user_buff, MAX_CPINFO_SIZE)) + return -EFAULT; + } + panic(iod->msd->cp_crash_info); return 0; - case IOCTL_MODEM_DUMP_RESET: - mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); - return iod->mc->ops.modem_dump_reset(iod->mc); + case IOCTL_MODEM_PROTOCOL_SUSPEND: + mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); + if (iod->format == IPC_MULTI_RAW) { + iodevs_for_each(iod->msd, iodev_netif_stop, 0); + return 0; + } + return -EINVAL; + + case IOCTL_MODEM_PROTOCOL_RESUME: + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); + if (iod->format != IPC_MULTI_RAW) { + iodevs_for_each(iod->msd, iodev_netif_wake, 0); + return 0; + } + return -EINVAL; case IOCTL_MIF_LOG_DUMP: iodevs_for_each(iod->msd, iodev_dump_status, 0); + user_buff = (void __user *)arg; size = MAX_MIF_BUFF_SIZE; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) + if (copy_to_user(user_buff, &size, sizeof(unsigned long))) return -EFAULT; - - mif_dump_log(iod->mc->msd, iod); + mif_dump_log(mc->msd, iod); return 0; - case IOCTL_MIF_DPRAM_DUMP: -#ifdef CONFIG_LINK_DEVICE_DPRAM - if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { - size = iod->mc->mdm_data->dpram_ctl->dp_size; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) - return -EFAULT; - mif_dump_dpram(iod); - return 0; - } -#endif - return -EINVAL; - default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1222,9 +1250,10 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_info("%s: ERR! cmd 0x%X not defined.\n", iod->name, cmd); + mif_info("%s: ERR! undefined cmd 0x%X\n", iod->name, cmd); return -EINVAL; } + return 0; } @@ -1234,49 +1263,76 @@ static ssize_t misc_write(struct file *filp, const char __user *data, struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); struct sk_buff *skb; + u8 *buff; int ret; - unsigned headroom = 0; - unsigned tailroom = 0; - size_t tx_size; - struct sipc5_frame_data frm; - struct timespec epoch; + size_t headroom; + size_t tailroom; + size_t tx_bytes; + u8 cfg; if (iod->format <= IPC_RFS && iod->id == 0) return -EINVAL; - headroom = tx_build_link_header(&frm, iod, ld, count); + cfg = sipc5_build_config(iod, ld, count); + + if (cfg) + headroom = sipc5_get_hdr_len(&cfg); + else + headroom = 0; if (ld->aligned) tailroom = sipc5_calc_padding_size(headroom + count); + else + tailroom = 0; - tx_size = headroom + count + tailroom; + tx_bytes = headroom + count + tailroom; - skb = alloc_skb(tx_size, GFP_KERNEL); + skb = alloc_skb(tx_bytes, GFP_KERNEL); if (!skb) { - mif_info("%s: ERR! alloc_skb fail (tx_size:%d)\n", - iod->name, tx_size); + mif_info("%s: ERR! alloc_skb fail (tx_bytes:%d)\n", + iod->name, tx_bytes); return -ENOMEM; } - /* store IPC link header*/ - memcpy(skb_put(skb, headroom), frm.hdr, headroom); + /* Build SIPC5 link header*/ + if (cfg) { + buff = skb_put(skb, headroom); + sipc5_build_header(iod, ld, buff, cfg, 0, count); + } - /* store IPC message */ - if (copy_from_user(skb_put(skb, count), data, count) != 0) { - if (skb) - dev_kfree_skb_any(skb); + /* Store IPC message */ + buff = skb_put(skb, count); + if (copy_from_user(buff, data, count)) { + mif_err("%s->%s: ERR! copy_from_user fail (count %d)\n", + iod->name, ld->name, count); + dev_kfree_skb_any(skb); return -EFAULT; } - if (iod->id == SIPC5_CH_ID_FMT_0) { + /* Apply padding */ + if (tailroom) + skb_put(skb, tailroom); + + if (iod->format == IPC_FMT) { + struct timespec epoch; + u8 *msg = (skb->data + headroom); +#if 0 + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: RIL2MIF", iod->mc->name); + pr_ipc(1, tag, msg, (count > 20 ? 20 : count)); +#endif getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); - mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len); + mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, msg, count); } - /* store padding */ - if (tailroom) - skb_put(skb, tailroom); +#if 0 + if (iod->format == IPC_RAMDUMP) { + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: DUMP2MIF", iod->name); + pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); + } +#endif /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data @@ -1286,13 +1342,15 @@ static ssize_t misc_write(struct file *filp, const char __user *data, ret = ld->send(ld, iod, skb); if (ret < 0) { - mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); + mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", + iod->name, ld->name, ret, tx_bytes); return ret; } - if (ret != tx_size) - mif_info("%s: wrong tx size (count:%d tx_size:%d ret:%d)\n", - iod->name, count, tx_size, ret); + if (ret != tx_bytes) { + mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", + iod->name, ld->name, ret, tx_bytes, count); + } return count; } @@ -1304,24 +1362,38 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; int copied = 0; - struct timespec epoch; - skb = skb_dequeue(rxq); - if (!skb) { + if (skb_queue_empty(rxq)) { mif_info("%s: ERR! no data in rxq\n", iod->name); return 0; } - if (iod->id == SIPC5_CH_ID_FMT_0) { + skb = skb_dequeue(rxq); + + if (iod->format == IPC_FMT) { + struct timespec epoch; +#if 0 + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2RIL", iod->mc->name); + pr_ipc(0, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); +#endif getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len); } +#if 0 + if (iod->format == IPC_RAMDUMP) { + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2DUMP", iod->name); + pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); + } +#endif + copied = skb->len > count ? count : skb->len; if (copy_to_user(buf, skb->data, copied)) { - mif_info("%s: ERR! copy_to_user fail\n", iod->name); + mif_err("%s: ERR! copy_to_user fail\n", iod->name); dev_kfree_skb_any(skb); return -EFAULT; } @@ -1339,43 +1411,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return copied; } -#ifdef CONFIG_LINK_DEVICE_C2C -static int misc_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int r = 0; - unsigned long size = 0; - unsigned long pfn = 0; - unsigned long offset = 0; - struct io_device *iod = (struct io_device *)filp->private_data; - - if (!vma) - return -EFAULT; - - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { - mif_info("ERR: offset + size > C2C_CP_RGN_SIZE\n"); - return -EINVAL; - } - - /* Set the noncacheable property to the region */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_RESERVED | VM_IO; - - pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); - r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); - if (r) { - mif_info("ERR: Failed in remap_pfn_range()!!!\n"); - return -EAGAIN; - } - - mif_info("%s: VA = 0x%08lx, offset = 0x%lx, size = %lu\n", - iod->name, vma->vm_start, offset, size); - - return 0; -} -#endif - static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1384,9 +1419,6 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, -#ifdef CONFIG_LINK_DEVICE_C2C - .mmap = misc_mmap, -#endif }; static int vnet_open(struct net_device *ndev) @@ -1419,11 +1451,13 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct link_device *ld = get_current_link(iod); struct sk_buff *skb_new; int ret; - unsigned headroom = 0; - unsigned tailroom = 0; - unsigned long tx_bytes = skb->len; + unsigned headroom; + unsigned tailroom; + size_t count; + size_t tx_bytes; struct iphdr *ip_header = NULL; - struct sipc5_frame_data frm; + u8 *buff; + u8 cfg; /* When use `handover' with Network Bridge, * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here. @@ -1436,19 +1470,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_pull(skb, sizeof(struct ethhdr)); } - headroom = tx_build_link_header(&frm, iod, ld, skb->len); + count = skb->len; - /* ip loop-back */ - ip_header = (struct iphdr *)skb->data; - if (iod->msd->loopback_ipaddr && - ip_header->daddr == iod->msd->loopback_ipaddr) { - swap(ip_header->saddr, ip_header->daddr); - frm.ch_id = DATA_LOOPBACK_CHANNEL; - frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; - } + cfg = sipc5_build_config(iod, ld, count); + + headroom = sipc5_get_hdr_len(&cfg); if (ld->aligned) - tailroom = sipc5_calc_padding_size(frm.len); + tailroom = sipc5_calc_padding_size(headroom + count); + else + tailroom = 0; + + tx_bytes = headroom + count + tailroom; if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) { mif_debug("%s: skb_copy_expand needed\n", iod->name); @@ -1463,7 +1496,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_new = skb; } - memcpy(skb_push(skb_new, headroom), frm.hdr, headroom); + /* Build SIPC5 link header*/ + buff = skb_push(skb_new, headroom); + sipc5_build_header(iod, ld, buff, cfg, 0, count); + + /* IP loop-back */ + ip_header = (struct iphdr *)skb->data; + if (iod->msd->loopback_ipaddr && + ip_header->daddr == iod->msd->loopback_ipaddr) { + swap(ip_header->saddr, ip_header->daddr); + buff[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; + } + if (tailroom) skb_put(skb_new, tailroom); @@ -1473,12 +1517,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) ret = ld->send(ld, iod, skb_new); if (ret < 0) { netif_stop_queue(ndev); - mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); + mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", + iod->name, ld->name, ret, tx_bytes); return NETDEV_TX_BUSY; } + if (ret != tx_bytes) { + mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", + iod->name, ld->name, ret, tx_bytes, count); + } + ndev->stats.tx_packets++; - ndev->stats.tx_bytes += tx_bytes; + ndev->stats.tx_bytes += count; return NETDEV_TX_OK; } @@ -1581,16 +1631,19 @@ int sipc5_init_io_device(struct io_device *iod) ret = misc_register(&iod->miscdev); if (ret) mif_info("%s: ERR! misc_register fail\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_waketime); if (ret) mif_info("%s: ERR! device_create_file fail\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_loopback); if (ret) mif_err("failed to create `loopback file' : %s\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_txlink); if (ret) diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c index f5e33d3..8fc65e4 100644 --- a/drivers/misc/modem_if/sipc5_modem.c +++ b/drivers/misc/modem_if/sipc5_modem.c @@ -34,6 +34,7 @@ #include #include +#include #include "modem_prj.h" #include "modem_variation.h" #include "modem_utils.h" @@ -77,27 +78,31 @@ static struct modem_shared *create_modem_shared_data(void) static struct modem_ctl *create_modemctl_device(struct platform_device *pdev, struct modem_shared *msd) { - int ret = 0; - struct modem_data *pdata; + struct modem_data *pdata = pdev->dev.platform_data; struct modem_ctl *modemctl; - struct device *dev = &pdev->dev; + int ret; /* create modem control device */ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL); - if (!modemctl) + if (!modemctl) { + mif_err("%s: modemctl kzalloc fail\n", pdata->name); + mif_err("%s: xxx\n", pdata->name); return NULL; + } modemctl->msd = msd; - modemctl->dev = dev; + modemctl->dev = &pdev->dev; modemctl->phone_state = STATE_OFFLINE; - pdata = pdev->dev.platform_data; modemctl->mdm_data = pdata; modemctl->name = pdata->name; /* init modemctl device for getting modemctl operations */ ret = call_modem_init_func(modemctl, pdata); if (ret) { + mif_err("%s: call_modem_init_func fail (err %d)\n", + pdata->name, ret); + mif_err("%s: xxx\n", pdata->name); kfree(modemctl); return NULL; } @@ -111,8 +116,8 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, struct modem_shared *msd, struct modem_ctl *modemctl, struct modem_data *pdata) { - int ret = 0; - struct io_device *iod = NULL; + int ret; + struct io_device *iod; iod = kzalloc(sizeof(struct io_device), GFP_KERNEL); if (!iod) { @@ -128,6 +133,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, iod->format = io_t->format; iod->io_typ = io_t->io_type; iod->link_types = io_t->links; + iod->app = io_t->app; iod->net_typ = pdata->modem_net; iod->use_handover = pdata->use_handover; iod->ipc_version = pdata->ipc_version; @@ -139,7 +145,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, modemctl->iod = iod; if (iod->format == IPC_BOOT) { modemctl->bootd = iod; - mif_info("Bood device = %s\n", iod->name); + mif_err("BOOT device = %s\n", iod->name); } /* link between io device and modem shared */ @@ -160,7 +166,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, return NULL; } - mif_debug("%s is created!!!\n", iod->name); + mif_info("%s created\n", iod->name); return iod; } @@ -168,7 +174,6 @@ static int attach_devices(struct io_device *iod, enum modem_link tx_link) { struct modem_shared *msd = iod->msd; struct link_device *ld; - unsigned ch; /* find link type for this io device */ list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -232,36 +237,36 @@ static int __devinit modem_probe(struct platform_device *pdev) { int i; struct modem_data *pdata = pdev->dev.platform_data; - struct modem_shared *msd = NULL; - struct modem_ctl *modemctl = NULL; + struct modem_shared *msd; + struct modem_ctl *modemctl; struct io_device *iod[pdata->num_iodevs]; struct link_device *ld; - - mif_err("%s\n", pdev->name); - memset(iod, 0, sizeof(iod)); + mif_err("%s: +++\n", pdata->name); msd = create_modem_shared_data(); if (!msd) { - mif_err("msd == NULL\n"); - goto err_free_modemctl; + mif_err("%s: msd == NULL\n", pdata->name); + return -ENOMEM; } modemctl = create_modemctl_device(pdev, msd); if (!modemctl) { - mif_err("modemctl == NULL\n"); - goto err_free_modemctl; + mif_err("%s: modemctl == NULL\n", pdata->name); + kfree(msd); + return -ENOMEM; } /* create link device */ /* support multi-link device */ + memset(iod, 0, sizeof(iod)); for (i = 0; i < LINKDEV_MAX ; i++) { /* find matching link type */ if (pdata->link_types & LINKTYPE(i)) { ld = call_link_init_func(pdev, i); if (!ld) - goto err_free_modemctl; + goto free_mc; - mif_err("link created: %s\n", ld->name); + mif_err("%s: %s link created\n", pdata->name, ld->name); ld->link_type = i; ld->mc = modemctl; ld->msd = msd; @@ -274,8 +279,8 @@ static int __devinit modem_probe(struct platform_device *pdev) iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl, pdata); if (!iod[i]) { - mif_err("iod[%d] == NULL\n", i); - goto err_free_modemctl; + mif_err("%s: iod[%d] == NULL\n", pdata->name, i); + goto free_iod; } attach_devices(iod[i], pdata->iodevs[i].tx_link); @@ -283,21 +288,23 @@ static int __devinit modem_probe(struct platform_device *pdev) platform_set_drvdata(pdev, modemctl); - mif_err("Complete!!!\n"); - + mif_err("%s: ---\n", pdata->name); return 0; -err_free_modemctl: - for (i = 0; i < pdata->num_iodevs; i++) - if (iod[i] != NULL) +free_iod: + for (i = 0; i < pdata->num_iodevs; i++) { + if (iod[i]) kfree(iod[i]); + } - if (modemctl != NULL) +free_mc: + if (modemctl) kfree(modemctl); - if (msd != NULL) + if (msd) kfree(msd); + mif_err("%s: xxx\n", pdata->name); return -ENOMEM; } @@ -305,13 +312,19 @@ static void modem_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct modem_ctl *mc = dev_get_drvdata(dev); + struct utc_time utc; + mc->ops.modem_off(mc); mc->phone_state = STATE_OFFLINE; + + get_utc_time(&utc); + mif_info("%s: at [%02d:%02d:%02d.%03d]\n", + mc->name, utc.hour, utc.min, utc.sec, utc.msec); } static int modem_suspend(struct device *pdev) { -#ifndef CONFIG_LINK_DEVICE_HSIC +#if !defined(CONFIG_LINK_DEVICE_HSIC) struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -323,7 +336,7 @@ static int modem_suspend(struct device *pdev) static int modem_resume(struct device *pdev) { -#ifndef CONFIG_LINK_DEVICE_HSIC +#if !defined(CONFIG_LINK_DEVICE_HSIC) struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -334,8 +347,8 @@ static int modem_resume(struct device *pdev) } static const struct dev_pm_ops modem_pm_ops = { - .suspend = modem_suspend, - .resume = modem_resume, + .suspend = modem_suspend, + .resume = modem_resume, }; static struct platform_driver modem_driver = { @@ -343,7 +356,7 @@ static struct platform_driver modem_driver = { .shutdown = modem_shutdown, .driver = { .name = "mif_sipc5", - .pm = &modem_pm_ops, + .pm = &modem_pm_ops, }, }; diff --git a/include/linux/platform_data/modem.h b/include/linux/platform_data/modem.h old mode 100644 new mode 100755 index 9598763..bc4433e --- a/include/linux/platform_data/modem.h +++ b/include/linux/platform_data/modem.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -16,14 +15,21 @@ #ifndef __MODEM_IF_H__ #define __MODEM_IF_H__ +#include +#include + enum modem_t { IMC_XMM6260, IMC_XMM6262, VIA_CBP71, VIA_CBP72, + VIA_CBP82, + SEC_CMC220, SEC_CMC221, + SEC_SS222, QC_MDM6600, QC_ESC6270, + QC_QSC6085, SPRD_SC8803, DUMMY, MAX_MODEM_TYPE @@ -33,10 +39,11 @@ enum dev_format { IPC_FMT, IPC_RAW, IPC_RFS, + IPC_MULTI_RAW, IPC_CMD, IPC_BOOT, - IPC_MULTI_RAW, IPC_RAMDUMP, + IPC_DEBUG, MAX_DEV_FORMAT, }; #define MAX_IPC_DEV (IPC_RFS + 1) /* FMT, RAW, RFS */ @@ -51,13 +58,14 @@ enum modem_io { enum modem_link { LINKDEV_UNDEFINED, LINKDEV_MIPI, - LINKDEV_DPRAM, - LINKDEV_SPI, LINKDEV_USB, LINKDEV_HSIC, - LINKDEV_C2C, + LINKDEV_DPRAM, LINKDEV_PLD, - LINKDEV_MAX, + LINKDEV_C2C, + LINKDEV_SHMEM, + LINKDEV_SPI, + LINKDEV_MAX }; #define LINKTYPE(modem_link) (1u << (modem_link)) @@ -66,6 +74,12 @@ enum modem_network { CDMA_NETWORK, TDSCDMA_NETWORK, LTE_NETWORK, + MAX_MODEM_NETWORK +}; + +enum ap_type { + S5P, + MAX_AP_TYPE }; enum sipc_ver { @@ -74,15 +88,16 @@ enum sipc_ver { SIPC_VER_41 = 41, SIPC_VER_42 = 42, SIPC_VER_50 = 50, - MAX_SIPC_VER, + MAX_SIPC_VER }; /** * struct modem_io_t - declaration for io_device * @name: device name - * @id: contain format & channel information + * @id: for SIPC4, contains format & channel information * (id & 11100000b)>>5 = format (eg, 0=FMT, 1=RAW, 2=RFS) * (id & 00011111b) = channel (valid only if format is RAW) + * for SIPC5, contains only 8-bit channel ID * @format: device format * @io_type: type of this io_device * @links: list of link_devices to use this io_device @@ -94,7 +109,7 @@ enum sipc_ver { * TX is only one link_device. * @app: the name of the application that will use this IO device * - * This structure is used in board-*-modem.c + * This structure is used in board-*-modems.c */ struct modem_io_t { char *name; @@ -103,11 +118,7 @@ struct modem_io_t { enum modem_io io_type; enum modem_link links; enum modem_link tx_link; -#ifdef CONFIG_MACH_U1 char *app; -#else - bool rx_gather; -#endif }; struct modemlink_pm_data { @@ -126,10 +137,14 @@ struct modemlink_pm_data { void *hub_pm_data; bool has_usbhub; +#ifdef CONFIG_EXYNOS4_CPUFREQ /* cpu/bus frequency lock */ atomic_t freqlock; + atomic_t freq_dpramlock; int (*freq_lock)(struct device *dev); int (*freq_unlock)(struct device *dev); + unsigned gpio_cpufreq_lock; +#endif int autosuspend_delay_ms; /* if zero, the default value is used */ void (*ehci_reg_dump)(struct device *); @@ -140,31 +155,25 @@ struct modemlink_pm_link_activectl { int gpio_request_host_active; }; -#define RES_CP_ACTIVE_IRQ_ID 0 -#define RES_DPRAM_MEM_ID 1 -#define RES_DPRAM_IRQ_ID 2 -#define RES_DPRAM_SFR_ID 3 +#define RES_DPRAM_MEM_ID 0 +#define RES_DPRAM_SFR_ID 1 -#ifdef CONFIG_MACH_U1 -#define STR_CP_ACTIVE_IRQ "cp_active_irq" #define STR_DPRAM_BASE "dpram_base" -#define STR_DPRAM_IRQ "dpram_irq" #define STR_DPRAM_SFR_BASE "dpram_sfr_base" -#endif enum dpram_type { EXT_DPRAM, AP_IDPRAM, CP_IDPRAM, - SHM_DPRAM, + PLD_DPRAM, MAX_DPRAM_TYPE }; -#define DPRAM_SIZE_8KB 0x02000 -#define DPRAM_SIZE_16KB 0x04000 -#define DPRAM_SIZE_32KB 0x08000 -#define DPRAM_SIZE_64KB 0x10000 -#define DPRAM_SIZE_128KB 0x20000 +#define DPRAM_SIZE_8KB (8 << 10) +#define DPRAM_SIZE_16KB (16 << 10) +#define DPRAM_SIZE_32KB (32 << 10) +#define DPRAM_SIZE_64KB (64 << 10) +#define DPRAM_SIZE_128KB (128 << 10) enum dpram_speed { DPRAM_SPEED_LOW, @@ -193,7 +202,16 @@ struct dpram_ipc_device { }; struct dpram_ipc_map { -#if defined(CONFIG_LINK_DEVICE_PLD) + u16 __iomem *magic; + u16 __iomem *access; + + struct dpram_ipc_device dev[MAX_IPC_DEV]; + + u16 __iomem *mbx_cp2ap; + u16 __iomem *mbx_ap2cp; +}; + +struct pld_ipc_map { u16 __iomem *mbx_ap2cp; u16 __iomem *magic_ap2cp; u16 __iomem *access_ap2cp; @@ -205,50 +223,52 @@ struct dpram_ipc_map { struct dpram_ipc_device dev[MAX_IPC_DEV]; u16 __iomem *address_buffer; -#else - u16 __iomem *magic; - u16 __iomem *access; - - struct dpram_ipc_device dev[MAX_IPC_DEV]; - - u16 __iomem *mbx_cp2ap; - u16 __iomem *mbx_ap2cp; -#endif }; -struct modemlink_dpram_control { - void (*reset)(void); - void (*clear_intr)(void); - u16 (*recv_intr)(void); - void (*send_intr)(u16); - u16 (*recv_msg)(void); - void (*send_msg)(u16); +struct modemlink_dpram_data { + enum dpram_type type; /* DPRAM type */ + enum ap_type ap; /* AP type for AP_IDPRAM */ - int (*wakeup)(void); - void (*sleep)(void); + /* Stirct I/O access (e.g. ioread16(), etc.) is required */ + bool strict_io_access; - void (*setup_speed)(enum dpram_speed); + /* Aligned access is required */ + int aligned; - enum dpram_type dp_type; /* DPRAM type */ - int aligned; /* aligned access is required */ -#ifdef CONFIG_MACH_U1 - bool disabled; /* Disabled during phone booting */ -#endif - u8 __iomem *dp_base; - u32 dp_size; + /* Disabled during phone booting */ + bool disabled; - int dpram_irq; - unsigned long dpram_irq_flags; + /* Virtual base address and size */ + u8 __iomem *base; + u32 size; - int max_ipc_dev; - struct dpram_ipc_map *ipc_map; + /* Pointer to an IPC map (DPRAM or PLD) */ + void *ipc_map; + + /* Timeout of waiting for RES_ACK from CP (in msec) */ + unsigned long res_ack_wait_timeout; unsigned boot_size_offset; unsigned boot_tag_offset; unsigned boot_count_offset; unsigned max_boot_frame_size; + + void (*setup_speed)(enum dpram_speed); + void (*clear_int2ap)(void); }; +enum shmem_type { + REAL_SHMEM, + C2C_SHMEM, + MAX_SHMEM_TYPE +}; + +#define STR_SHMEM_BASE "shmem_base" + +#define SHMEM_SIZE_1MB (1 << 20) /* 1 MB */ +#define SHMEM_SIZE_2MB (2 << 20) /* 2 MB */ +#define SHMEM_SIZE_4MB (4 << 20) /* 4 MB */ + /* platform data */ struct modem_data { char *name; @@ -257,20 +277,45 @@ struct modem_data { unsigned gpio_cp_off; unsigned gpio_reset_req_n; unsigned gpio_cp_reset; + + /* for broadcasting AP's PM state (active or sleep) */ unsigned gpio_pda_active; + + /* for checking aliveness of CP */ unsigned gpio_phone_active; + int irq_phone_active; + + /* for AP-CP IPC interrupt */ + unsigned gpio_ipc_int2ap; + int irq_ipc_int2ap; + unsigned long irqf_ipc_int2ap; /* IRQ flags */ + unsigned gpio_ipc_int2cp; + + /* for AP-CP power management (PM) handshaking */ + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* for USB/HSIC PM */ + unsigned gpio_host_wakeup; + int irq_host_wakeup; + unsigned gpio_host_active; + unsigned gpio_slave_wakeup; + unsigned gpio_hub_suspend; + unsigned gpio_cp_dump_int; unsigned gpio_ap_dump_int; unsigned gpio_flm_uart_sel; + unsigned gpio_cp_warm_reset; #if defined(CONFIG_MACH_M0_CTC) unsigned gpio_flm_uart_sel_rev06; - unsigned gpio_host_wakeup; #endif - unsigned gpio_cp_warm_reset; + unsigned gpio_sim_detect; -#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD) - unsigned gpio_dpram_int; -#endif + int irq_sim_detect; #ifdef CONFIG_LINK_DEVICE_PLD unsigned gpio_fpga1_creset; @@ -284,14 +329,6 @@ struct modem_data { unsigned gpio_fpga2_cs_n; #endif -#ifdef CONFIG_LTE_MODEM_CMC221 - unsigned gpio_dpram_status; - unsigned gpio_dpram_wakeup; - unsigned gpio_slave_wakeup; - unsigned gpio_host_active; - unsigned gpio_host_wakeup; - int irq_host_wakeup; -#endif #ifdef CONFIG_MACH_U1_KOR_LGT unsigned gpio_cp_reset_msm; unsigned gpio_boot_sw_sel; @@ -316,41 +353,74 @@ struct modem_data { #endif /* Switch with 2 links in a modem */ - unsigned gpio_dynamic_switching; + unsigned gpio_link_switch; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + unsigned gpio_cpufreq_lock; +#endif /* Modem component */ - enum modem_network modem_net; - enum modem_t modem_type; - enum modem_link link_types; - char *link_name; -#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD) + enum modem_network modem_net; + enum modem_t modem_type; + enum modem_link link_types; + char *link_name; + /* Link to DPRAM control functions dependent on each platform */ - struct modemlink_dpram_control *dpram_ctl; -#endif + struct modemlink_dpram_data *dpram; /* SIPC version */ enum sipc_ver ipc_version; + /* the number of real IPC devices -> (IPC_RAW + 1) or (IPC_RFS + 1) */ + int max_ipc_dev; + /* Information of IO devices */ - unsigned num_iodevs; - struct modem_io_t *iodevs; + unsigned num_iodevs; + struct modem_io_t *iodevs; /* Modem link PM support */ struct modemlink_pm_data *link_pm_data; - void (*gpio_revers_bias_clear)(void); - void (*gpio_revers_bias_restore)(void); - /* Handover with 2+ modems */ bool use_handover; - /* Debugging option */ - bool use_mif_log; /* SIM Detect polarity */ bool sim_polarity; + + void (*gpio_revers_bias_clear)(void); + void (*gpio_revers_bias_restore)(void); }; -#define LOG_TAG "mif: " +#define MODEM_BOOT_DEV_SPI "modem_boot_spi" + +struct modem_boot_spi_platform_data { + const char *name; + unsigned int gpio_cp_status; +}; + +struct modem_boot_spi { + struct miscdevice dev; + struct spi_device *spi_dev; + struct mutex lock; + unsigned gpio_cp_status; +}; +#define to_modem_boot_spi(misc) container_of(misc, struct modem_boot_spi, dev); + +struct utc_time { + u16 year; + u8 mon:4, + day:4; + u8 hour; + u8 min; + u8 sec; + u16 msec; +} __packed; + +extern void get_utc_time(struct utc_time *utc); + +#define LOG_TAG "mif: " +#define CALLER (__builtin_return_address(0)) #define mif_err(fmt, ...) \ pr_err(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) -- cgit v1.1 From 25e56ac917a9c8ace9d03d29e1e6ff9a54beb54d Mon Sep 17 00:00:00 2001 From: RGIB Date: Thu, 26 May 2016 10:24:00 +0200 Subject: smdk4412 : function moved to modem.h Change-Id: Idb3a6fee0585177e5b7a4e152a8179c93814c36b --- drivers/misc/modem_if_u1/modem_utils.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/misc/modem_if_u1/modem_utils.h b/drivers/misc/modem_if_u1/modem_utils.h index 219e059..b9b79c4 100644 --- a/drivers/misc/modem_if_u1/modem_utils.h +++ b/drivers/misc/modem_if_u1/modem_utils.h @@ -98,18 +98,7 @@ struct mif_time_block { char buff[MAX_TIM_LOG_SIZE]; }; -struct utc_time { - u16 year; - u8 mon:4, - day:4; - u8 hour; - u8 min; - u8 sec; - u16 msec; -} __packed; - void ts2utc(struct timespec *ts, struct utc_time *utc); -void get_utc_time(struct utc_time *utc); int mif_dump_dpram(struct io_device *); int mif_dump_log(struct modem_shared *, struct io_device *); -- cgit v1.1 From 9ed4ef902d589d7b112b0b8d26373c4384a85c21 Mon Sep 17 00:00:00 2001 From: Roberto Gibellini Date: Fri, 27 May 2016 02:34:15 -0700 Subject: Revert "smdk4412 : function moved to modem.h" This reverts commit 25e56ac917a9c8ace9d03d29e1e6ff9a54beb54d. Change-Id: Ied9388c7dc7e1b9c0845fb18012eb58057bc7865 --- drivers/misc/modem_if_u1/modem_utils.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/misc/modem_if_u1/modem_utils.h b/drivers/misc/modem_if_u1/modem_utils.h index b9b79c4..219e059 100644 --- a/drivers/misc/modem_if_u1/modem_utils.h +++ b/drivers/misc/modem_if_u1/modem_utils.h @@ -98,7 +98,18 @@ struct mif_time_block { char buff[MAX_TIM_LOG_SIZE]; }; +struct utc_time { + u16 year; + u8 mon:4, + day:4; + u8 hour; + u8 min; + u8 sec; + u16 msec; +} __packed; + void ts2utc(struct timespec *ts, struct utc_time *utc); +void get_utc_time(struct utc_time *utc); int mif_dump_dpram(struct io_device *); int mif_dump_log(struct modem_shared *, struct io_device *); -- cgit v1.1 From 206f43e8ae09166ba608f520c60bb208661769ca Mon Sep 17 00:00:00 2001 From: Roberto Gibellini Date: Fri, 27 May 2016 02:34:23 -0700 Subject: Revert "smdk4412 : modem_if KK driver from N5100ZTCNL4" This reverts commit 540f1d84d4f8fb27e73dfcb6f3b13c39fa667041. Change-Id: I8671bdc7f46a11375b6f710efa4af6bf32aea908 --- drivers/misc/modem_if/Kconfig | 29 +- drivers/misc/modem_if/Makefile | 34 +- drivers/misc/modem_if/modem_boot_device_spi.c | 279 -- drivers/misc/modem_if/modem_link_device_c2c.c | 2281 +------------- drivers/misc/modem_if/modem_link_device_c2c.h | 202 +- drivers/misc/modem_if/modem_link_device_dpram.c | 3296 +++++++------------- drivers/misc/modem_if/modem_link_device_dpram.h | 455 +-- .../misc/modem_if/modem_link_device_dpram_ext_op.c | 1619 +++------- drivers/misc/modem_if/modem_link_device_hsic.c | 31 +- drivers/misc/modem_if/modem_link_device_memory.c | 496 --- drivers/misc/modem_if/modem_link_device_memory.h | 409 --- drivers/misc/modem_if/modem_link_device_pld.c | 1464 +++++---- drivers/misc/modem_if/modem_link_device_pld.h | 578 ++-- .../misc/modem_if/modem_link_device_pld_ext_op.c | 277 +- drivers/misc/modem_if/modem_link_device_shmem.h | 700 ----- drivers/misc/modem_if/modem_link_device_spi.c | 244 +- drivers/misc/modem_if/modem_link_device_spi.h | 22 +- drivers/misc/modem_if/modem_link_device_usb.c | 14 +- drivers/misc/modem_if/modem_link_pm_usb.c | 29 +- .../misc/modem_if/modem_modemctl_device_cbp72.c | 43 +- .../misc/modem_if/modem_modemctl_device_cbp82.c | 288 -- .../misc/modem_if/modem_modemctl_device_cmc221.c | 105 +- .../misc/modem_if/modem_modemctl_device_esc6270.c | 16 +- .../misc/modem_if/modem_modemctl_device_mdm6600.c | 41 +- .../misc/modem_if/modem_modemctl_device_qsc6085.c | 218 -- .../misc/modem_if/modem_modemctl_device_sprd8803.c | 58 +- .../misc/modem_if/modem_modemctl_device_ss222.c | 312 -- .../misc/modem_if/modem_modemctl_device_xmm6260.c | 10 +- .../misc/modem_if/modem_modemctl_device_xmm6262.c | 90 +- drivers/misc/modem_if/modem_prj.h | 514 ++- drivers/misc/modem_if/modem_sim_slot_switch.c | 11 +- drivers/misc/modem_if/modem_utils.c | 669 ++-- drivers/misc/modem_if/modem_utils.h | 56 +- drivers/misc/modem_if/modem_variation.h | 104 +- drivers/misc/modem_if/sipc4_io_device.c | 309 +- drivers/misc/modem_if/sipc4_modem.c | 13 - drivers/misc/modem_if/sipc5_common.c | 239 -- drivers/misc/modem_if/sipc5_io_device.c | 1559 +++++---- drivers/misc/modem_if/sipc5_modem.c | 85 +- include/linux/platform_data/modem.h | 244 +- 40 files changed, 5247 insertions(+), 12196 deletions(-) delete mode 100644 drivers/misc/modem_if/modem_boot_device_spi.c delete mode 100644 drivers/misc/modem_if/modem_link_device_memory.c delete mode 100644 drivers/misc/modem_if/modem_link_device_memory.h delete mode 100644 drivers/misc/modem_if/modem_link_device_shmem.h delete mode 100644 drivers/misc/modem_if/modem_modemctl_device_cbp82.c delete mode 100644 drivers/misc/modem_if/modem_modemctl_device_qsc6085.c delete mode 100644 drivers/misc/modem_if/modem_modemctl_device_ss222.c delete mode 100644 drivers/misc/modem_if/sipc5_common.c mode change 100755 => 100644 include/linux/platform_data/modem.h diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig index 52591dc..6a6eeab 100644 --- a/drivers/misc/modem_if/Kconfig +++ b/drivers/misc/modem_if/Kconfig @@ -24,21 +24,11 @@ config CDMA_MODEM_CBP72 depends on SEC_MODEM default n -config CDMA_MODEM_CBP82 - bool "modem chip : VIA CBP8.2" - depends on SEC_MODEM - default n - config LTE_MODEM_CMC221 bool "modem chip : SEC CMC221" depends on SEC_MODEM default n -config UMTS_MODEM_SS222 - bool "modem chip : SEC SS222" - depends on SEC_MODEM - default n - config CDMA_MODEM_MDM6600 bool "modem chip : QC MDM6600" depends on SEC_MODEM @@ -54,11 +44,6 @@ config GSM_MODEM_ESC6270 depends on SEC_MODEM default n -config CDMA_MODEM_QSC6085 - bool "modem chip : Qualcomm QSC6085" - depends on SEC_MODEM - default n - config LINK_DEVICE_MIPI bool "modem driver link device MIPI-HSI" depends on SEC_MODEM @@ -73,7 +58,6 @@ config LINK_DEVICE_PLD bool "modem driver link device PLD" depends on SEC_MODEM default n - config LINK_DEVICE_USB bool "modem driver link device USB" depends on SEC_MODEM @@ -94,13 +78,18 @@ config LINK_DEVICE_SPI depends on SEC_MODEM default n -config BOOT_DEVICE_SPI - bool "modem driver boot device SPI" +config WORKQUEUE_FRONT + bool "IPC: SPI workqueue front" depends on SEC_MODEM default n -config WORKQUEUE_FRONT - bool "IPC: SPI workqueue front" +config IPC_CMC22x_OLD_RFS + bool "IPC: CMC22x ancient RFS" + depends on SEC_MODEM + default n + +config SIPC_VER_5 + bool "IPC: Samsung IPC 5.0" depends on SEC_MODEM default n diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile index 6aa28fc..5bd62ea 100644 --- a/drivers/misc/modem_if/Makefile +++ b/drivers/misc/modem_if/Makefile @@ -2,7 +2,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if -obj-y += sipc5_modem.o sipc5_io_device.o sipc5_common.o +obj-y += sipc5_modem.o sipc5_io_device.o obj-y += sipc4_modem.o sipc4_io_device.o obj-y += modem_net_flowcontrol_device.o modem_utils.o @@ -10,43 +10,17 @@ obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o -obj-$(CONFIG_CDMA_MODEM_CBP82) += modem_modemctl_device_cbp82.o -obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o -obj-$(CONFIG_UMTS_MODEM_SS222) += modem_modemctl_device_ss222.o +obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o -obj-$(CONFIG_CDMA_MODEM_QSC6085) += modem_modemctl_device_qsc6085.o obj-$(CONFIG_TDSCDMA_MODEM_SPRD8803) += modem_modemctl_device_sprd8803.o obj-$(CONFIG_LINK_DEVICE_MIPI) += modem_link_device_mipi.o -obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o -obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o +obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o +obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o obj-$(CONFIG_LINK_DEVICE_C2C) += modem_link_device_c2c.o obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o -obj-$(CONFIG_BOOT_DEVICE_SPI) += modem_boot_device_spi.o - obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o - -# Check whether or not memory-type interface -ifeq ($(CONFIG_LINK_DEVICE_DPRAM),y) -LINK_DEVICE_MEMORY_INTERFACE=y -endif - -ifeq ($(CONFIG_LINK_DEVICE_PLD),y) -LINK_DEVICE_MEMORY_INTERFACE=y -endif - -ifeq ($(CONFIG_LINK_DEVICE_C2C),y) -LINK_DEVICE_MEMORY_INTERFACE=y -endif - -ifeq ($(CONFIG_LINK_DEVICE_SHMEM),y) -LINK_DEVICE_MEMORY_INTERFACE=y -endif - -ifdef LINK_DEVICE_MEMORY_INTERFACE -obj-y += modem_link_device_memory.o -endif diff --git a/drivers/misc/modem_if/modem_boot_device_spi.c b/drivers/misc/modem_if/modem_boot_device_spi.c deleted file mode 100644 index 8cee588..0000000 --- a/drivers/misc/modem_if/modem_boot_device_spi.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics. - * - * 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 "modem_prj.h" - -#define SPI_XMIT_DELEY 100 - -static int check_cp_status(unsigned int gpio_cp_status, unsigned int count) -{ - int ret = 0; - int cnt = 0; - - while (1) { - if (gpio_get_value(gpio_cp_status) != 0) { - ret = 0; - break; - } - - cnt++; - if (cnt >= count) { - mif_err("ERR! gpio_cp_status == 0 (cnt %d)\n", cnt); - ret = -EFAULT; - break; - } - - msleep(20); - } - - return ret; -} - -static inline int spi_send(struct modem_boot_spi *loader, const char val) -{ - char buff[1]; - int ret; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 1, - .tx_buf = buff, - }; - - buff[0] = val; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - ret = spi_sync(loader->spi_dev, &msg); - if (ret < 0) - mif_err("ERR! spi_sync fail (err %d)\n", ret); - - return ret; -} - -static int spi_boot_write(struct modem_boot_spi *loader, const char *addr, - const long len) -{ - int i; - int ret = 0; - char *buff = NULL; - mif_err("+++\n"); - - buff = kzalloc(len, GFP_KERNEL); - if (!buff) { - mif_err("ERR! kzalloc(%ld) fail\n", len); - ret = -ENOMEM; - goto exit; - } - - ret = copy_from_user(buff, (const void __user *)addr, len); - if (ret) { - mif_err("ERR! copy_from_user fail (err %d)\n", ret); - ret = -EFAULT; - goto exit; - } - - for (i = 0 ; i < len ; i++) { - ret = spi_send(loader, buff[i]); - if (ret < 0) { - mif_err("ERR! spi_send fail (err %d)\n", ret); - goto exit; - } - } - -exit: - if (buff) - kfree(buff); - - mif_err("---\n"); - return ret; -} - -static int spi_boot_open(struct inode *inode, struct file *filp) -{ - struct modem_boot_spi *loader = to_modem_boot_spi(filp->private_data); - filp->private_data = loader; - return 0; -} - -static long spi_boot_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - int ret = 0; - struct modem_firmware img; - struct modem_boot_spi *loader = filp->private_data; - - mutex_lock(&loader->lock); - switch (cmd) { - case IOCTL_MODEM_XMIT_BOOT: - ret = copy_from_user(&img, (const void __user *)arg, - sizeof(struct modem_firmware)); - if (ret) { - mif_err("ERR! copy_from_user fail (err %d)\n", ret); - ret = -EFAULT; - goto exit_err; - } - mif_info("IOCTL_MODEM_XMIT_BOOT (size %d)\n", img.size); - - ret = spi_boot_write(loader, img.binary, img.size); - if (ret < 0) { - mif_err("ERR! spi_boot_write fail (err %d)\n", ret); - break; - } - - if (!loader->gpio_cp_status) - break; - - ret = check_cp_status(loader->gpio_cp_status, 100); - if (ret < 0) - mif_err("ERR! check_cp_status fail (err %d)\n", ret); - - break; - - default: - mif_err("ioctl cmd error\n"); - ret = -ENOIOCTLCMD; - - break; - } - mutex_unlock(&loader->lock); - -exit_err: - return ret; -} - -static const struct file_operations modem_spi_boot_fops = { - .owner = THIS_MODULE, - .open = spi_boot_open, - .unlocked_ioctl = spi_boot_ioctl, -}; - -static int __devinit modem_spi_boot_probe(struct spi_device *spi) -{ - int ret; - struct modem_boot_spi *loader; - struct modem_boot_spi_platform_data *pdata; - mif_debug("+++\n"); - - loader = kzalloc(sizeof(*loader), GFP_KERNEL); - if (!loader) { - mif_err("failed to allocate for modem_boot_spi\n"); - ret = -ENOMEM; - goto err_alloc; - } - mutex_init(&loader->lock); - - spi->bits_per_word = 8; - - if (spi_setup(spi)) { - mif_err("ERR! spi_setup fail\n"); - ret = -EINVAL; - goto err_setup; - } - loader->spi_dev = spi; - - if (!spi->dev.platform_data) { - mif_err("ERR! no platform_data\n"); - ret = -EINVAL; - goto err_setup; - } - pdata = (struct modem_boot_spi_platform_data *)spi->dev.platform_data; - - loader->gpio_cp_status = pdata->gpio_cp_status; - - spi_set_drvdata(spi, loader); - - loader->dev.minor = MISC_DYNAMIC_MINOR; - loader->dev.name = MODEM_BOOT_DEV_SPI; - loader->dev.fops = &modem_spi_boot_fops; - ret = misc_register(&loader->dev); - if (ret) { - mif_err("ERR! misc_register fail (err %d)\n", ret); - goto err_setup; - } - - mif_debug("---\n"); - return 0; - -err_setup: - mutex_destroy(&loader->lock); - kfree(loader); - -err_alloc: - mif_err("xxx\n"); - return ret; -} - -static int __devexit modem_spi_boot_remove(struct spi_device *spi) -{ - struct modem_boot_spi *loader = spi_get_drvdata(spi); - - misc_deregister(&loader->dev); - mutex_destroy(&loader->lock); - kfree(loader); - - return 0; -} - -static struct spi_driver modem_boot_device_spi_driver = { - .driver = { - .name = MODEM_BOOT_DEV_SPI, - .owner = THIS_MODULE, - }, - .probe = modem_spi_boot_probe, - .remove = __devexit_p(modem_spi_boot_remove), -}; - -static int __init modem_boot_device_spi_init(void) -{ - int err; - - err = spi_register_driver(&modem_boot_device_spi_driver); - if (err) { - mif_err("spi_register_driver fail (err %d)\n", err); - return err; - } - - return 0; -} - -static void __exit modem_boot_device_spi_exit(void) -{ - spi_unregister_driver(&modem_boot_device_spi_driver); -} - -module_init(modem_boot_device_spi_init); -module_exit(modem_boot_device_spi_exit); - -MODULE_DESCRIPTION("SPI Driver for Downloading Modem Bootloader"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/misc/modem_if/modem_link_device_c2c.c b/drivers/misc/modem_if/modem_link_device_c2c.c index 15665f7..acbaadf 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.c +++ b/drivers/misc/modem_if/modem_link_device_c2c.c @@ -1,4 +1,6 @@ -/* +/* /linux/drivers/new_modem_if/link_dev_c2c.c + * + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,9 +14,9 @@ * */ +#include #include #include -#include #include #include #include @@ -22,2279 +24,38 @@ #include #include #include +#include #include #include -#include -#include -#ifdef CONFIG_FB -#include -#endif -#include -#include -#include -#include #include +#include #include "modem_prj.h" -#include "modem_utils.h" #include "modem_link_device_c2c.h" -static void trigger_forced_cp_crash(struct shmem_link_device *shmd); - -#ifdef DEBUG_MODEM_IF -static void save_mem_dump(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - char *path = shmd->dump_path; - struct file *fp; - struct utc_time t; - - get_utc_time(&t); - snprintf(path, MIF_MAX_PATH_LEN, "%s/%s_%d%02d%02d_%02d%02d%02d.dump", - MIF_LOG_DIR, ld->name, t.year, t.mon, t.day, t.hour, t.min, - t.sec); - - fp = mif_open_file(path); - if (!fp) { - mif_err("%s: ERR! %s open fail\n", ld->name, path); - return; - } - mif_err("%s: %s opened\n", ld->name, path); - - mif_save_file(fp, shmd->base, shmd->size); - - mif_close_file(fp); -} - -/** - * mem_dump_work - * @ws: pointer to an instance of work_struct structure - * - * Performs actual file operation for saving a DPRAM dump. - */ -static void mem_dump_work(struct work_struct *ws) -{ - struct shmem_link_device *shmd; - - shmd = container_of(ws, struct shmem_link_device, dump_dwork.work); - if (!shmd) { - mif_err("ERR! no shmd\n"); - return; - } - - save_mem_dump(shmd); -} -#endif - -static void print_pm_status(struct shmem_link_device *shmd, int level) -{ -#ifdef DEBUG_MODEM_IF - struct utc_time t; - u32 magic; - int ap_wakeup; - int ap_status; - int cp_wakeup; - int cp_status; - - if (level < 0) - return; - - get_utc_time(&t); - magic = get_magic(shmd); - ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); - ap_status = gpio_get_value(shmd->gpio_ap_status); - cp_wakeup = gpio_get_value(shmd->gpio_cp_wakeup); - cp_status = gpio_get_value(shmd->gpio_cp_status); - - /* - ** PM {ap_wakeup:cp_wakeup:cp_status:ap_status:magic:state} CALLER - */ - if (level > 0) { - pr_err(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " - "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, - cp_wakeup, cp_status, ap_status, magic, CALLER); - } else { - pr_info(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " - "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, - cp_wakeup, cp_status, ap_status, magic, CALLER); - } -#endif -} - -static inline void s5p_change_irq_type(int irq, int value) -{ - irq_set_irq_type(irq, value ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH); - /** - * Exynos BSP has a problem when using level-triggering interrupt. - * If the irq type is changed in an interrupt handler, the handler will - * be called again. - * Below is a temporary solution until SYS.LSI resolves this problem. - */ - __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); -} - -/** - * recv_int2ap - * @shmd: pointer to an instance of shmem_link_device structure - * - * Returns the value of the CP-to-AP interrupt register. - */ -static inline u16 recv_int2ap(struct shmem_link_device *shmd) -{ - if (shmd->type == C2C_SHMEM) - return (u16)c2c_read_interrupt(); - - if (shmd->mbx2ap) - return *shmd->mbx2ap; - - return 0; -} - -/** - * send_int2cp - * @shmd: pointer to an instance of shmem_link_device structure - * @mask: value to be written to the AP-to-CP interrupt register - */ -static inline void send_int2cp(struct shmem_link_device *shmd, u16 mask) -{ - struct link_device *ld = &shmd->ld; - - if (ld->mode != LINK_MODE_IPC) - mif_info("%s: mask = 0x%04X\n", ld->name, CALLER, mask); - - if (shmd->type == C2C_SHMEM) - c2c_send_interrupt(mask); - - if (shmd->mbx2cp) - *shmd->mbx2cp = mask; -} - -/** - * get_shmem_status - * @shmd: pointer to an instance of shmem_link_device structure - * @dir: direction of communication (TX or RX) - * @mst: pointer to an instance of mem_status structure - * - * Takes a snapshot of the current status of a SHMEM. - */ -static void get_shmem_status(struct shmem_link_device *shmd, - enum circ_dir_type dir, struct mem_status *mst) -{ -#ifdef DEBUG_MODEM_IF - getnstimeofday(&mst->ts); -#endif - - mst->dir = dir; - mst->magic = get_magic(shmd); - mst->access = get_access(shmd); - mst->head[IPC_FMT][TX] = get_txq_head(shmd, IPC_FMT); - mst->tail[IPC_FMT][TX] = get_txq_tail(shmd, IPC_FMT); - mst->head[IPC_FMT][RX] = get_rxq_head(shmd, IPC_FMT); - mst->tail[IPC_FMT][RX] = get_rxq_tail(shmd, IPC_FMT); - mst->head[IPC_RAW][TX] = get_txq_head(shmd, IPC_RAW); - mst->tail[IPC_RAW][TX] = get_txq_tail(shmd, IPC_RAW); - mst->head[IPC_RAW][RX] = get_rxq_head(shmd, IPC_RAW); - mst->tail[IPC_RAW][RX] = get_rxq_tail(shmd, IPC_RAW); -} - -static inline int check_link_status(struct shmem_link_device *shmd) -{ - u32 magic = get_magic(shmd); - int cnt; - - if (gpio_get_value(shmd->gpio_cp_status) != 0 && magic == SHM_IPC_MAGIC) - return 0; - - cnt = 0; - while (gpio_get_value(shmd->gpio_cp_status) == 0) { - if (gpio_get_value(shmd->gpio_ap_status) == 0) { - print_pm_status(shmd, 1); - gpio_set_value(shmd->gpio_ap_status, 1); - } - - cnt++; - if (cnt >= 100) { - mif_err("ERR! cp_status != 1 (cnt %d)\n", cnt); - return -EACCES; - } - - if (in_interrupt()) - udelay(100); - else - usleep_range(100, 200); - } - - cnt = 0; - while (1) { - magic = get_magic(shmd); - if (magic == SHM_IPC_MAGIC) - break; - - cnt++; - if (cnt >= 100) { - mif_err("ERR! magic 0x%X != SHM_IPC_MAGIC (cnt %d)\n", - magic, cnt); - return -EACCES; - } - - if (in_interrupt()) - udelay(100); - else - usleep_range(100, 200); - } - - return 0; -} - -static void release_cp_wakeup(struct work_struct *ws) -{ - struct shmem_link_device *shmd; - struct link_device *ld; - int i; - unsigned long flags; - - shmd = container_of(ws, struct shmem_link_device, cp_sleep_dwork.work); - - spin_lock_irqsave(&shmd->pm_lock, flags); - i = atomic_read(&shmd->ref_cnt); - spin_unlock_irqrestore(&shmd->pm_lock, flags); - if (i > 0) - goto reschedule; - - /* - * If there is any IPC message remained in a TXQ, AP must prevent CP - * from going to sleep. - */ - ld = &shmd->ld; - for (i = 0; i < ld->max_ipc_dev; i++) { - if (ld->skb_txq[i]->qlen > 0) - goto reschedule; - } - - if (gpio_get_value(shmd->gpio_ap_wakeup)) - goto reschedule; - - gpio_set_value(shmd->gpio_cp_wakeup, 0); - print_pm_status(shmd, 1); - return; - -reschedule: - if (!work_pending(&shmd->cp_sleep_dwork.work)) { - queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, - msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); - } -} - -static void release_ap_status(struct work_struct *ws) -{ - struct shmem_link_device *shmd; - struct link_device *ld; - int i; - unsigned long flags; - - shmd = container_of(ws, struct shmem_link_device, link_off_dwork.work); - - spin_lock_irqsave(&shmd->pm_lock, flags); - i = atomic_read(&shmd->ref_cnt); - spin_unlock_irqrestore(&shmd->pm_lock, flags); - if (i > 0) - goto reschedule; - - if (gpio_get_value(shmd->gpio_cp_status)) - goto reschedule; - - gpio_set_value(shmd->gpio_ap_status, 0); - print_pm_status(shmd, 1); - - if (wake_lock_active(&shmd->cp_wlock)) - wake_unlock(&shmd->cp_wlock); - - return; - -reschedule: - if (!work_pending(&shmd->link_off_dwork.work)) { - queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, - msecs_to_jiffies(100)); - } -} - -/** - * forbid_cp_sleep - * @shmd: pointer to an instance of shmem_link_device structure - * - * Wakes up a CP if it can sleep and increases the "ref_cnt" counter in the - * shmem_link_device instance. - * - * CAUTION!!! permit_cp_sleep() MUST be invoked after forbid_cp_sleep() success - * to decrease the "ref_cnt" counter. - */ -static int forbid_cp_sleep(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - int err = 0; - unsigned long flags; - - spin_lock_irqsave(&shmd->pm_lock, flags); - atomic_inc(&shmd->ref_cnt); - if (gpio_get_value(shmd->gpio_ap_status) == 0) { - gpio_set_value(shmd->gpio_ap_status, 1); - print_pm_status(shmd, 1); - } - spin_unlock_irqrestore(&shmd->pm_lock, flags); - - if (work_pending(&shmd->cp_sleep_dwork.work)) - cancel_delayed_work(&shmd->cp_sleep_dwork); - - if (work_pending(&shmd->link_off_dwork.work)) - cancel_delayed_work(&shmd->link_off_dwork); - - if (gpio_get_value(shmd->gpio_cp_wakeup) == 0) { - gpio_set_value(shmd->gpio_cp_wakeup, 1); - print_pm_status(shmd, 1); - } - - if (check_link_status(shmd) < 0) { - print_pm_status(shmd, 1); - mif_err("%s: ERR! check_link_status fail\n", ld->name); - err = -EACCES; - goto exit; - } - -exit: - return err; -} - -/** - * permit_cp_sleep - * @shmd: pointer to an instance of shmem_link_device structure - * - * Decreases the "ref_cnt" counter in the shmem_link_device instance if it can - * sleep and allows a CP to sleep only if the value of "ref_cnt" counter is - * less than or equal to 0. - * - * MUST be invoked after forbid_cp_sleep() success to decrease the "ref_cnt" - * counter. - */ -static void permit_cp_sleep(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - unsigned long flags; - - spin_lock_irqsave(&shmd->pm_lock, flags); - - if (atomic_dec_return(&shmd->ref_cnt) > 0) { - spin_unlock_irqrestore(&shmd->pm_lock, flags); - return; - } - - atomic_set(&shmd->ref_cnt, 0); - spin_unlock_irqrestore(&shmd->pm_lock, flags); - - /* Hold gpio_cp_wakeup for CP_WAKEUP_HOLD_TIME until CP finishes RX */ - if (!work_pending(&shmd->cp_sleep_dwork.work)) { - queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, - msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); - } -} - -/** - * ap_wakeup_handler: interrupt handler for a wakeup interrupt - * @irq: IRQ number - * @data: pointer to a data - * - * 1) Reads the interrupt value - * 2) Performs interrupt handling - */ -static irqreturn_t ap_wakeup_handler(int irq, void *data) -{ - struct shmem_link_device *shmd = (struct shmem_link_device *)data; - struct link_device *ld = (struct link_device *)&shmd->ld; - int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); - int ap_status = gpio_get_value(shmd->gpio_ap_status); - - s5p_change_irq_type(irq, ap_wakeup); - - if (ld->mode != LINK_MODE_IPC) - goto exit; - - if (work_pending(&shmd->cp_sleep_dwork.work)) - __cancel_delayed_work(&shmd->cp_sleep_dwork); - - print_pm_status(shmd, 1); - - if (ap_wakeup) { - if (work_pending(&shmd->link_off_dwork.work)) - __cancel_delayed_work(&shmd->link_off_dwork); - - if (!wake_lock_active(&shmd->ap_wlock)) - wake_lock(&shmd->ap_wlock); - - if (!c2c_suspended() && !ap_status) - gpio_set_value(shmd->gpio_ap_status, 1); - } else { - if (wake_lock_active(&shmd->ap_wlock)) - wake_unlock(&shmd->ap_wlock); - - queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, - msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); - } - -exit: - return IRQ_HANDLED; -} - -static irqreturn_t cp_status_handler(int irq, void *data) -{ - struct shmem_link_device *shmd = (struct shmem_link_device *)data; - struct link_device *ld = (struct link_device *)&shmd->ld; - int cp_status = gpio_get_value(shmd->gpio_cp_status); - unsigned long flags; - - spin_lock_irqsave(&shmd->pm_lock, flags); - - s5p_change_irq_type(irq, cp_status); - - if (ld->mode != LINK_MODE_IPC) - goto exit; - - if (work_pending(&shmd->link_off_dwork.work)) - __cancel_delayed_work(&shmd->link_off_dwork); - - print_pm_status(shmd, 1); - - if (cp_status) { - if (!wake_lock_active(&shmd->cp_wlock)) - wake_lock(&shmd->cp_wlock); - } else { - if (atomic_read(&shmd->ref_cnt) > 0) { - queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, - msecs_to_jiffies(10)); - } else { - gpio_set_value(shmd->gpio_ap_status, 0); - if (wake_lock_active(&shmd->cp_wlock)) - wake_unlock(&shmd->cp_wlock); - } - } - -exit: - spin_unlock_irqrestore(&shmd->pm_lock, flags); - return IRQ_HANDLED; -} - -#if 1 -/* Functions for IPC/BOOT/DUMP RX */ -#endif - -/** - * handle_cp_crash - * @shmd: pointer to an instance of shmem_link_device structure - * - * Actual handler for the CRASH_EXIT command from a CP. - */ -static void handle_cp_crash(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - struct io_device *iod; - int i; - - if (shmd->forced_cp_crash) - shmd->forced_cp_crash = false; - - /* Stop network interfaces */ - mif_netif_stop(ld); - - /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ - for (i = 0; i < MAX_SIPC5_DEV; i++) - skb_queue_purge(ld->skb_txq[i]); - - /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ - iod = link_get_iod_with_format(ld, IPC_FMT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_EXIT); - - /* time margin for taking state changes by rild */ - mdelay(100); - - /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ - iod = link_get_iod_with_format(ld, IPC_BOOT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_EXIT); -} - -/** - * handle_no_cp_crash_ack - * @arg: pointer to an instance of shmem_link_device structure - * - * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no - * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. - */ -static void handle_no_cp_crash_ack(unsigned long arg) -{ - struct shmem_link_device *shmd = (struct shmem_link_device *)arg; - struct link_device *ld = &shmd->ld; - - mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name); - - handle_cp_crash(shmd); -} - -/** - * trigger_forced_cp_crash - * @shmd: pointer to an instance of shmem_link_device structure - * - * Triggers an enforced CP crash. - */ -static void trigger_forced_cp_crash(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - struct utc_time t; - - if (ld->mode == LINK_MODE_ULOAD) { - mif_err("%s: ALREADY in progress\n", ld->name, CALLER); - return; - } - ld->mode = LINK_MODE_ULOAD; - shmd->forced_cp_crash = true; - - get_utc_time(&t); - mif_err("%s: [%02d:%02d:%02d.%03d] \n", - ld->name, t.hour, t.min, t.sec, t.msec, CALLER); - - if (!wake_lock_active(&shmd->wlock)) - wake_lock(&shmd->wlock); - -#ifdef DEBUG_MODEM_IF - if (in_interrupt()) - queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); - else - save_mem_dump(shmd); -#endif - - /* Send CRASH_EXIT command to a CP */ - send_int2cp(shmd, INT_CMD(INT_CMD_CRASH_EXIT)); - get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); - - /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, - handle_no_cp_crash_ack() will be executed. */ - mif_add_timer(&shmd->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_cp_crash_ack, (unsigned long)shmd); - - return; -} - -/** - * cmd_crash_reset_handler - * @shmd: pointer to an instance of shmem_link_device structure - * - * Handles the CRASH_RESET command from a CP. - */ -static void cmd_crash_reset_handler(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - struct io_device *iod = NULL; - int i; - struct utc_time t; - - ld->mode = LINK_MODE_ULOAD; - - if (!wake_lock_active(&shmd->wlock)) - wake_lock(&shmd->wlock); - - get_utc_time(&t); - mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC7 (CRASH_RESET)\n", - ld->name, t.hour, t.min, t.sec, t.msec); -#ifdef DEBUG_MODEM_IF - queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); -#endif - - /* Stop network interfaces */ - mif_netif_stop(ld); - - /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ - for (i = 0; i < MAX_SIPC5_DEV; i++) - skb_queue_purge(ld->skb_txq[i]); - - /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ - iod = link_get_iod_with_format(ld, IPC_FMT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_RESET); - - /* time margin for taking state changes by rild */ - mdelay(100); - - /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ - iod = link_get_iod_with_format(ld, IPC_BOOT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_RESET); -} - -/** - * cmd_crash_exit_handler - * @shmd: pointer to an instance of shmem_link_device structure - * - * Handles the CRASH_EXIT command from a CP. - */ -static void cmd_crash_exit_handler(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - struct utc_time t; - - ld->mode = LINK_MODE_ULOAD; - - del_timer(&shmd->crash_ack_timer); - - if (!wake_lock_active(&shmd->wlock)) - wake_lock(&shmd->wlock); - - get_utc_time(&t); - mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC9 (CRASH_EXIT)\n", - ld->name, t.hour, t.min, t.sec, t.msec); -#ifdef DEBUG_MODEM_IF - queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); -#endif - - handle_cp_crash(shmd); -} - -/** - * cmd_phone_start_handler - * @shmd: pointer to an instance of shmem_link_device structure - * - * Handles the PHONE_START command from a CP. - */ -static void cmd_phone_start_handler(struct shmem_link_device *shmd) -{ - int err; - struct link_device *ld = &shmd->ld; - struct io_device *iod; - int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); - int cp_status = gpio_get_value(shmd->gpio_cp_status); - - mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); - - iod = link_get_iod_with_format(ld, IPC_FMT); - if (!iod) { - mif_err("%s: ERR! no iod\n", ld->name); - return; - } - - err = init_shmem_ipc(shmd); - if (err) - return; - - if (wake_lock_active(&shmd->wlock)) - wake_unlock(&shmd->wlock); - - s5p_change_irq_type(shmd->irq_ap_wakeup, ap_wakeup); - if (ap_wakeup && !wake_lock_active(&shmd->ap_wlock)) - wake_lock(&shmd->ap_wlock); - - s5p_change_irq_type(shmd->irq_cp_status, cp_status); - if (cp_status && !wake_lock_active(&shmd->ap_wlock)) - wake_lock(&shmd->cp_wlock); - - ld->mode = LINK_MODE_IPC; - iod->modem_state_changed(iod, STATE_ONLINE); -} - -/** - * cmd_handler: processes a SHMEM command from a CP - * @shmd: pointer to an instance of shmem_link_device structure - * @cmd: SHMEM command from a CP - */ -static void cmd_handler(struct shmem_link_device *shmd, u16 cmd) -{ - struct link_device *ld = &shmd->ld; - - switch (INT_CMD_MASK(cmd)) { - case INT_CMD_CRASH_RESET: - cmd_crash_reset_handler(shmd); - break; - - case INT_CMD_CRASH_EXIT: - cmd_crash_exit_handler(shmd); - break; - - case INT_CMD_PHONE_START: - cmd_phone_start_handler(shmd); - complete_all(&ld->init_cmpl); - break; - - default: - mif_err("%s: unknown command 0x%04X\n", ld->name, cmd); - break; - } -} - -/** - * ipc_rx_work - * @ws: pointer to an instance of work_struct structure - * - * Invokes the recv method in the io_device instance to perform receiving IPC - * messages from each skb. - */ -static void ipc_rx_work(struct work_struct *ws) +struct link_device *c2c_create_link_device(struct platform_device *pdev) { - struct shmem_link_device *shmd; + struct c2c_link_device *dpld; struct link_device *ld; - struct io_device *iod; - struct sk_buff *skb; - int i; - - shmd = container_of(ws, struct shmem_link_device, ipc_rx_dwork.work); - ld = &shmd->ld; - - for (i = 0; i < MAX_SIPC5_DEV; i++) { - iod = shmd->iod[i]; - while (1) { - skb = skb_dequeue(ld->skb_rxq[i]); - if (!skb) - break; - iod->recv_skb(iod, ld, skb); - } - } -} - -/** - * rx_ipc_frames - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * - * Returns - * ret < 0 : error - * ret == 0 : ILLEGAL status - * ret > 0 : valid data - * - * Must be invoked only when there is data in the corresponding RXQ. - * - * Requires a recv_skb method in the io_device instance, so this function must - * be used for only SIPC5. - */ -static int rx_ipc_frames(struct shmem_link_device *shmd, int dev, - struct circ_status *circ) -{ - struct link_device *ld = &shmd->ld; - struct sk_buff_head *rxq = ld->skb_rxq[dev]; - struct sk_buff *skb; - int ret; - /** - * variables for the status of the circular queue - */ - u8 *src; - u8 hdr[SIPC5_MIN_HEADER_SIZE]; - /** - * variables for RX processing - */ - int qsize; /* size of the queue */ - int rcvd; /* size of data in the RXQ or error */ - int rest; /* size of the rest data */ - int out; /* index to the start of current frame */ - u8 *dst; /* pointer to the destination buffer */ - int tot; /* total length including padding data */ - - src = circ->buff; - qsize = circ->qsize; - out = circ->out; - rcvd = circ->size; - - rest = rcvd; - tot = 0; - while (rest > 0) { - /* Copy the header in the frame to the header buffer */ - circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); - - /* Check the config field in the header */ - if (unlikely(!sipc5_start_valid(hdr))) { - mif_err("%s: ERR! %s INVALID config 0x%02X " - "(rcvd %d, rest %d)\n", ld->name, - get_dev_name(dev), hdr[0], rcvd, rest); - ret = -EBADMSG; - goto exit; - } - - /* Verify the total length of the frame (data + padding) */ - tot = sipc5_get_total_len(hdr); - if (unlikely(tot > rest)) { - mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", - ld->name, get_dev_name(dev), tot, rest, rcvd); - ret = -EBADMSG; - goto exit; - } + struct modem_data *pdata; - /* Allocate an skb */ - skb = dev_alloc_skb(tot); - if (!skb) { - mif_err("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); - ret = -ENOMEM; - goto exit; - } + pdata = pdev->dev.platform_data; - /* Set the attribute of the skb as "single frame" */ - skbpriv(skb)->single_frame = true; - - /* Read the frame from the RXQ */ - dst = skb_put(skb, tot); - circ_read(dst, src, qsize, out, tot); - - /* Store the skb to the corresponding skb_rxq */ - skb_queue_tail(rxq, skb); - - /* Calculate new out value */ - rest -= tot; - out += tot; - if (unlikely(out >= qsize)) - out -= qsize; + dpld = kzalloc(sizeof(struct c2c_link_device), GFP_KERNEL); + if (!dpld) { + mif_err("dpld == NULL\n"); + return NULL; } - /* Update tail (out) pointer to empty out the RXQ */ - set_rxq_tail(shmd, dev, circ->in); - - return rcvd; - -exit: -#ifdef DEBUG_MODEM_IF - mif_err("%s: ERR! rcvd:%d tot:%d rest:%d\n", ld->name, rcvd, tot, rest); - pr_ipc(1, "c2c: ERR! CP2MIF", (src + out), (rest > 20) ? 20 : rest); -#endif - - return ret; -} - -/** - * msg_handler: receives IPC messages from every RXQ - * @shmd: pointer to an instance of shmem_link_device structure - * @mst: pointer to an instance of mem_status structure - * - * 1) Receives all IPC message frames currently in every IPC RXQ. - * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. - * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if - * there is any RES_ACK response. - */ -static void msg_handler(struct shmem_link_device *shmd, struct mem_status *mst) -{ - struct link_device *ld = &shmd->ld; - struct circ_status circ; - int i = 0; - int ret = 0; + wake_lock_init(&dpld->c2c_wake_lock, WAKE_LOCK_SUSPEND, "c2c_wakelock"); + wake_lock(&dpld->c2c_wake_lock); - if (!ipc_active(shmd)) - return; + ld = &dpld->ld; + dpld->pdata = pdata; - /* Read data from every RXQ */ - for (i = 0; i < MAX_SIPC5_DEV; i++) { - /* Invoke an RX function only when there is data in the RXQ */ - if (likely(mst->head[i][RX] != mst->tail[i][RX])) { - ret = get_rxq_rcvd(shmd, i, mst, &circ); - if (unlikely(ret < 0)) { - mif_err("%s: ERR! get_rxq_rcvd fail (err %d)\n", - ld->name, ret); -#ifdef DEBUG_MODEM_IF - trigger_forced_cp_crash(shmd); -#endif - return; - } + ld->name = "c2c"; - ret = rx_ipc_frames(shmd, i, &circ); - if (ret < 0) { -#ifdef DEBUG_MODEM_IF - trigger_forced_cp_crash(shmd); -#endif - reset_rxq_circ(shmd, i); - } - } - } + mif_info("%s is created!!!\n", dpld->ld.name); - /* Schedule soft IRQ for RX */ - queue_delayed_work(system_nrt_wq, &shmd->ipc_rx_dwork, 0); + return ld; } - -static void msg_rx_task(unsigned long data) -{ - struct shmem_link_device *shmd = (struct shmem_link_device *)data; - struct link_device *ld = &shmd->ld; - struct mem_status mst; - u16 mask = 0; - - get_shmem_status(shmd, RX, &mst); - - if ((mst.head[IPC_FMT][RX] != mst.tail[IPC_FMT][RX]) - || (mst.head[IPC_RAW][RX] != mst.tail[IPC_RAW][RX])) { -#if 0 - print_mem_status(ld, &mst); -#endif - msg_handler(shmd, &mst); - } - - /* - ** Check and process REQ_ACK (at this time, in == out) - */ - if (unlikely(shmd->dev[IPC_FMT]->req_ack_rcvd)) { - mask |= get_mask_res_ack(shmd, IPC_FMT); - shmd->dev[IPC_FMT]->req_ack_rcvd = 0; - } - - if (unlikely(shmd->dev[IPC_RAW]->req_ack_rcvd)) { - mask |= get_mask_res_ack(shmd, IPC_RAW); - shmd->dev[IPC_RAW]->req_ack_rcvd = 0; - } - - if (unlikely(mask)) { -#ifdef DEBUG_MODEM_IF - mif_info("%s: send RES_ACK 0x%04X\n", ld->name, mask); -#endif - send_int2cp(shmd, INT_NON_CMD(mask)); - } -} - -/** - * ipc_handler: processes a SHMEM command or receives IPC messages - * @shmd: pointer to an instance of shmem_link_device structure - * @mst: pointer to an instance of mem_status structure - * - * Invokes cmd_handler for a SHMEM command or msg_handler for IPC messages. - */ -static void ipc_handler(struct shmem_link_device *shmd, struct mem_status *mst) -{ -#ifdef DEBUG_MODEM_IF - struct link_device *ld = &shmd->ld; -#endif - u16 intr = mst->int2ap; - - if (unlikely(INT_CMD_VALID(intr))) { - cmd_handler(shmd, intr); - return; - } - - /* - ** Check REQ_ACK from CP -> REQ_ACK will be processed in the RX tasklet - */ - if (unlikely(intr & get_mask_req_ack(shmd, IPC_FMT))) - shmd->dev[IPC_FMT]->req_ack_rcvd = 1; - - if (unlikely(intr & get_mask_req_ack(shmd, IPC_RAW))) - shmd->dev[IPC_RAW]->req_ack_rcvd = 1; - - /* - ** Check and process RES_ACK from CP - */ - if (unlikely(intr & get_mask_res_ack(shmd, IPC_FMT))) { -#ifdef DEBUG_MODEM_IF - mif_info("%s: recv FMT RES_ACK\n", ld->name); -#endif - complete(&shmd->req_ack_cmpl[IPC_FMT]); - } - - if (unlikely(intr & get_mask_res_ack(shmd, IPC_RAW))) { -#ifdef DEBUG_MODEM_IF - mif_info("%s: recv RAW RES_ACK\n", ld->name); -#endif - complete(&shmd->req_ack_cmpl[IPC_RAW]); - } - - /* - ** Schedule RX tasklet - */ - tasklet_hi_schedule(&shmd->rx_tsk); -} - -/** - * rx_udl_frames - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * - * Returns - * ret < 0 : error - * ret == 0 : ILLEGAL status - * ret > 0 : valid data - * - * Must be invoked only when there is data in the corresponding RXQ. - * - * Requires a recv_skb method in the io_device instance, so this function must - * be used for only SIPC5. - */ -static int rx_udl_frames(struct shmem_link_device *shmd, int dev, - struct circ_status *circ) -{ - struct link_device *ld = &shmd->ld; - struct io_device *iod; - struct sk_buff *skb; - int ret; - /** - * variables for the status of the circular queue - */ - u8 *src; - u8 hdr[SIPC5_MIN_HEADER_SIZE]; - /** - * variables for RX processing - */ - int qsize; /* size of the queue */ - int rcvd; /* size of data in the RXQ or error */ - int rest; /* size of the rest data */ - int out; /* index to the start of current frame */ - u8 *dst; /* pointer to the destination buffer */ - int tot; /* total length including padding data */ - - src = circ->buff; - qsize = circ->qsize; - out = circ->out; - rcvd = circ->size; - - rest = rcvd; - tot = 0; - while (rest > 0) { - /* Copy the header in the frame to the header buffer */ - circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); - - /* Check the config field in the header */ - if (unlikely(!sipc5_start_valid(hdr))) { - mif_err("%s: ERR! %s INVALID config 0x%02X " - "(rest %d, rcvd %d)\n", ld->name, - get_dev_name(dev), hdr[0], rest, rcvd); - pr_ipc(1, "UDL", (src + out), (rest > 20) ? 20 : rest); - ret = -EBADMSG; - goto exit; - } - - /* Verify the total length of the frame (data + padding) */ - tot = sipc5_get_total_len(hdr); - if (unlikely(tot > rest)) { - mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", - ld->name, get_dev_name(dev), tot, rest, rcvd); - ret = -ENODATA; - goto exit; - } - - /* Allocate an skb */ - skb = alloc_skb(tot + NET_SKB_PAD, GFP_KERNEL); - if (!skb) { - mif_err("%s: ERR! %s alloc_skb fail\n", - ld->name, get_dev_name(dev)); - ret = -ENOMEM; - goto exit; - } - skb_reserve(skb, NET_SKB_PAD); - - /* Set the attribute of the skb as "single frame" */ - skbpriv(skb)->single_frame = true; - - /* Read the frame from the RXQ */ - dst = skb_put(skb, tot); - circ_read(dst, src, qsize, out, tot); - - /* Pass the skb to an iod */ - iod = link_get_iod_with_channel(ld, sipc5_get_ch_id(skb->data)); - if (!iod) { - mif_err("%s: ERR! no IPC_BOOT iod\n", ld->name); - break; - } - -#ifdef DEBUG_MODEM_IF - if (!std_udl_with_payload(std_udl_get_cmd(skb->data))) { - if (ld->mode == LINK_MODE_DLOAD) { - pr_ipc(0, "[CP->AP] DL CMD", skb->data, - (skb->len > 20 ? 20 : skb->len)); - } else { - pr_ipc(0, "[CP->AP] UL CMD", skb->data, - (skb->len > 20 ? 20 : skb->len)); - } - } -#endif - - iod->recv_skb(iod, ld, skb); - - /* Calculate new out value */ - rest -= tot; - out += tot; - if (unlikely(out >= qsize)) - out -= qsize; - } - - /* Update tail (out) pointer to empty out the RXQ */ - set_rxq_tail(shmd, dev, circ->in); - - return rcvd; - -exit: - return ret; -} - -/** - * udl_rx_work - * @ws: pointer to an instance of the work_struct structure - * - * Invokes the recv method in the io_device instance to perform receiving IPC - * messages from each skb. - */ -static void udl_rx_work(struct work_struct *ws) -{ - struct shmem_link_device *shmd; - struct link_device *ld; - struct sk_buff_head *rxq; - struct mem_status mst; - struct circ_status circ; - int dev = IPC_RAW; - - shmd = container_of(ws, struct shmem_link_device, udl_rx_dwork.work); - ld = &shmd->ld; - rxq = ld->skb_rxq[dev]; - - while (1) { - get_shmem_status(shmd, RX, &mst); - if (mst.head[dev][RX] == mst.tail[dev][RX]) - break; - - /* Invoke an RX function only when there is data in the RXQ */ - if (get_rxq_rcvd(shmd, dev, &mst, &circ) < 0) { - mif_err("%s: ERR! get_rxq_rcvd fail\n", ld->name); -#ifdef DEBUG_MODEM_IF - trigger_forced_cp_crash(shmd); -#endif - break; - } - - if (rx_udl_frames(shmd, dev, &circ) < 0) { - skb_queue_purge(rxq); - break; - } - } -} - -/** - * udl_handler: receives BOOT/DUMP IPC messages from every RXQ - * @shmd: pointer to an instance of shmem_link_device structure - * @mst: pointer to an instance of mem_status structure - * - * 1) Receives all IPC message frames currently in every IPC RXQ. - * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. - * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if - * there is any RES_ACK response. - */ -static void udl_handler(struct shmem_link_device *shmd, struct mem_status *mst) -{ - u16 intr = mst->int2ap; - - /* Schedule soft IRQ for RX */ - queue_delayed_work(system_nrt_wq, &shmd->udl_rx_dwork, 0); - - /* Check and process RES_ACK */ - if (intr & INT_MASK_RES_ACK_SET) { - if (intr & get_mask_res_ack(shmd, IPC_RAW)) { -#ifdef DEBUG_MODEM_IF - struct link_device *ld = &shmd->ld; - mif_info("%s: recv RAW RES_ACK\n", ld->name); - print_circ_status(ld, IPC_RAW, mst); -#endif - complete(&shmd->req_ack_cmpl[IPC_RAW]); - } - } -} - -/** - * c2c_irq_handler: interrupt handler for a C2C interrupt - * @data: pointer to a data - * - * 1) Reads the interrupt value - * 2) Performs interrupt handling - * - * Flow for normal interrupt handling: - * c2c_irq_handler -> udl_handler - * c2c_irq_handler -> ipc_handler -> cmd_handler -> cmd_xxx_handler - * c2c_irq_handler -> ipc_handler -> msg_handler -> rx_ipc_frames -> ... - */ -static void c2c_irq_handler(void *data, u32 intr) -{ - struct shmem_link_device *shmd = (struct shmem_link_device *)data; - struct link_device *ld = (struct link_device *)&shmd->ld; - struct mem_status *mst = msq_get_free_slot(&shmd->stat_list); - - get_shmem_status(shmd, RX, mst); - - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { - mif_info("%s: ld->mode == LINK_MODE_OFFLINE\n", ld->name); - return; - } - - if (unlikely(!INT_VALID(intr))) { - mif_info("%s: ERR! invalid intr 0x%X\n", ld->name, intr); - return; - } - - mst->int2ap = intr; - - if (ld->mode == LINK_MODE_DLOAD || ld->mode == LINK_MODE_ULOAD) - udl_handler(shmd, mst); - else - ipc_handler(shmd, mst); -} - -#if 1 -/* Functions for IPC/BOOT/DUMP TX */ -#endif - -/** - * write_ipc_to_txq - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @circ: pointer to an instance of circ_status structure - * @skb: pointer to an instance of sk_buff structure - * - * Must be invoked only when there is enough space in the TXQ. - */ -static void write_ipc_to_txq(struct shmem_link_device *shmd, int dev, - struct circ_status *circ, struct sk_buff *skb) -{ - u32 qsize = circ->qsize; - u32 in = circ->in; - u8 *buff = circ->buff; - u8 *src = skb->data; - u32 len = skb->len; -#ifdef DEBUG_MODEM_IF - struct io_device *iod = skbpriv(skb)->iod; - struct modem_ctl *mc = shmd->ld.mc; - char tag[MIF_MAX_STR_LEN]; - - snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", iod->name, mc->name); - - if (dev == IPC_FMT) - pr_ipc(1, tag, src, (len > 20 ? 20 : len)); -#if 0 - if (dev == IPC_RAW) - pr_ipc(0, tag, src, (len > 20 ? 20 : len)); -#endif -#endif - - /* Write data to the TXQ */ - circ_write(buff, src, qsize, in, len); - - /* Update new head (in) pointer */ - set_txq_head(shmd, dev, circ_new_pointer(qsize, in, len)); -} - -/** - * xmit_ipc_msg - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. - * - * Returns total length of IPC messages transmitted or an error code. - */ -static int xmit_ipc_msg(struct shmem_link_device *shmd, int dev) -{ - struct link_device *ld = &shmd->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - struct sk_buff *skb; - unsigned long flags; - struct circ_status circ; - int space; - int copied = 0; - - /* Acquire the spin lock for a TXQ */ - spin_lock_irqsave(&shmd->tx_lock[dev], flags); - - while (1) { - /* Get the size of free space in the TXQ */ - space = get_txq_space(shmd, dev, &circ); - if (unlikely(space < 0)) { -#ifdef DEBUG_MODEM_IF - /* Trigger a enforced CP crash */ - trigger_forced_cp_crash(shmd); -#endif - /* Empty out the TXQ */ - reset_txq_circ(shmd, dev); - copied = -EIO; - break; - } - - skb = skb_dequeue(txq); - if (unlikely(!skb)) - break; - - /* Check the free space size comparing with skb->len */ - if (unlikely(space < skb->len)) { -#ifdef DEBUG_MODEM_IF - struct mem_status mst; -#endif - /* Set res_required flag for the "dev" */ - atomic_set(&shmd->res_required[dev], 1); - - /* Take the skb back to the skb_txq */ - skb_queue_head(txq, skb); - - mif_err("%s: NOSPC in %s_TXQ" - "{qsize:%u in:%u out:%u} free:%u < len:%u\n", - ld->name, CALLER, get_dev_name(dev), - circ.qsize, circ.in, circ.out, space, skb->len); -#ifdef DEBUG_MODEM_IF - get_shmem_status(shmd, TX, &mst); - print_circ_status(ld, dev, &mst); -#endif - copied = -ENOSPC; - break; - } - -#ifdef DEBUG_MODEM_IF - if (!ipc_active(shmd)) { - if (get_magic(shmd) == SHM_PM_MAGIC) { - mif_err("%s: Going to SLEEP\n", ld->name); - copied = -EBUSY; - } else { - mif_err("%s: IPC is NOT active\n", ld->name); - copied = -EIO; - } - break; - } -#endif - - /* TX only when there is enough space in the TXQ */ - write_ipc_to_txq(shmd, dev, &circ, skb); - copied += skb->len; - dev_kfree_skb_any(skb); - } - - /* Release the spin lock */ - spin_unlock_irqrestore(&shmd->tx_lock[dev], flags); - - return copied; -} - -/** - * wait_for_res_ack - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * 1) Sends an REQ_ACK interrupt for @dev to CP. - * 2) Waits for the corresponding RES_ACK for @dev from CP. - * - * Returns the return value from wait_for_completion_interruptible_timeout(). - */ -static int wait_for_res_ack(struct shmem_link_device *shmd, int dev) -{ - struct link_device *ld = &shmd->ld; - struct completion *cmpl = &shmd->req_ack_cmpl[dev]; - unsigned long timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); - int ret; - u16 mask; - -#ifdef DEBUG_MODEM_IF - mif_info("%s: send %s REQ_ACK\n", ld->name, get_dev_name(dev)); -#endif - - mask = get_mask_req_ack(shmd, dev); - send_int2cp(shmd, INT_NON_CMD(mask)); - - /* ret < 0 if interrupted, ret == 0 on timeout */ - ret = wait_for_completion_interruptible_timeout(cmpl, timeout); - if (ret < 0) { - mif_err("%s: %s: wait_for_completion interrupted! (ret %d)\n", - ld->name, get_dev_name(dev), ret); - goto exit; - } - - if (ret == 0) { - struct mem_status mst; - get_shmem_status(shmd, TX, &mst); - - mif_err("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", - ld->name, get_dev_name(dev)); - - /* - ** The TXQ must be checked whether or not it is empty, because - ** an interrupt mask can be overwritten by the next interrupt. - */ - if (mst.head[dev][TX] == mst.tail[dev][TX]) { - ret = get_txq_buff_size(shmd, dev); -#ifdef DEBUG_MODEM_IF - mif_err("%s: %s_TXQ has been emptied\n", - ld->name, get_dev_name(dev)); - print_circ_status(ld, dev, &mst); -#endif - } - } - -exit: - return ret; -} - -/** - * process_res_ack - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). - * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). - * 3) Restarts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. - * - * Returns the return value from xmit_ipc_msg(). - */ -static int process_res_ack(struct shmem_link_device *shmd, int dev) -{ - int ret; - u16 mask; - - ret = xmit_ipc_msg(shmd, dev); - if (ret > 0) { - mask = get_mask_send(shmd, dev); - send_int2cp(shmd, INT_NON_CMD(mask)); - get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); - } - - if (ret >= 0) - atomic_set(&shmd->res_required[dev], 0); - - return ret; -} - -/** - * fmt_tx_work: performs TX for FMT IPC device under SHMEM flow control - * @ws: pointer to an instance of the work_struct structure - * - * 1) Starts waiting for RES_ACK of FMT IPC device. - * 2) Returns immediately if the wait is interrupted. - * 3) Restarts SHMEM flow control if there is a timeout from the wait. - * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. - */ -static void fmt_tx_work(struct work_struct *ws) -{ - struct link_device *ld; - struct shmem_link_device *shmd; - int ret; - - ld = container_of(ws, struct link_device, fmt_tx_dwork.work); - shmd = to_shmem_link_device(ld); - - ret = wait_for_res_ack(shmd, IPC_FMT); - /* ret < 0 if interrupted */ - if (ret < 0) - return; - - /* ret == 0 on timeout */ - if (ret == 0) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); - return; - } - - ret = process_res_ack(shmd, IPC_FMT); - if (ret >= 0) { - permit_cp_sleep(shmd); - return; - } - - /* At this point, ret < 0 */ - if (ret == -ENOSPC || ret == -EBUSY) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], - msecs_to_jiffies(1)); - } -} - -/** - * raw_tx_work: performs TX for RAW IPC device under SHMEM flow control. - * @ws: pointer to an instance of the work_struct structure - * - * 1) Starts waiting for RES_ACK of RAW IPC device. - * 2) Returns immediately if the wait is interrupted. - * 3) Restarts SHMEM flow control if there is a timeout from the wait. - * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. - */ -static void raw_tx_work(struct work_struct *ws) -{ - struct link_device *ld; - struct shmem_link_device *shmd; - int ret; - - ld = container_of(ws, struct link_device, raw_tx_dwork.work); - shmd = to_shmem_link_device(ld); - - ret = wait_for_res_ack(shmd, IPC_RAW); - /* ret < 0 if interrupted */ - if (ret < 0) - return; - - /* ret == 0 on timeout */ - if (ret == 0) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); - return; - } - - ret = process_res_ack(shmd, IPC_RAW); - if (ret >= 0) { - permit_cp_sleep(shmd); - mif_netif_wake(ld); - return; - } - - /* At this point, ret < 0 */ - if (ret == -ENOSPC || ret == -EBUSY) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], - msecs_to_jiffies(1)); - } -} - -/** - * c2c_send_ipc - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @skb: pointer to an skb that will be transmitted - * - * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). - * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). - * 3) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. - */ -static int c2c_send_ipc(struct shmem_link_device *shmd, int dev) -{ - struct link_device *ld = &shmd->ld; - int ret; - u16 mask; - - if (atomic_read(&shmd->res_required[dev]) > 0) { - mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); - return 0; - } - - ret = xmit_ipc_msg(shmd, dev); - if (likely(ret > 0)) { - mask = get_mask_send(shmd, dev); - send_int2cp(shmd, INT_NON_CMD(mask)); - get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); - goto exit; - } - - /* If there was no TX, just exit */ - if (ret == 0) - goto exit; - - /* At this point, ret < 0 */ - if (ret == -ENOSPC || ret == -EBUSY) { - /* Prohibit CP from sleeping until the TXQ buffer is empty */ - if (forbid_cp_sleep(shmd) < 0) { - trigger_forced_cp_crash(shmd); - goto exit; - } - - /*----------------------------------------------------*/ - /* shmd->res_required[dev] was set in xmit_ipc_msg(). */ - /*----------------------------------------------------*/ - - if (dev == IPC_RAW) - mif_netif_stop(ld); - - queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], - msecs_to_jiffies(1)); - } - -exit: - return ret; -} - -/** - * c2c_try_send_ipc - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @iod: pointer to an instance of the io_device structure - * @skb: pointer to an skb that will be transmitted - * - * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. - * 2) Tries to transmit IPC messages with c2c_send_ipc(). - */ -static void c2c_try_send_ipc(struct shmem_link_device *shmd, int dev, - struct io_device *iod, struct sk_buff *skb) -{ - struct link_device *ld = &shmd->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - int ret; - - if (forbid_cp_sleep(shmd) < 0) { - trigger_forced_cp_crash(shmd); - goto exit; - } - - if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { - mif_err("%s: %s txq->qlen %d >= %d\n", ld->name, - get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); - dev_kfree_skb_any(skb); - goto exit; - } - - skb_queue_tail(txq, skb); - - ret = c2c_send_ipc(shmd, dev); - if (ret < 0) { - mif_err("%s->%s: ERR! c2c_send_ipc fail (err %d)\n", - iod->name, ld->name, ret); - } - -exit: - permit_cp_sleep(shmd); -} - -static int c2c_send_udl_cmd(struct shmem_link_device *shmd, int dev, - struct io_device *iod, struct sk_buff *skb) -{ - struct link_device *ld = &shmd->ld; - u8 *buff; - u8 *src; - u32 qsize; - u32 in; - int space; - int tx_bytes; - struct circ_status circ; - - if (iod->format == IPC_BOOT) { - pr_ipc(0, "[AP->CP] DL CMD", skb->data, - (skb->len > 20 ? 20 : skb->len)); - } else { - pr_ipc(0, "[AP->CP] UL CMD", skb->data, - (skb->len > 20 ? 20 : skb->len)); - } - - /* Get the size of free space in the TXQ */ - space = get_txq_space(shmd, dev, &circ); - if (space < 0) { - reset_txq_circ(shmd, dev); - tx_bytes = -EIO; - goto exit; - } - - /* Get the size of data to be sent */ - tx_bytes = skb->len; - - /* Check the size of free space */ - if (space < tx_bytes) { - mif_err("%s: NOSPC in %s_TXQ {qsize:%u in:%u out:%u}, " - "free:%u < tx_bytes:%u\n", ld->name, get_dev_name(dev), - circ.qsize, circ.in, circ.out, space, tx_bytes); - tx_bytes = -ENOSPC; - goto exit; - } - - /* Write data to the TXQ */ - buff = circ.buff; - src = skb->data; - qsize = circ.qsize; - in = circ.in; - circ_write(buff, src, qsize, in, tx_bytes); - - /* Update new head (in) pointer */ - set_txq_head(shmd, dev, circ_new_pointer(qsize, circ.in, tx_bytes)); - -exit: - dev_kfree_skb_any(skb); - return tx_bytes; -} - -static int c2c_send_udl_data(struct shmem_link_device *shmd, int dev) -{ - struct link_device *ld = &shmd->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - struct sk_buff *skb; - u8 *src; - int tx_bytes; - int copied; - u8 *buff; - u32 qsize; - u32 in; - u32 out; - int space; - struct circ_status circ; - - /* Get the size of free space in the TXQ */ - space = get_txq_space(shmd, dev, &circ); - if (space < 0) { -#ifdef DEBUG_MODEM_IF - /* Trigger a enforced CP crash */ - trigger_forced_cp_crash(shmd); -#endif - /* Empty out the TXQ */ - reset_txq_circ(shmd, dev); - return -EFAULT; - } - - buff = circ.buff; - qsize = circ.qsize; - in = circ.in; - out = circ.out; - space = circ.size; - - copied = 0; - while (1) { - skb = skb_dequeue(txq); - if (!skb) - break; - - /* Get the size of data to be sent */ - src = skb->data; - tx_bytes = skb->len; - - /* Check the free space size comparing with skb->len */ - if (space < tx_bytes) { - /* Set res_required flag for the "dev" */ - atomic_set(&shmd->res_required[dev], 1); - - /* Take the skb back to the skb_txq */ - skb_queue_head(txq, skb); - - mif_info("NOSPC in RAW_TXQ {qsize:%u in:%u out:%u}, " - "space:%u < tx_bytes:%u\n", - qsize, in, out, space, tx_bytes); - break; - } - - /* - ** TX only when there is enough space in the TXQ - */ - circ_write(buff, src, qsize, in, tx_bytes); - - copied += tx_bytes; - in = circ_new_pointer(qsize, in, tx_bytes); - space -= tx_bytes; - - dev_kfree_skb_any(skb); - } - - /* Update new head (in) pointer */ - if (copied > 0) { - in = circ_new_pointer(qsize, circ.in, copied); - set_txq_head(shmd, dev, in); - } - - return copied; -} - -/** - * c2c_send_udl - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @iod: pointer to an instance of the io_device structure - * @skb: pointer to an skb that will be transmitted - * - * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. - * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() - * function. - * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). - * 4) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. - */ -static void c2c_send_udl(struct shmem_link_device *shmd, int dev, - struct io_device *iod, struct sk_buff *skb) -{ - struct link_device *ld = &shmd->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - struct completion *cmpl = &shmd->req_ack_cmpl[dev]; - struct std_dload_info *dl_info = &shmd->dl_info; - struct mem_status mst; - u32 timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); - u32 udl_cmd; - int ret; - u16 mask = get_mask_send(shmd, dev); - - udl_cmd = std_udl_get_cmd(skb->data); - if (iod->format == IPC_RAMDUMP || !std_udl_with_payload(udl_cmd)) { - ret = c2c_send_udl_cmd(shmd, dev, iod, skb); - if (ret > 0) - send_int2cp(shmd, INT_NON_CMD(mask)); - else - mif_err("ERR! c2c_send_udl_cmd fail (err %d)\n", ret); - goto exit; - } - - skb_queue_tail(txq, skb); - if (txq->qlen < dl_info->num_frames) - goto exit; - - mask |= get_mask_req_ack(shmd, dev); - while (1) { - ret = c2c_send_udl_data(shmd, dev); - if (ret < 0) { - mif_err("ERR! c2c_send_udl_data fail (err %d)\n", ret); - skb_queue_purge(txq); - break; - } - - if (skb_queue_empty(txq)) { - send_int2cp(shmd, INT_NON_CMD(mask)); - break; - } - - send_int2cp(shmd, INT_NON_CMD(mask)); - - do { - ret = wait_for_completion_timeout(cmpl, timeout); - get_shmem_status(shmd, TX, &mst); - } while (mst.head[dev][TX] != mst.tail[dev][TX]); - } - -exit: - return; -} - -/** - * c2c_send - * @ld: pointer to an instance of the link_device structure - * @iod: pointer to an instance of the io_device structure - * @skb: pointer to an skb that will be transmitted - * - * Returns the length of data transmitted or an error code. - * - * Normal call flow for an IPC message: - * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq - * - * Call flow on congestion in a IPC TXQ: - * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work - * => xxx_tx_work -> wait_for_res_ack - * => msg_handler - * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) - */ -static int c2c_send(struct link_device *ld, struct io_device *iod, - struct sk_buff *skb) -{ - struct shmem_link_device *shmd = to_shmem_link_device(ld); - int dev = iod->format; - int len = skb->len; - - switch (dev) { - case IPC_FMT: - case IPC_RAW: - if (likely(ld->mode == LINK_MODE_IPC)) { - c2c_try_send_ipc(shmd, dev, iod, skb); - } else { - mif_err("%s->%s: ERR! ld->mode != LINK_MODE_IPC\n", - iod->name, ld->name); - dev_kfree_skb_any(skb); - } - return len; - - case IPC_BOOT: - case IPC_RAMDUMP: - c2c_send_udl(shmd, IPC_RAW, iod, skb); - return len; - - default: - mif_err("%s: ERR! no TXQ for %s\n", ld->name, iod->name); - dev_kfree_skb_any(skb); - return -ENODEV; - } -} - -#if 1 -/* Functions for BOOT/DUMP and INIT */ -#endif - -static int c2c_dload_start(struct link_device *ld, struct io_device *iod) -{ - struct shmem_link_device *shmd = to_shmem_link_device(ld); - u32 magic; - - ld->mode = LINK_MODE_DLOAD; - - clear_shmem_map(shmd); - - set_magic(shmd, SHM_BOOT_MAGIC); - magic = get_magic(shmd); - if (magic != SHM_BOOT_MAGIC) { - mif_err("%s: ERR! magic 0x%08X != SHM_BOOT_MAGIC 0x%08X\n", - ld->name, magic, SHM_BOOT_MAGIC); - return -EFAULT; - } - - return 0; -} - -/** - * c2c_set_dload_info - * @ld: pointer to an instance of link_device structure - * @iod: pointer to an instance of io_device structure - * @arg: pointer to an instance of std_dload_info structure in "user" memory - * - */ -static int c2c_set_dload_info(struct link_device *ld, struct io_device *iod, - unsigned long arg) -{ - struct shmem_link_device *shmd = to_shmem_link_device(ld); - struct std_dload_info *dl_info = &shmd->dl_info; - int ret; - - ret = copy_from_user(dl_info, (void __user *)arg, - sizeof(struct std_dload_info)); - if (ret) { - mif_err("ERR! copy_from_user fail!\n"); - return -EFAULT; - } - - return 0; -} - -static int c2c_force_dump(struct link_device *ld, struct io_device *iod) -{ - struct shmem_link_device *shmd = to_shmem_link_device(ld); - mif_err("+++\n"); - trigger_forced_cp_crash(shmd); - mif_err("---\n"); - return 0; -} - -static int c2c_dump_start(struct link_device *ld, struct io_device *iod) -{ - struct shmem_link_device *shmd = to_shmem_link_device(ld); - - ld->mode = LINK_MODE_ULOAD; - - clear_shmem_map(shmd); - - mif_err("%s: magic = 0x%08X\n", ld->name, SHM_DUMP_MAGIC); - set_magic(shmd, SHM_DUMP_MAGIC); - - return 0; -} - -static void c2c_remap_4mb_ipc_region(struct shmem_link_device *shmd) -{ - struct shmem_4mb_phys_map *map; - struct shmem_ipc_device *dev; - - map = (struct shmem_4mb_phys_map *)shmd->base; - - /* Magic code and access enable fields */ - shmd->ipc_map.magic = (u32 __iomem *)&map->magic; - shmd->ipc_map.access = (u32 __iomem *)&map->access; - - /* FMT */ - dev = &shmd->ipc_map.dev[IPC_FMT]; - - strcpy(dev->name, "FMT"); - dev->id = IPC_FMT; - - dev->txq.head = (u32 __iomem *)&map->fmt_tx_head; - dev->txq.tail = (u32 __iomem *)&map->fmt_tx_tail; - dev->txq.buff = (u8 __iomem *)&map->fmt_tx_buff[0]; - dev->txq.size = SHM_4M_FMT_TX_BUFF_SZ; - - dev->rxq.head = (u32 __iomem *)&map->fmt_rx_head; - dev->rxq.tail = (u32 __iomem *)&map->fmt_rx_tail; - dev->rxq.buff = (u8 __iomem *)&map->fmt_rx_buff[0]; - dev->rxq.size = SHM_4M_FMT_RX_BUFF_SZ; - - dev->mask_req_ack = INT_MASK_REQ_ACK_F; - dev->mask_res_ack = INT_MASK_RES_ACK_F; - dev->mask_send = INT_MASK_SEND_F; - - /* RAW */ - dev = &shmd->ipc_map.dev[IPC_RAW]; - - strcpy(dev->name, "RAW"); - dev->id = IPC_RAW; - - dev->txq.head = (u32 __iomem *)&map->raw_tx_head; - dev->txq.tail = (u32 __iomem *)&map->raw_tx_tail; - dev->txq.buff = (u8 __iomem *)&map->raw_tx_buff[0]; - dev->txq.size = SHM_4M_RAW_TX_BUFF_SZ; - - dev->rxq.head = (u32 __iomem *)&map->raw_rx_head; - dev->rxq.tail = (u32 __iomem *)&map->raw_rx_tail; - dev->rxq.buff = (u8 __iomem *)&map->raw_rx_buff[0]; - dev->rxq.size = SHM_4M_RAW_RX_BUFF_SZ; - - dev->mask_req_ack = INT_MASK_REQ_ACK_R; - dev->mask_res_ack = INT_MASK_RES_ACK_R; - dev->mask_send = INT_MASK_SEND_R; - - /* interrupt ports */ - shmd->ipc_map.mbx2ap = NULL; - shmd->ipc_map.mbx2cp = NULL; -} - -static int c2c_init_ipc_map(struct shmem_link_device *shmd) -{ - if (shmd->size >= SHMEM_SIZE_4MB) - c2c_remap_4mb_ipc_region(shmd); - else - return -EINVAL; - - memset(shmd->base, 0, shmd->size); - - shmd->magic = shmd->ipc_map.magic; - shmd->access = shmd->ipc_map.access; - - shmd->dev[IPC_FMT] = &shmd->ipc_map.dev[IPC_FMT]; - shmd->dev[IPC_RAW] = &shmd->ipc_map.dev[IPC_RAW]; - - shmd->mbx2ap = shmd->ipc_map.mbx2ap; - shmd->mbx2cp = shmd->ipc_map.mbx2cp; - - return 0; -} -static int c2c_init_communication(struct link_device *ld, struct io_device *iod) -{ - struct shmem_link_device *shmd = to_shmem_link_device(ld); - struct io_device *check_iod; - - if (iod->format == IPC_BOOT) - return 0; - - /* send 0xC2 */ - switch(iod->format) { - case IPC_FMT: - check_iod = link_get_iod_with_format(ld, IPC_RFS); - if (check_iod ? atomic_read(&check_iod->opened) : true) { - mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); - } else - mif_err("%s defined but not opened\n", check_iod->name); - break; - case IPC_RFS: - check_iod = link_get_iod_with_format(ld, IPC_FMT); - if (check_iod && atomic_read(&check_iod->opened)) { - mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); - } else - mif_err("not opened\n"); - break; - default: - break; - } - return 0; -} - -#if 0 -static void c2c_link_terminate(struct link_device *ld, struct io_device *iod) -{ - if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { - if (!atomic_read(&iod->opened)) { - ld->mode = LINK_MODE_OFFLINE; - mif_err("%s: %s: link mode changed: IPC -> OFFLINE\n", - iod->name, ld->name); - } - } - - return; -} -#endif - -struct link_device *c2c_create_link_device(struct platform_device *pdev) -{ - struct modem_data *modem; - struct shmem_link_device *shmd; - struct link_device *ld; - int err; - int i; - u32 phys_base; - u32 offset; - u32 size; - int irq; - unsigned long irq_flags; - char name[MIF_MAX_NAME_LEN]; - - /* - ** Get the modem (platform) data - */ - modem = (struct modem_data *)pdev->dev.platform_data; - if (!modem) { - mif_err("ERR! modem == NULL\n"); - return NULL; - } - mif_err("%s: %s\n", modem->link_name, modem->name); - - if (modem->ipc_version < SIPC_VER_50) { - mif_err("%s: %s: ERR! IPC version %d < SIPC_VER_50\n", - modem->link_name, modem->name, modem->ipc_version); - return NULL; - } - - /* - ** Alloc an instance of shmem_link_device structure - */ - shmd = kzalloc(sizeof(struct shmem_link_device), GFP_KERNEL); - if (!shmd) { - mif_err("%s: ERR! shmd kzalloc fail\n", modem->link_name); - goto error; - } - ld = &shmd->ld; - - /* - ** Retrieve modem data and SHMEM control data from the modem data - */ - ld->mdm_data = modem; - ld->name = modem->link_name; - ld->aligned = 1; - ld->ipc_version = modem->ipc_version; - ld->max_ipc_dev = MAX_SIPC5_DEV; - - /* - ** Set attributes as a link device - */ - ld->init_comm = c2c_init_communication; -#if 0 - ld->terminate_comm = c2c_link_terminate; -#endif - ld->send = c2c_send; - ld->dload_start = c2c_dload_start; - ld->firm_update = c2c_set_dload_info; - ld->force_dump = c2c_force_dump; - ld->dump_start = c2c_dump_start; - - INIT_LIST_HEAD(&ld->list); - - skb_queue_head_init(&ld->sk_fmt_tx_q); - skb_queue_head_init(&ld->sk_raw_tx_q); - ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q; - ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; - - skb_queue_head_init(&ld->sk_fmt_rx_q); - skb_queue_head_init(&ld->sk_raw_rx_q); - ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; - ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; - - init_completion(&ld->init_cmpl); - - /* - ** Retrieve SHMEM resource - */ - if (modem->link_types & LINKTYPE(LINKDEV_C2C)) { - shmd->type = C2C_SHMEM; - mif_debug("%s: shmd->type = C2C_SHMEM\n", ld->name); - } else if (modem->link_types & LINKTYPE(LINKDEV_SHMEM)) { - shmd->type = REAL_SHMEM; - mif_debug("%s: shmd->type = REAL_SHMEM\n", ld->name); - } else { - mif_err("%s: ERR! invalid type\n", ld->name); - goto error; - } - - phys_base = c2c_get_phys_base(); - offset = c2c_get_sh_rgn_offset(); - size = c2c_get_sh_rgn_size(); - mif_debug("%s: phys_base:0x%08X offset:0x%08X size:%d\n", - ld->name, phys_base, offset, size); - - shmd->start = phys_base + offset; - shmd->size = size; - shmd->base = c2c_request_sh_region(shmd->start, shmd->size); - if (!shmd->base) { - mif_err("%s: ERR! c2c_request_sh_region fail\n", ld->name); - goto error; - } - - mif_debug("%s: phys_addr:0x%08X virt_addr:0x%08X size:%d\n", - ld->name, shmd->start, (int)shmd->base, shmd->size); - - /* - ** Initialize SHMEM maps (physical map -> logical map) - */ - err = c2c_init_ipc_map(shmd); - if (err < 0) { - mif_err("%s: ERR! c2c_init_ipc_map fail (err %d)\n", - ld->name, err); - goto error; - } - - /* - ** Initialize locks, completions, and bottom halves - */ - sprintf(shmd->wlock_name, "%s_wlock", ld->name); - wake_lock_init(&shmd->wlock, WAKE_LOCK_SUSPEND, shmd->wlock_name); - - sprintf(shmd->ap_wlock_name, "%s_ap_wlock", ld->name); - wake_lock_init(&shmd->ap_wlock, WAKE_LOCK_SUSPEND, shmd->ap_wlock_name); - - sprintf(shmd->cp_wlock_name, "%s_cp_wlock", ld->name); - wake_lock_init(&shmd->cp_wlock, WAKE_LOCK_SUSPEND, shmd->cp_wlock_name); - - init_completion(&shmd->udl_cmpl); - for (i = 0; i < MAX_SIPC5_DEV; i++) - init_completion(&shmd->req_ack_cmpl[i]); - - tasklet_init(&shmd->rx_tsk, msg_rx_task, (unsigned long)shmd); - INIT_DELAYED_WORK(&shmd->ipc_rx_dwork, ipc_rx_work); - INIT_DELAYED_WORK(&shmd->udl_rx_dwork, udl_rx_work); - - for (i = 0; i < MAX_SIPC5_DEV; i++) { - spin_lock_init(&shmd->tx_lock[i]); - atomic_set(&shmd->res_required[i], 0); - } - - ld->tx_wq = create_singlethread_workqueue("shmem_tx_wq"); - if (!ld->tx_wq) { - mif_err("%s: ERR! fail to create tx_wq\n", ld->name); - goto error; - } - INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); - INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); - ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; - ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; - - spin_lock_init(&shmd->stat_list.lock); - spin_lock_init(&shmd->trace_list.lock); -#ifdef DEBUG_MODEM_IF - INIT_DELAYED_WORK(&shmd->dump_dwork, mem_dump_work); -#endif - - INIT_DELAYED_WORK(&shmd->cp_sleep_dwork, release_cp_wakeup); - INIT_DELAYED_WORK(&shmd->link_off_dwork, release_ap_status); - spin_lock_init(&shmd->pm_lock); - - /* - ** Retrieve SHMEM IRQ GPIO#, IRQ#, and IRQ flags - */ - shmd->gpio_pda_active = modem->gpio_pda_active; - mif_err("PDA_ACTIVE gpio# = %d (value %d)\n", - shmd->gpio_pda_active, gpio_get_value(shmd->gpio_pda_active)); - - shmd->gpio_ap_status = modem->gpio_ap_status; - shmd->gpio_ap_wakeup = modem->gpio_ap_wakeup; - shmd->irq_ap_wakeup = modem->irq_ap_wakeup; - if (!shmd->irq_ap_wakeup) { - mif_err("ERR! no irq_ap_wakeup\n"); - goto error; - } - mif_debug("CP2AP_WAKEUP IRQ# = %d\n", shmd->irq_ap_wakeup); - - shmd->gpio_cp_wakeup = modem->gpio_cp_wakeup; - shmd->gpio_cp_status = modem->gpio_cp_status; - shmd->irq_cp_status = modem->irq_cp_status; - if (!shmd->irq_cp_status) { - mif_err("ERR! no irq_cp_status\n"); - goto error; - } - mif_debug("CP2AP_STATUS IRQ# = %d\n", shmd->irq_cp_status); - - c2c_assign_gpio_ap_wakeup(shmd->gpio_ap_wakeup); - c2c_assign_gpio_ap_status(shmd->gpio_ap_status); - c2c_assign_gpio_cp_wakeup(shmd->gpio_cp_wakeup); - c2c_assign_gpio_cp_status(shmd->gpio_cp_status); - - gpio_set_value(shmd->gpio_pda_active, 1); - gpio_set_value(shmd->gpio_ap_status, 1); - - /* - ** Register interrupt handlers - */ - err = c2c_register_handler(c2c_irq_handler, shmd); - if (err) { - mif_err("%s: ERR! c2c_register_handler fail (err %d)\n", - ld->name, err); - goto error; - } - - snprintf(name, MIF_MAX_NAME_LEN, "%s_ap_wakeup", ld->name); - irq = shmd->irq_ap_wakeup; - irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); - err = mif_register_isr(irq, ap_wakeup_handler, irq_flags, name, shmd); - if (err) - goto error; - - snprintf(name, MIF_MAX_NAME_LEN, "%s_cp_status", ld->name); - irq = shmd->irq_cp_status; - irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); - err = mif_register_isr(irq, cp_status_handler, irq_flags, name, shmd); - if (err) - goto error; - - return ld; - -error: - mif_err("xxx\n"); - kfree(shmd); - return NULL; -} - diff --git a/drivers/misc/modem_if/modem_link_device_c2c.h b/drivers/misc/modem_if/modem_link_device_c2c.h index 9dba90b..7ec9aa6 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.h +++ b/drivers/misc/modem_if/modem_link_device_c2c.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -7,18 +8,209 @@ * * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ +#include #ifndef __MODEM_LINK_DEVICE_C2C_H__ #define __MODEM_LINK_DEVICE_C2C_H__ -#include -#include "modem_link_device_shmem.h" +#define DPRAM_ERR_MSG_LEN 128 +#define DPRAM_ERR_DEVICE "c2cerr" -#define CP_WAKEUP_HOLD_TIME 500 /* 500 ms */ +#define MAX_IDX 2 -#endif +#define DPRAM_BASE_PTR 0x4000000 + +#define DPRAM_START_ADDRESS 0 +#define DPRAM_MAGIC_CODE_ADDRESS DPRAM_START_ADDRESS +#define DPRAM_GOTA_MAGIC_CODE_SIZE 0x4 +#define DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS \ + (DPRAM_START_ADDRESS + DPRAM_GOTA_MAGIC_CODE_SIZE) +#define BSP_DPRAM_BASE_SIZE 0x1ff8 +#define DPRAM_END_OF_ADDRESS (BSP_DPRAM_BASE_SIZE - 1) +#define DPRAM_INTERRUPT_SIZE 0x2 +#define DPRAM_PDA2PHONE_INTERRUPT_ADDRESS \ + (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE - DPRAM_INTERRUPT_SIZE*2) +#define DPRAM_PHONE2PDA_INTERRUPT_ADDRESS \ + (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE) +#define DPRAM_BUFFER_SIZE \ + (DPRAM_PHONE2PDA_INTERRUPT_ADDRESS -\ + DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS) +#define DPRAM_INDEX_SIZE 0x2 + +#define MAGIC_DMDL 0x4445444C +#define MAGIC_UMDL 0x4445444D + +#define DPRAM_PACKET_DATA_SIZE 0x3f00 +#define DPRAM_PACKET_HEADER_SIZE 0x7 + +#define INT_GOTA_MASK_VALID 0xA000 +#define INT_DPRAM_DUMP_MASK_VALID 0xA000 +#define MASK_CMD_RECEIVE_READY_NOTIFICATION 0xA100 +#define MASK_CMD_DOWNLOAD_START_REQUEST 0xA200 +#define MASK_CMD_DOWNLOAD_START_RESPONSE 0xA301 +#define MASK_CMD_IMAGE_SEND_REQUEST 0xA400 +#define MASK_CMD_IMAGE_SEND_RESPONSE 0xA500 +#define MASK_CMD_SEND_DONE_REQUEST 0xA600 +#define MASK_CMD_SEND_DONE_RESPONSE 0xA701 +#define MASK_CMD_STATUS_UPDATE_NOTIFICATION 0xA800 +#define MASK_CMD_UPDATE_DONE_NOTIFICATION 0xA900 +#define MASK_CMD_EFS_CLEAR_RESPONSE 0xAB00 +#define MASK_CMD_ALARM_BOOT_OK 0xAC00 +#define MASK_CMD_ALARM_BOOT_FAIL 0xAD00 + +#define WRITEIMG_HEADER_SIZE 8 +#define WRITEIMG_TAIL_SIZE 4 +#define WRITEIMG_BODY_SIZE \ + (DPRAM_BUFFER_SIZE - WRITEIMG_HEADER_SIZE - WRITEIMG_TAIL_SIZE) + +#define DPDN_DEFAULT_WRITE_LEN WRITEIMG_BODY_SIZE +#define CMD_DL_START_REQ 0x9200 +#define CMD_IMG_SEND_REQ 0x9400 +#define CMD_DL_SEND_DONE_REQ 0x9600 + +#define CMD_UL_START_REQ 0x9200 +#define CMD_UL_START_READY 0x9400 +#define CMD_UL_SEND_RESP 0x9601 +#define CMD_UL_SEND_DONE_RESP 0x9801 +#define CMD_UL_SEND_REQ 0xA500 +#define CMD_UL_START_RESPONSE 0xA301 +#define CMD_UL_SEND_DONE_REQ 0xA700 +#define CMD_RECEIVE_READY_NOTIFICATION 0xA100 + +#define MASK_CMD_RESULT_FAIL 0x0002 +#define MASK_CMD_RESULT_SUCCESS 0x0001 + +#define START_INDEX 0x007F +#define END_INDEX 0x007E + +#define CMD_IMG_SEND_REQ 0x9400 + +#define CRC_TAB_SIZE 256 +#define CRC_16_L_SEED 0xFFFF + +struct c2c_device { + /* DPRAM memory addresses */ + u16 *in_head_addr; + u16 *in_tail_addr; + u8 *in_buff_addr; + unsigned long in_buff_size; + + u16 *out_head_addr; + u16 *out_tail_addr; + u8 *out_buff_addr; + unsigned long out_buff_size; + + unsigned long in_head_saved; + unsigned long in_tail_saved; + unsigned long out_head_saved; + unsigned long out_tail_saved; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; +}; + +struct memory_region { + u8 *control; + u8 *fmt_out; + u8 *raw_out; + u8 *fmt_in; + u8 *raw_in; + u8 *mbx; +}; + +struct UldDataHeader { + u8 bop; + u16 total_frame; + u16 curr_frame; + u16 len; +}; + +struct c2c_link_device { + struct link_device ld; + + struct modem_data *pdata; + + /*only c2c*/ + struct wake_lock c2c_wake_lock; + atomic_t raw_txq_req_ack_rcvd; + atomic_t fmt_txq_req_ack_rcvd; + u8 net_stop_flag; + int phone_sync; + u8 phone_status; + + struct work_struct xmit_work_struct; + + struct workqueue_struct *gota_wq; + struct work_struct gota_cmd_work; + + struct c2c_device dev_map[MAX_IDX]; + struct wake_lock dumplock; + + u8 c2c_read_data[131072]; + + int c2c_init_cmd_wait_condition; + wait_queue_head_t c2c_init_cmd_wait_q; + + int modem_pif_init_wait_condition; + wait_queue_head_t modem_pif_init_done_wait_q; + + struct completion gota_download_start_complete; + + int gota_send_done_cmd_wait_condition; + wait_queue_head_t gota_send_done_cmd_wait_q; + + int gota_update_done_cmd_wait_condition; + wait_queue_head_t gota_update_done_cmd_wait_q; + + int upload_send_req_wait_condition; + wait_queue_head_t upload_send_req_wait_q; + + int upload_send_done_wait_condition; + wait_queue_head_t upload_send_done_wait_q; + + int upload_start_req_wait_condition; + wait_queue_head_t upload_start_req_wait_q; + + int upload_packet_start_condition; + wait_queue_head_t upload_packet_start_wait_q; + + u16 gota_irq_handler_cmd; + + u16 c2c_dump_handler_cmd; + + int dump_region_number; + + unsigned int is_c2c_err ; + + int c2c_dump_start; + int gota_start; + + char c2c_err_buf[DPRAM_ERR_MSG_LEN]; + + struct fasync_struct *c2c_err_async_q; + + void (*clear_interrupt)(struct c2c_link_device *); + + struct memory_region m_region; + + unsigned long fmt_out_buff_size; + unsigned long raw_out_buff_size; + unsigned long fmt_in_buff_size; + unsigned long raw_in_buff_size; + + struct delayed_work delayed_tx; + struct sk_buff *delayed_skb; + u8 delayed_count; +}; + +/* converts from struct link_device* to struct xxx_link_device* */ +#define to_c2c_link_device(linkdev) \ + container_of(linkdev, struct c2c_link_device, ld) + +#endif diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c index 2d08c4e..a650ed9 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.c +++ b/drivers/misc/modem_if/modem_link_device_dpram.c @@ -25,385 +25,560 @@ #include #include #include -#include -#include -#include - #include + #include "modem_prj.h" -#include "modem_utils.h" #include "modem_link_device_dpram.h" +#include "modem_utils.h" -static void trigger_forced_cp_crash(struct dpram_link_device *dpld); +/* +** Function prototypes for basic DPRAM operations +*/ +static inline void clear_intr(struct dpram_link_device *dpld); +static inline u16 recv_intr(struct dpram_link_device *dpld); +static inline void send_intr(struct dpram_link_device *dpld, u16 mask); + +static inline u16 get_magic(struct dpram_link_device *dpld); +static inline void set_magic(struct dpram_link_device *dpld, u16 val); +static inline u16 get_access(struct dpram_link_device *dpld); +static inline void set_access(struct dpram_link_device *dpld, u16 val); + +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); + +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); + +static void handle_cp_crash(struct dpram_link_device *dpld); +static int trigger_force_cp_crash(struct dpram_link_device *dpld); +static void dpram_dump_memory(struct link_device *ld, char *buff); -/** - * set_circ_pointer - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @dir: direction of communication (TX or RX) - * @ptr: type of the queue pointer (HEAD or TAIL) - * @addr: address of the queue pointer - * @val: value to be written to the queue pointer - * - * Writes a value to a pointer in a circular queue with verification. - */ -static inline void set_circ_pointer(struct dpram_link_device *dpld, int id, - int dir, int ptr, void __iomem *addr, u16 val) +/* +** Functions for debugging +*/ +static inline void log_dpram_status(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int cnt = 0; - u16 saved = 0; + pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " + "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", + dpld->ld.mc->name, + get_magic(dpld), get_access(dpld), + get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), + get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), + get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), + get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), + recv_intr(dpld)); +} - iowrite16(val, addr); +static void set_dpram_map(struct dpram_link_device *dpld, + struct mif_irq_map *map) +{ + map->magic = get_magic(dpld); + map->access = get_access(dpld); - while (1) { - /* Check the value written to the address */ - saved = ioread16(addr); - if (likely(saved == val)) - break; + map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); + map->raw_tx_in = get_tx_head(dpld, IPC_RAW); + map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rx_head(dpld, IPC_RAW); + map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); - cnt++; - mif_err("%s: ERR! %s_%s.%s saved(%d) != val(%d), count %d\n", - ld->name, get_dev_name(id), circ_dir(dir), - circ_ptr(ptr), saved, val, cnt); - if (cnt >= MAX_RETRY_CNT) { - trigger_forced_cp_crash(dpld); - break; - } + map->cp2ap = recv_intr(dpld); +} - udelay(100); +/* +** RXB (DPRAM RX buffer) functions +*/ +static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) +{ + struct dpram_rxb *rxb; + u8 *buff; + int i; + + rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); + if (!rxb) { + mif_info("ERR! kzalloc rxb fail\n"); + return NULL; + } + + buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); + if (!buff) { + mif_info("ERR! kzalloc buff fail\n"); + kfree(rxb); + return NULL; + } - /* Write the value again */ - iowrite16(val, addr); + for (i = 0; i < count; i++) { + rxb[i].buff = buff; + rxb[i].size = size; + buff += size; } + + return rxb; } -/** - * recv_int2ap - * @dpld: pointer to an instance of dpram_link_device structure - * - * Returns the value of the CP-to-AP interrupt register in a DPRAM. - */ -static inline u16 recv_int2ap(struct dpram_link_device *dpld) +static inline unsigned rxbq_get_page_size(unsigned len) { - return ioread16(dpld->mbx2ap); + return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; } -/** - * send_int2cp - * @dpld: pointer to an instance of dpram_link_device structure - * @mask: value to be written to the AP-to-CP interrupt register in a DPRAM - */ -static inline void send_int2cp(struct dpram_link_device *dpld, u16 mask) +static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) { - struct idpram_pm_op *pm_op = dpld->pm_op; + return (rxbq->in == rxbq->out) ? true : false; +} - if (pm_op && pm_op->int2cp_possible) { - if (!pm_op->int2cp_possible(dpld)) - return; +static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) +{ + struct dpram_rxb *rxb = NULL; + + if (likely(rxbq_free_size(rxbq) > 0)) { + rxb = &rxbq->rxb[rxbq->in]; + rxbq->in++; + if (rxbq->in >= rxbq->size) + rxbq->in -= rxbq->size; + rxb->data = rxb->buff; } - iowrite16(mask, dpld->mbx2cp); + return rxb; } -/** - * read_int2cp - * @dpld: pointer to an instance of dpram_link_device structure - * - * Returns the value of the AP-to-CP interrupt register in a DPRAM. - */ -static inline u16 read_int2cp(struct dpram_link_device *dpld) +static inline int rxbq_size(struct dpram_rxb_queue *rxbq) { - return ioread16(dpld->mbx2cp); + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) +{ + struct dpram_rxb *rxb = NULL; + + if (likely(!rxbq_empty(rxbq))) { + rxb = &rxbq->rxb[rxbq->out]; + rxbq->out++; + if (rxbq->out >= rxbq->size) + rxbq->out -= rxbq->size; + } + + return rxb; +} + +static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) +{ + rxb->len = len; + return rxb->data; +} + +static inline void rxb_clear(struct dpram_rxb *rxb) +{ + rxb->data = NULL; + rxb->len = 0; +} + +/* +** DPRAM operations +*/ +static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), + unsigned long flag, const char *name, + struct dpram_link_device *dpld) +{ + int ret; + + ret = request_irq(irq, isr, flag, name, dpld); + if (ret) { + mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); + return ret; + } + + ret = enable_irq_wake(irq); + if (ret) + mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); + + mif_info("%s (#%d) handler registered\n", name, irq); + + return 0; +} + +static inline void clear_intr(struct dpram_link_device *dpld) +{ + if (likely(dpld->dpctl->clear_intr)) + dpld->dpctl->clear_intr(); +} + +static inline u16 recv_intr(struct dpram_link_device *dpld) +{ + if (likely(dpld->dpctl->recv_intr)) + return dpld->dpctl->recv_intr(); + else + return ioread16(dpld->mbx2ap); +} + +static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +{ + if (likely(dpld->dpctl->send_intr)) + dpld->dpctl->send_intr(mask); + else + iowrite16(mask, dpld->mbx2cp); } -/** - * get_magic - * @dpld: pointer to an instance of dpram_link_device structure - * - * Returns the value of the "magic code" field in a DPRAM. - */ static inline u16 get_magic(struct dpram_link_device *dpld) { return ioread16(dpld->magic); } -/** - * set_magic - * @dpld: pointer to an instance of dpram_link_device structure - * @val: value to be written to the "magic code" field in a DPRAM - */ static inline void set_magic(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->magic); } -/** - * get_access - * @dpld: pointer to an instance of dpram_link_device structure - * - * Returns the value of the "access enable" field in a DPRAM. - */ static inline u16 get_access(struct dpram_link_device *dpld) { return ioread16(dpld->access); } -/** - * set_access - * @dpld: pointer to an instance of dpram_link_device structure - * @val: value to be written to the "access enable" field in a DPRAM - */ static inline void set_access(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->access); } -/** - * get_txq_head - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a head (in) pointer in a TX queue. - */ -static inline u32 get_txq_head(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->txq.head); } -/** - * get_txq_tail - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a tail (out) pointer in a TX queue. - * - * It is useless for an AP to read a tail pointer in a TX queue twice to verify - * whether or not the value in the pointer is valid, because it can already have - * been updated by a CP after the first access from the AP. - */ -static inline u32 get_txq_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->txq.tail); } -/** - * get_txq_buff - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the start address of the buffer in a TXQ. - */ -static inline u8 *get_txq_buff(struct dpram_link_device *dpld, int id) +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) { - return dpld->dev[id]->txq.buff; + int cnt = 3; + u32 val = 0; + + iowrite16((u16)in, dpld->dev[id]->txq.head); + + do { + /* Check head value written */ + val = ioread16(dpld->dev[id]->txq.head); + if (likely(val == in)) + return; + + mif_err("ERR: %s txq.head(%d) != in(%d)\n", + get_dev_name(id), val, in); + udelay(100); + + /* Write head value again */ + iowrite16((u16)in, dpld->dev[id]->txq.head); + } while (cnt--); + + trigger_force_cp_crash(dpld); } -/** - * get_txq_buff_size - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the size of the buffer in a TXQ. - */ -static inline u32 get_txq_buff_size(struct dpram_link_device *dpld, int id) +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) { - return dpld->dev[id]->txq.size; + int cnt = 3; + u32 val = 0; + + iowrite16((u16)out, dpld->dev[id]->txq.tail); + + do { + /* Check tail value written */ + val = ioread16(dpld->dev[id]->txq.tail); + if (likely(val == out)) + return; + + mif_err("ERR: %s txq.tail(%d) != out(%d)\n", + get_dev_name(id), val, out); + udelay(100); + + /* Write tail value again */ + iowrite16((u16)out, dpld->dev[id]->txq.tail); + } while (cnt--); + + trigger_force_cp_crash(dpld); } -/** - * get_rxq_head - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a head (in) pointer in an RX queue. - * - * It is useless for an AP to read a head pointer in an RX queue twice to verify - * whether or not the value in the pointer is valid, because it can already have - * been updated by a CP after the first access from the AP. - */ -static inline u32 get_rxq_head(struct dpram_link_device *dpld, int id) +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) { - return ioread16(dpld->dev[id]->rxq.head); + return dpld->dev[id]->txq.buff; } -/** - * get_rxq_tail - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a tail (in) pointer in an RX queue. - */ -static inline u32 get_rxq_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) { - return ioread16(dpld->dev[id]->rxq.tail); + return dpld->dev[id]->txq.size; } -/** - * get_rxq_buff - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the start address of the buffer in an RXQ. - */ -static inline u8 *get_rxq_buff(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) { - return dpld->dev[id]->rxq.buff; + return ioread16(dpld->dev[id]->rxq.head); } -/** - * get_rxq_buff_size - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the size of the buffer in an RXQ. - */ -static inline u32 get_rxq_buff_size(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) { - return dpld->dev[id]->rxq.size; + return ioread16(dpld->dev[id]->rxq.tail); } -/** - * set_txq_head - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @in: value to be written to the head pointer in a TXQ - */ -static inline void set_txq_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) { - set_circ_pointer(dpld, id, TX, HEAD, dpld->dev[id]->txq.head, in); + int cnt = 3; + u32 val = 0; + + iowrite16((u16)in, dpld->dev[id]->rxq.head); + + do { + /* Check head value written */ + val = ioread16(dpld->dev[id]->rxq.head); + if (val == in) + return; + + mif_err("ERR: %s rxq.head(%d) != in(%d)\n", + get_dev_name(id), val, in); + udelay(100); + + /* Write head value again */ + iowrite16((u16)in, dpld->dev[id]->rxq.head); + } while (cnt--); + + trigger_force_cp_crash(dpld); } -/** - * set_txq_tail - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @out: value to be written to the tail pointer in a TXQ - */ -static inline void set_txq_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) { - set_circ_pointer(dpld, id, TX, TAIL, dpld->dev[id]->txq.tail, out); + int cnt = 3; + u32 val = 0; + + iowrite16((u16)out, dpld->dev[id]->rxq.tail); + + do { + /* Check tail value written */ + val = ioread16(dpld->dev[id]->rxq.tail); + if (val == out) + return; + + mif_err("ERR: %s rxq.tail(%d) != out(%d)\n", + get_dev_name(id), val, out); + udelay(100); + + /* Write tail value again */ + iowrite16((u16)out, dpld->dev[id]->rxq.tail); + } while (cnt--); + + trigger_force_cp_crash(dpld); } -/** - * set_rxq_head - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @in: value to be written to the head pointer in an RXQ - */ -static inline void set_rxq_head(struct dpram_link_device *dpld, int id, u32 in) +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) { - set_circ_pointer(dpld, id, RX, HEAD, dpld->dev[id]->rxq.head, in); + return dpld->dev[id]->rxq.buff; } -/** - * set_rxq_tail - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @out: value to be written to the tail pointer in an RXQ - */ -static inline void set_rxq_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) { - set_circ_pointer(dpld, id, RX, TAIL, dpld->dev[id]->rxq.tail, out); + return dpld->dev[id]->rxq.size; } -/** - * get_mask_req_ack - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the REQ_ACK mask value for the IPC device. - */ static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->mask_req_ack; } -/** - * get_mask_res_ack - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the RES_ACK mask value for the IPC device. - */ static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->mask_res_ack; } -/** - * get_mask_send - * @dpld: pointer to an instance of dpram_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the SEND mask value for the IPC device. - */ static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->mask_send; } -/** - * reset_txq_circ - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Empties a TXQ by resetting the head (in) pointer with the value in the tail - * (out) pointer. - */ -static inline void reset_txq_circ(struct dpram_link_device *dpld, int dev) +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +{ + if (in >= size) + return false; + + if (out >= size) + return false; + + return true; +} + +/* Get free space in the TXQ as well as in & out pointers */ +static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) { struct link_device *ld = &dpld->ld; - u32 head = get_txq_head(dpld, dev); - u32 tail = get_txq_tail(dpld, dev); + int cnt = 3; + u32 head; + u32 tail; + int space; + + do { + head = get_tx_head(dpld, dev); + tail = get_tx_tail(dpld, dev); + + space = (head < tail) ? (tail - head - 1) : + (qsize + tail - head - 1); + mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); - mif_info("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", - ld->name, get_dev_name(dev), head, tail); + if (dpram_circ_valid(qsize, head, tail)) { + *in = head; + *out = tail; + return space; + } + + mif_info("%s: CAUTION! <%pf> " + "%s_TXQ invalid (size:%d in:%d out:%d)\n", + ld->name, __builtin_return_address(0), + get_dev_name(dev), qsize, head, tail); + + udelay(100); + } while (cnt--); - set_txq_head(dpld, dev, tail); + *in = 0; + *out = 0; + return -EINVAL; } -/** - * reset_rxq_circ - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Empties an RXQ by resetting the tail (out) pointer with the value in the head - * (in) pointer. - */ -static inline void reset_rxq_circ(struct dpram_link_device *dpld, int dev) +static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +{ + set_tx_head(dpld, dev, 0); + set_tx_tail(dpld, dev, 0); + if (dev == IPC_FMT) + trigger_force_cp_crash(dpld); +} + +/* Get data size in the RXQ as well as in & out pointers */ +static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) { struct link_device *ld = &dpld->ld; - u32 head = get_rxq_head(dpld, dev); - u32 tail = get_rxq_tail(dpld, dev); + int cnt = 3; + u32 head; + u32 tail; + u32 rcvd; + + do { + head = get_rx_head(dpld, dev); + tail = get_rx_tail(dpld, dev); + if (head == tail) { + *in = head; + *out = tail; + return 0; + } + + rcvd = (head > tail) ? (head - tail) : (qsize - tail + head); + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), qsize, head, tail, rcvd); + + if (dpram_circ_valid(qsize, head, tail)) { + *in = head; + *out = tail; + return rcvd; + } - mif_info("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", - ld->name, get_dev_name(dev), tail, head); + mif_info("%s: CAUTION! <%pf> " + "%s_RXQ invalid (size:%d in:%d out:%d)\n", + ld->name, __builtin_return_address(0), + get_dev_name(dev), qsize, head, tail); + + udelay(100); + } while (cnt--); - set_rxq_tail(dpld, dev, head); + *in = 0; + *out = 0; + return -EINVAL; } -/** - * check_magic_access - * @dpld: pointer to an instance of dpram_link_device structure - * - * Returns 0 if the "magic code" and "access enable" values are valid, otherwise - * returns -EACCES. - */ -static int check_magic_access(struct dpram_link_device *dpld) +static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +{ + set_rx_head(dpld, dev, 0); + set_rx_tail(dpld, dev, 0); + if (dev == IPC_FMT) + trigger_force_cp_crash(dpld); +} + +/* +** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success +*/ +static int dpram_wake_up(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (!dpld->dpctl->wakeup) + return 0; + + if (dpld->dpctl->wakeup() < 0) { + mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n", + ld->name, __builtin_return_address(0)); + + if (dpld->dpctl->sleep) + dpld->dpctl->sleep(); + + udelay(10); + + if (dpld->dpctl->wakeup() < 0) { + mif_err("%s: ERR! <%pf> DPRAM wakeup fail twice\n", + ld->name, __builtin_return_address(0)); + return -EACCES; + } + } + + atomic_inc(&dpld->accessing); + return 0; +} + +static void dpram_allow_sleep(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (!dpld->dpctl->sleep) + return; + + if (atomic_dec_return(&dpld->accessing) <= 0) { + dpld->dpctl->sleep(); + atomic_set(&dpld->accessing, 0); + mif_debug("%s: DPRAM sleep possible\n", ld->name); + } +} + +static int dpram_check_access(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; int i; u16 magic = get_magic(dpld); u16 access = get_access(dpld); - /* Returns 0 if the "magic code" and "access enable" are valid */ if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; - /* Retry up to 100 times with 100 us delay per each retry */ for (i = 1; i <= 100; i++) { - mif_info("%s: magic:%X access:%X -> retry:%d\n", + mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n", ld->name, magic, access, i); udelay(100); @@ -417,313 +592,357 @@ static int check_magic_access(struct dpram_link_device *dpld) return -EACCES; } -/** - * ipc_active - * @dpld: pointer to an instance of dpram_link_device structure - * - * Returns whether or not IPC via the dpram_link_device instance is possible. - */ -static bool ipc_active(struct dpram_link_device *dpld) +static bool dpram_ipc_active(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { - mif_err("%s: ERR! ld->mode != LINK_MODE_IPC\n", - ld->name, CALLER); + mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n", + ld->name, __builtin_return_address(0)); return false; } - /* Check "magic code" and "access enable" values */ - if (check_magic_access(dpld) < 0) { - mif_err("%s: ERR! check_magic_access fail\n", - ld->name, CALLER); + if (dpram_check_access(dpld) < 0) { + mif_info("%s: ERR! <%pf> dpram_check_access fail\n", + ld->name, __builtin_return_address(0)); return false; } return true; } -/** - * get_dpram_status - * @dpld: pointer to an instance of dpram_link_device structure - * @dir: direction of communication (TX or RX) - * @stat: pointer to an instance of mem_status structure - * - * Takes a snapshot of the current status of a DPRAM. - */ -static void get_dpram_status(struct dpram_link_device *dpld, - enum circ_dir_type dir, struct mem_status *stat) -{ -#ifdef DEBUG_MODEM_IF - getnstimeofday(&stat->ts); -#endif - - stat->dir = dir; - stat->magic = get_magic(dpld); - stat->access = get_access(dpld); - stat->head[IPC_FMT][TX] = get_txq_head(dpld, IPC_FMT); - stat->tail[IPC_FMT][TX] = get_txq_tail(dpld, IPC_FMT); - stat->head[IPC_FMT][RX] = get_rxq_head(dpld, IPC_FMT); - stat->tail[IPC_FMT][RX] = get_rxq_tail(dpld, IPC_FMT); - stat->head[IPC_RAW][TX] = get_txq_head(dpld, IPC_RAW); - stat->tail[IPC_RAW][TX] = get_txq_tail(dpld, IPC_RAW); - stat->head[IPC_RAW][RX] = get_rxq_head(dpld, IPC_RAW); - stat->tail[IPC_RAW][RX] = get_rxq_tail(dpld, IPC_RAW); - stat->int2ap = recv_int2ap(dpld); - stat->int2cp = read_int2cp(dpld); -} - -#if 0 -/** - * save_ipc_trace_work - * @work: pointer to an instance of work_struct structure - * - * Performs actual file operation for saving RX IPC trace. - */ -static void save_ipc_trace_work(struct work_struct *work) -{ - struct dpram_link_device *dpld; - struct link_device *ld; - struct trace_data_queue *trq; - struct trace_data *trd; - struct circ_status *stat; - struct file *fp; - struct timespec *ts; - int dev; - u8 *dump; - int rcvd; - u8 *buff; - char *path; - struct utc_time utc; +static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 in, u32 out, struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *buff = get_tx_buff(dpld, dev); + u8 *src = skb->data; + u32 len = skb->len; + u32 inp; + struct mif_irq_map map; - dpld = container_of(work, struct dpram_link_device, trace_dwork.work); - ld = &dpld->ld; - trq = &dpld->trace_list; - path = dpld->trace_path; + if (in < out) { + /* +++++++++ in ---------- out ++++++++++ */ + memcpy((buff + in), src, len); + } else { + /* ------ out +++++++++++ in ------------ */ + u32 space = qsize - in; - buff = kzalloc(dpld->size << 3, GFP_KERNEL); - if (!buff) { - while (1) { - trd = trq_get_data_slot(trq); - if (!trd) - break; - - ts = &trd->ts; - dev = trd->dev; - stat = &trd->circ_stat; - dump = trd->data; - rcvd = trd->size; - print_ipc_trace(ld, dev, stat, ts, dump, rcvd); - - kfree(dump); - } - return; - } + /* 1) in -> buffer end */ + memcpy((buff + in), src, ((len > space) ? space : len)); - while (1) { - trd = trq_get_data_slot(trq); - if (!trd) - break; + /* 2) buffer start -> out */ + if (len > space) + memcpy(buff, (src + space), (len - space)); + } - ts = &trd->ts; - dev = trd->dev; - stat = &trd->circ_stat; - dump = trd->data; - rcvd = trd->size; - - ts2utc(ts, &utc); - snprintf(path, MIF_MAX_PATH_LEN, - "%s/%s_%s_%d%02d%02d-%02d%02d%02d.lst", - MIF_LOG_DIR, ld->name, get_dev_name(dev), - utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec); - - fp = mif_open_file(path); - if (fp) { - int len; - - snprintf(buff, MIF_MAX_PATH_LEN, - "[%d-%02d-%02d %02d:%02d:%02d.%03d] " - "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", - utc.year, utc.mon, utc.day, utc.hour, utc.min, - utc.sec, utc.msec, ld->name, get_dev_name(dev), - stat->in, stat->out, stat->size); - len = strlen(buff); - mif_dump2format4(dump, rcvd, (buff + len), NULL); - strcat(buff, "\n"); - len = strlen(buff); - - mif_save_file(fp, buff, len); - - memset(buff, 0, len); - mif_close_file(fp); - } else { - mif_err("%s: %s open fail\n", ld->name, path); - print_ipc_trace(ld, dev, stat, ts, dump, rcvd); - } + /* update new in pointer */ + inp = in + len; + if (inp >= qsize) + inp -= qsize; + set_tx_head(dpld, dev, inp); - kfree(dump); + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); + mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); } - kfree(buff); + if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + trigger_force_cp_crash(dpld); + } } -#endif -/** - * set_dpram_map - * @dpld: pointer to an instance of dpram_link_device structure - * @map: pointer to an instance of mif_irq_map structure - * - * Sets variables in an mif_irq_map instance as current DPRAM status for IPC - * logging. - */ -static void set_dpram_map(struct dpram_link_device *dpld, - struct mif_irq_map *map) +static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) { - map->magic = get_magic(dpld); - map->access = get_access(dpld); + struct link_device *ld = &dpld->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long int flags; + u32 qsize = get_tx_buff_size(dpld, dev); + u32 in; + u32 out; + int space; + int copied = 0; + + spin_lock_irqsave(&dpld->tx_lock[dev], flags); + + while (1) { + space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); + if (unlikely(space < 0)) { + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + dpram_reset_tx_circ(dpld, dev); + return space; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + if (unlikely(space < skb->len)) { + atomic_set(&dpld->res_required[dev], 1); + skb_queue_head(txq, skb); + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + mif_info("%s: %s " + "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", + ld->name, get_dev_name(dev), + qsize, in, out, space, skb->len); + return -ENOSPC; + } + + /* TX if there is enough room in the queue */ + dpram_ipc_write(dpld, dev, qsize, in, out, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } - map->fmt_tx_in = get_txq_head(dpld, IPC_FMT); - map->fmt_tx_out = get_txq_tail(dpld, IPC_FMT); - map->fmt_rx_in = get_rxq_head(dpld, IPC_FMT); - map->fmt_rx_out = get_rxq_tail(dpld, IPC_FMT); - map->raw_tx_in = get_txq_head(dpld, IPC_RAW); - map->raw_tx_out = get_txq_tail(dpld, IPC_RAW); - map->raw_rx_in = get_rxq_head(dpld, IPC_RAW); - map->raw_rx_out = get_rxq_tail(dpld, IPC_RAW); + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - map->cp2ap = recv_int2ap(dpld); + return copied; } -/** - * dpram_wake_up - * @dpld: pointer to an instance of dpram_link_device structure - * - * Wakes up a DPRAM if it can sleep and increases the "accessing" counter in the - * dpram_link_device instance. - * - * CAUTION!!! dpram_allow_sleep() MUST be invoked after dpram_wake_up() success - * to decrease the "accessing" counter. - */ -static int dpram_wake_up(struct dpram_link_device *dpld) +static void dpram_ipc_rx_task(unsigned long data) { + struct dpram_link_device *dpld = (struct dpram_link_device *)data; struct link_device *ld = &dpld->ld; + struct io_device *iod; + struct dpram_rxb *rxb; + unsigned qlen; + int i; - if (unlikely(!dpld->need_wake_up)) - return 0; - - if (dpld->ext_op->wakeup(dpld) < 0) { - mif_err("%s: ERR! wakeup fail\n", - ld->name, CALLER); - return -EACCES; + for (i = 0; i < dpld->max_ipc_dev; i++) { + iod = dpld->iod[i]; + qlen = rxbq_size(&dpld->rxbq[i]); + while (qlen > 0) { + rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); + iod->recv(iod, ld, rxb->data, rxb->len); + rxb_clear(rxb); + qlen--; + } } +} - atomic_inc(&dpld->accessing); +static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, + u8 __iomem *src, u32 out, u32 len, u32 qsize) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; - return 0; + /* 1) out -> buffer end */ + memcpy(dst, (src + out), len1); + + /* 2) buffer start -> in */ + dst += len1; + memcpy(dst, src, (len - len1)); + } } -/** - * dpram_allow_sleep - * @dpld: pointer to an instance of dpram_link_device structure - * - * Decreases the "accessing" counter in the dpram_link_device instance if it can - * sleep and allows the DPRAM to sleep only if the value of "accessing" counter - * is less than or equal to 0. - * - * MUST be invoked after dpram_wake_up() success to decrease the "accessing" - * counter. - */ -static void dpram_allow_sleep(struct dpram_link_device *dpld) +/* + ret < 0 : error + ret == 0 : no data + ret > 0 : valid data +*/ +static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + struct dpram_rxb *rxb; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + struct mif_irq_map map; - if (unlikely(!dpld->need_wake_up)) - return; + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) + return rcvd; - if (atomic_dec_return(&dpld->accessing) <= 0) { - dpld->ext_op->sleep(dpld); - atomic_set(&dpld->accessing, 0); - mif_debug("%s: DPRAM sleep possible\n", ld->name); + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + + /* Allocate an rxb */ + rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); + if (!rxb) { + mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", + ld->name, get_dev_name(dev)); + return -ENOMEM; } + + /* Read data from each DPRAM buffer */ + dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); + + /* Calculate and set new out */ + out += rcvd; + if (out >= qsize) + out -= qsize; + set_rx_tail(dpld, dev, out); + + return rcvd; } -static int capture_dpram_snapshot(struct link_device *ld, struct io_device *iod) +/* + ret < 0 : error + ret == 0 : no data + ret > 0 : valid data +*/ +static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct link_device *ld = &dpld->ld; + struct io_device *iod = dpld->iod[dev]; struct sk_buff *skb; - u32 size = dpld->size; - u32 copied = 0; - u8 *dump; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + int rest; + u8 *frm; + u8 *dst; + unsigned int len; + unsigned int pad; + unsigned int tot; + struct mif_irq_map map; - dpram_wake_up(dpld); - dump = capture_mem_dump(ld, dpld->base, dpld->size); - dpram_allow_sleep(dpld); + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) + return rcvd; - if (!dump) - return -ENOMEM; + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + + rest = rcvd; + while (rest > 0) { + frm = src + out; + if (unlikely(!sipc5_start_valid(frm[0]))) { + mif_err("%s: ERR! %s invalid start 0x%02X\n", + ld->name, get_dev_name(dev), frm[0]); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + len = sipc5_get_frame_sz16(frm); + if (unlikely(len > rest)) { + mif_err("%s: ERR! %s len %d > rest %d\n", + ld->name, get_dev_name(dev), len, rest); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + pad = sipc5_calc_padding_size(len); + tot = len + pad; - while (copied < size) { - skb = alloc_skb(MAX_DUMP_SKB_SIZE, GFP_ATOMIC); + /* Allocate an skb */ + skb = dev_alloc_skb(tot); if (!skb) { - mif_err("ERR! alloc_skb fail\n"); - kfree(dump); + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); return -ENOMEM; } - skb_put(skb, MAX_DUMP_SKB_SIZE); - memcpy(skb->data, (dump + copied), MAX_DUMP_SKB_SIZE); - copied += MAX_DUMP_SKB_SIZE; + /* Read data from each DPRAM buffer */ + dst = skb_put(skb, tot); + dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); + skb_trim(skb, len); + iod->recv_skb(iod, ld, skb); - skb_queue_tail(&iod->sk_rx_q, skb); - wake_up(&iod->wq); + /* Calculate and set new out */ + rest -= tot; + out += tot; + if (out >= qsize) + out -= qsize; } - kfree(dump); - return 0; + set_rx_tail(dpld, dev, out); + + return rcvd; +} + +static void non_command_handler(struct dpram_link_device *dpld, u16 intr) +{ + struct link_device *ld = &dpld->ld; + int i = 0; + int ret = 0; + u16 tx_mask = 0; + + if (!dpram_ipc_active(dpld)) + return; + + /* Read data from DPRAM */ + for (i = 0; i < dpld->max_ipc_dev; i++) { + if (dpld->use_skb) + ret = dpram_ipc_recv_data_with_skb(dpld, i); + else + ret = dpram_ipc_recv_data_with_rxb(dpld, i); + if (ret < 0) + dpram_reset_rx_circ(dpld, i); + + /* Check and process REQ_ACK (at this time, in == out) */ + if (intr & get_mask_req_ack(dpld, i)) { + mif_debug("%s: send %s_RES_ACK\n", + ld->name, get_dev_name(i)); + tx_mask |= get_mask_res_ack(dpld, i); + } + } + + if (!dpld->use_skb) { + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&dpld->rx_tsk); + } + + /* Try TX via DPRAM */ + for (i = 0; i < dpld->max_ipc_dev; i++) { + if (atomic_read(&dpld->res_required[i]) > 0) { + ret = dpram_try_ipc_tx(dpld, i); + if (ret > 0) { + atomic_set(&dpld->res_required[i], 0); + tx_mask |= get_mask_send(dpld, i); + } else if (ret == -ENOSPC) { + tx_mask |= get_mask_req_ack(dpld, i); + } + } + } + + if (tx_mask) { + send_intr(dpld, INT_NON_CMD(tx_mask)); + mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); + } } -/** - * handle_cp_crash - * @dpld: pointer to an instance of dpram_link_device structure - * - * Actual handler for the CRASH_EXIT command from a CP. - */ static void handle_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod; int i; - if (dpld->forced_cp_crash) - dpld->forced_cp_crash = false; - - /* Stop network interfaces */ - mif_netif_stop(ld); - - /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ - for (i = 0; i < ld->max_ipc_dev; i++) + for (i = 0; i < dpld->max_ipc_dev; i++) { + mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); skb_queue_purge(ld->skb_txq[i]); + } - /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_EXIT); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); - /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); if (iod) - iod->modem_state_changed(iod, STATE_CRASH_EXIT); + iodevs_for_each(iod->msd, iodev_netif_stop, 0); } -/** - * handle_no_cp_crash_ack - * @arg: pointer to an instance of dpram_link_device structure - * - * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no - * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. - */ -static void handle_no_cp_crash_ack(unsigned long arg) +static void handle_no_crash_ack(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; struct link_device *ld = &dpld->ld; @@ -736,363 +955,187 @@ static void handle_no_cp_crash_ack(unsigned long arg) handle_cp_crash(dpld); } -/** - * trigger_forced_cp_crash - * @dpld: pointer to an instance of dpram_link_device structure - * - * Triggers an enforced CP crash. - */ -static void trigger_forced_cp_crash(struct dpram_link_device *dpld) +static int trigger_force_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; -#ifdef DEBUG_MODEM_IF - struct trace_data *trd; - u8 *dump; - struct timespec ts; - getnstimeofday(&ts); -#endif if (ld->mode == LINK_MODE_ULOAD) { - mif_err("%s: ALREADY in progress\n", - ld->name, CALLER); - return; + mif_err("%s: CP crash is already in progress\n", ld->mc->name); + return 0; } ld->mode = LINK_MODE_ULOAD; - dpld->forced_cp_crash = true; - - disable_irq_nosync(dpld->irq); - - dpram_wake_up(dpld); - -#ifdef DEBUG_MODEM_IF - dump = capture_mem_dump(ld, dpld->base, dpld->size); - if (dump) { - trd = trq_get_free_slot(&dpld->trace_list); - memcpy(&trd->ts, &ts, sizeof(struct timespec)); - trd->dev = IPC_DEBUG; - trd->data = dump; - trd->size = dpld->size; - } -#endif - - enable_irq(dpld->irq); + mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); - mif_err("%s: \n", ld->name, CALLER); + if (dpld->dp_type == CP_IDPRAM) + dpram_wake_up(dpld); - /* Send CRASH_EXIT command to a CP */ - send_int2cp(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); - get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); - /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, - handle_no_cp_crash_ack() will be executed. */ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_cp_crash_ack, (unsigned long)dpld); + handle_no_crash_ack, (unsigned long)dpld); - return; + return 0; } -/** - * ext_command_handler - * @dpld: pointer to an instance of dpram_link_device structure - * @cmd: extended DPRAM command from a CP - * - * Processes an extended command from a CP. - */ -static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) +static int dpram_init_ipc(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - u16 resp; + int i; - switch (EXT_CMD_MASK(cmd)) { - case EXT_CMD_SET_SPEED_LOW: - if (dpld->dpram->setup_speed) { - dpld->dpram->setup_speed(DPRAM_SPEED_LOW); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); - send_int2cp(dpld, resp); - } - break; + if (ld->mode == LINK_MODE_IPC && + get_magic(dpld) == DPRAM_MAGIC_CODE && + get_access(dpld) == 1) + mif_info("%s: IPC already initialized\n", ld->name); - case EXT_CMD_SET_SPEED_MID: - if (dpld->dpram->setup_speed) { - dpld->dpram->setup_speed(DPRAM_SPEED_MID); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); - send_int2cp(dpld, resp); - } - break; + /* Clear pointers in every circular queue */ + for (i = 0; i < dpld->max_ipc_dev; i++) { + set_tx_head(dpld, i, 0); + set_tx_tail(dpld, i, 0); + set_rx_head(dpld, i, 0); + set_rx_tail(dpld, i, 0); + } - case EXT_CMD_SET_SPEED_HIGH: - if (dpld->dpram->setup_speed) { - dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); - send_int2cp(dpld, resp); - } - break; + /* Initialize variables for efficient TX/RX processing */ + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->iod[i] = link_get_iod_with_format(ld, i); + dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - default: - mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); - break; + if (dpld->iod[IPC_RAW]->recv_skb) + dpld->use_skb = true; + + for (i = 0; i < dpld->max_ipc_dev; i++) { + spin_lock_init(&dpld->tx_lock[i]); + atomic_set(&dpld->res_required[i], 0); + skb_queue_purge(&dpld->skb_rxq[i]); } -} -/** - * udl_command_handler - * @dpld: pointer to an instance of dpram_link_device structure - * @cmd: DPRAM upload/download command from a CP - * - * Processes a command for upload/download from a CP. - */ -static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) -{ - struct link_device *ld = &dpld->ld; + /* Enable IPC */ + atomic_set(&dpld->accessing, 0); - if (cmd & UDL_RESULT_FAIL) { - mif_err("%s: ERR! command fail (0x%04X)\n", ld->name, cmd); - return; - } + set_magic(dpld, DPRAM_MAGIC_CODE); + set_access(dpld, 1); + if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + return -EACCES; - switch (UDL_CMD_MASK(cmd)) { - case UDL_CMD_RECV_READY: - mif_err("%s: [CP->AP] CMD_DL_READY (0x%04X)\n", ld->name, cmd); -#ifdef CONFIG_CDMA_MODEM_CBP72 - mif_err("%s: [AP->CP] CMD_DL_START_REQ (0x%04X)\n", - ld->name, CMD_DL_START_REQ); - send_int2cp(dpld, CMD_DL_START_REQ); -#else - complete(&dpld->udl_cmpl); -#endif - break; + ld->mode = LINK_MODE_IPC; - default: - complete(&dpld->udl_cmpl); - } + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); + + return 0; } -/** - * cmd_req_active_handler - * @dpld: pointer to an instance of dpram_link_device structure - * - * Handles the REQ_ACTIVE command from a CP. - */ static void cmd_req_active_handler(struct dpram_link_device *dpld) { - send_int2cp(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); } -/** - * cmd_crash_reset_handler - * @dpld: pointer to an instance of dpram_link_device structure - * - * Handles the CRASH_RESET command from a CP. - */ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; - int i; ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); - /* Stop network interfaces */ - mif_netif_stop(ld); - - /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ - for (i = 0; i < ld->max_ipc_dev; i++) - skb_queue_purge(ld->skb_txq[i]); - mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); - /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); iod->modem_state_changed(iod, STATE_CRASH_RESET); - /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_RESET); } -/** - * cmd_crash_exit_handler - * @dpld: pointer to an instance of dpram_link_device structure - * - * Handles the CRASH_EXIT command from a CP. - */ static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; -#ifdef DEBUG_MODEM_IF - struct trace_data *trd; - u8 *dump; - struct timespec ts; - getnstimeofday(&ts); -#endif + u32 size = dpld->dpctl->dp_size; + char *dpram_buff = NULL; ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); - del_timer(&dpld->crash_ack_timer); + mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - dpram_wake_up(dpld); - -#ifdef DEBUG_MODEM_IF - if (!dpld->forced_cp_crash) { - dump = capture_mem_dump(ld, dpld->base, dpld->size); - if (dump) { - trd = trq_get_free_slot(&dpld->trace_list); - memcpy(&trd->ts, &ts, sizeof(struct timespec)); - trd->dev = IPC_DEBUG; - trd->data = dump; - trd->size = dpld->size; - } + if (dpld->dp_type == CP_IDPRAM) + dpram_wake_up(dpld); + + dpram_buff = kzalloc(size + (MAX_MIF_SEPA_SIZE * 2), GFP_ATOMIC); + if (!dpram_buff) { + mif_err("DPRAM dump failed!!\n"); + } else { + memset(dpram_buff, 0, size + (MAX_MIF_SEPA_SIZE * 2)); + memcpy(dpram_buff, MIF_SEPARATOR_DPRAM, MAX_MIF_SEPA_SIZE); + memcpy(dpram_buff + MAX_MIF_SEPA_SIZE, &size, sizeof(u32)); + dpram_buff += (MAX_MIF_SEPA_SIZE * 2); + dpram_dump_memory(ld, dpram_buff); } -#endif + + del_timer(&dpld->crash_ack_timer); if (dpld->ext_op && dpld->ext_op->crash_log) dpld->ext_op->crash_log(dpld); - mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - handle_cp_crash(dpld); } -/** - * init_dpram_ipc - * @dpld: pointer to an instance of dpram_link_device structure - * - * Initializes IPC via DPRAM. - */ -static int init_dpram_ipc(struct dpram_link_device *dpld) +static void cmd_phone_start_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - int i; - - if (ld->mode == LINK_MODE_IPC && - get_magic(dpld) == DPRAM_MAGIC_CODE && - get_access(dpld) == 1) - mif_info("%s: IPC already initialized\n", ld->name); - - /* Clear pointers in every circular queue */ - for (i = 0; i < ld->max_ipc_dev; i++) { - set_txq_head(dpld, i, 0); - set_txq_tail(dpld, i, 0); - set_rxq_head(dpld, i, 0); - set_rxq_tail(dpld, i, 0); - } - - /* Initialize variables for efficient TX/RX processing */ - for (i = 0; i < ld->max_ipc_dev; i++) - dpld->iod[i] = link_get_iod_with_format(ld, i); - dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + struct io_device *iod = NULL; - /* Initialize variables for TX flow control */ - for (i = 0; i < ld->max_ipc_dev; i++) - atomic_set(&dpld->res_required[i], 0); + mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); - /* Enable IPC */ - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + dpram_init_ipc(dpld); - atomic_set(&dpld->accessing, 0); + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_info("%s: ERR! no iod\n", ld->name); + return; + } - set_magic(dpld, DPRAM_MAGIC_CODE); - set_access(dpld, 1); - if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) - return -EACCES; + if (dpld->ext_op && dpld->ext_op->cp_start_handler) + dpld->ext_op->cp_start_handler(dpld); - ld->mode = LINK_MODE_IPC; + if (ld->mc->phone_state != STATE_ONLINE) { + mif_info("%s: phone_state: %d -> ONLINE\n", + ld->name, ld->mc->phone_state); + iod->modem_state_changed(iod, STATE_ONLINE); + } - return 0; + mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); } -/** - * reset_dpram_ipc - * @dpld: pointer to an instance of dpram_link_device structure - * - * Reset DPRAM with IPC map. - */ -static void reset_dpram_ipc(struct dpram_link_device *dpld) +static void command_handler(struct dpram_link_device *dpld, u16 cmd) { - int i; struct link_device *ld = &dpld->ld; - dpld->set_access(dpld, 0); - - /* Clear pointers in every circular queue */ - for (i = 0; i < ld->max_ipc_dev; i++) { - dpld->set_txq_head(dpld, i, 0); - dpld->set_txq_tail(dpld, i, 0); - dpld->set_rxq_head(dpld, i, 0); - dpld->set_rxq_tail(dpld, i, 0); - } - - dpld->set_magic(dpld, DPRAM_MAGIC_CODE); - dpld->set_access(dpld, 1); -} - -/** - * cmd_phone_start_handler - * @dpld: pointer to an instance of dpram_link_device structure - * - * Handles the PHONE_START command from a CP. - */ -static void cmd_phone_start_handler(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - struct io_device *iod; - - mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); - - iod = link_get_iod_with_format(ld, IPC_FMT); - if (!iod) { - mif_err("%s: ERR! no iod\n", ld->name); - return; - } - - init_dpram_ipc(dpld); - - iod->modem_state_changed(iod, STATE_ONLINE); - - if (dpld->ext_op && dpld->ext_op->cp_start_handler) { - dpld->ext_op->cp_start_handler(dpld); - } else { - mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_int2cp(dpld, INT_CMD(INT_CMD_INIT_END)); - } -} - -/** - * cmd_handler: processes a DPRAM command from a CP - * @dpld: pointer to an instance of dpram_link_device structure - * @cmd: DPRAM command from a CP - */ -static void cmd_handler(struct dpram_link_device *dpld, u16 cmd) -{ - struct link_device *ld = &dpld->ld; - - switch (INT_CMD_MASK(cmd)) { - case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(dpld); - break; + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + cmd_req_active_handler(dpld); + break; case INT_CMD_CRASH_RESET: - dpld->init_status = DPRAM_INIT_STATE_NONE; + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; cmd_crash_reset_handler(dpld); break; case INT_CMD_CRASH_EXIT: - dpld->init_status = DPRAM_INIT_STATE_NONE; + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; cmd_crash_exit_handler(dpld); break; case INT_CMD_PHONE_START: - dpld->init_status = DPRAM_INIT_STATE_READY; + dpld->dpram_init_status = DPRAM_INIT_STATE_READY; cmd_phone_start_handler(dpld); - complete_all(&ld->init_cmpl); + complete_all(&dpld->dpram_init_cmd); break; case INT_CMD_NV_REBUILDING: @@ -1100,14 +1143,14 @@ static void cmd_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&ld->pif_cmpl); + complete_all(&dpld->modem_pif_init_done); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_POWER_OFF: + case INT_CMD_NORMAL_PWR_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1122,1143 +1165,213 @@ static void cmd_handler(struct dpram_link_device *dpld, u16 cmd) } } -/** - * ipc_rx_work - * @work: pointer to an instance of the work_struct structure - * - * Invokes the recv method in the io_device instance to perform receiving IPC - * messages from each skb. - */ -static void ipc_rx_work(struct work_struct *work) -{ - struct dpram_link_device *dpld; - struct link_device *ld; - struct io_device *iod; - struct sk_buff *skb; - int i; - - dpld = container_of(work, struct dpram_link_device, rx_dwork.work); - ld = &dpld->ld; - - for (i = 0; i < ld->max_ipc_dev; i++) { - iod = dpld->iod[i]; - while (1) { - skb = skb_dequeue(ld->skb_rxq[i]); - if (!skb) - break; - - if (iod->recv_skb) { - iod->recv_skb(iod, ld, skb); - } else { - iod->recv(iod, ld, skb->data, skb->len); - dev_kfree_skb_any(skb); - } - } - } -} - -/** - * get_rxq_rcvd - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * OUT @dcst: pointer to an instance of circ_status structure - * - * Stores {start address of the buffer in a RXQ, size of the buffer, in & out - * pointer values, size of received data} into the 'stat' instance. - * - * Returns an error code. - */ -static int get_rxq_rcvd(struct dpram_link_device *dpld, int dev, - struct mem_status *mst, struct circ_status *dcst) -{ - struct link_device *ld = &dpld->ld; - - dcst->buff = get_rxq_buff(dpld, dev); - dcst->qsize = get_rxq_buff_size(dpld, dev); - dcst->in = mst->head[dev][RX]; - dcst->out = mst->tail[dev][RX]; - dcst->size = circ_get_usage(dcst->qsize, dcst->in, dcst->out); - - if (circ_valid(dcst->qsize, dcst->in, dcst->out)) { - mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", - ld->name, get_dev_name(dev), dcst->qsize, dcst->in, - dcst->out, dcst->size); - return 0; - } else { - mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", - ld->name, get_dev_name(dev), dcst->qsize, dcst->in, - dcst->out); - return -EIO; - } -} - -/** - * rx_sipc4_frames - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * - * Returns - * ret < 0 : error - * ret == 0 : ILLEGAL status - * ret > 0 : valid data - * - * Must be invoked only when there is data in the corresponding RXQ. - * - * Requires a bottom half (e.g. ipc_rx_task) that will invoke the recv method in - * the io_device instance. - */ -static int rx_sipc4_frames(struct dpram_link_device *dpld, int dev, - struct mem_status *mst) -{ - struct link_device *ld = &dpld->ld; - struct sk_buff *skb; - u8 *dst; - struct circ_status dcst; - int rcvd; - - rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); - if (unlikely(rcvd < 0)) { -#ifdef DEBUG_MODEM_IF - trigger_forced_cp_crash(dpld); -#endif - goto exit; - } - rcvd = dcst.size; - - /* Allocate an skb */ - skb = dev_alloc_skb(rcvd); - if (!skb) { - mif_info("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); - rcvd = -ENOMEM; - goto exit; - } - - /* Read data from the RXQ */ - dst = skb_put(skb, rcvd); - circ_read16_from_io(dst, dcst.buff, dcst.qsize, dcst.out, rcvd); - - /* Store the skb to the corresponding skb_rxq */ - skb_queue_tail(ld->skb_rxq[dev], skb); - -exit: - /* Update tail (out) pointer to empty out the RXQ */ - set_rxq_tail(dpld, dev, dcst.in); - - return rcvd; -} - -/** - * rx_sipc5_frames - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * - * Returns - * ret < 0 : error - * ret == 0 : ILLEGAL status - * ret > 0 : valid data - * - * Must be invoked only when there is data in the corresponding RXQ. - * - * Requires a recv_skb method in the io_device instance, so this function must - * be used for only SIPC5. - */ -static int rx_sipc5_frames(struct dpram_link_device *dpld, int dev, - struct mem_status *mst) -{ - struct link_device *ld = &dpld->ld; - struct sk_buff *skb; - /** - * variables for the status of the circular queue - */ - u8 __iomem *src; - u8 hdr[SIPC5_MIN_HEADER_SIZE]; - struct circ_status dcst; - /** - * variables for RX processing - */ - int qsize; /* size of the queue */ - int rcvd; /* size of data in the RXQ or error */ - int rest; /* size of the rest data */ - int idx; /* index to the start of current frame */ - u8 *frm; /* pointer to current frame */ - u8 *dst; /* pointer to the destination buffer */ - int tot; /* total length including padding data */ - /** - * variables for debug logging - */ - struct mif_irq_map map; - - /* Get data size in the RXQ and in/out pointer values */ - rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); - if (unlikely(rcvd < 0)) { - mif_err("%s: ERR! rcvd %d < 0\n", ld->name, rcvd); - goto exit; - } - - rcvd = dcst.size; - src = dcst.buff; - qsize = dcst.qsize; - idx = dcst.out; - - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } - -#if 0 - skb = dev_alloc_skb(rcvd); - - /* - ** If there is enough free space for an skb to store received - ** data at once, - */ - if (skb) { - /* Read all data from the RXQ to the skb */ - dst = skb_put(skb, rcvd); - if (unlikely(dpld->strict_io_access)) - circ_read16_from_io(dst, src, qsize, idx, rcvd); - else - circ_read(dst, src, qsize, idx, rcvd); - -#ifdef DEBUG_MODEM_IF - /* Verify data copied to the skb */ - if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { - mif_err("%s: memcmp16_to_io fail\n", ld->name); - rcvd = -EIO; - goto exit; - } -#endif - - /* Store the skb to the corresponding skb_rxq */ - skb_queue_tail(ld->skb_rxq[dev], skb); - - goto exit; - } - - /* - ** If there was no enough space to store received data at once, - */ -#endif - - rest = rcvd; - while (rest > 0) { - /* Calculate the start of an SIPC5 frame */ - frm = src + idx; - - /* Copy the header in the frame to the header buffer */ - if (unlikely(dpld->strict_io_access)) - memcpy16_from_io(hdr, frm, SIPC5_MIN_HEADER_SIZE); - else - memcpy(hdr, frm, SIPC5_MIN_HEADER_SIZE); - - /* Check the config field in the header */ - if (unlikely(!sipc5_start_valid(hdr))) { - char str[MIF_MAX_STR_LEN]; - snprintf(str, MIF_MAX_STR_LEN, "%s: BAD CONFIG", - ld->mc->name); - mif_err("%s: ERR! %s INVALID config 0x%02X\n", - ld->name, get_dev_name(dev), hdr[0]); - pr_ipc(1, str, hdr, 4); - rcvd = -EBADMSG; - goto exit; - } - - /* Verify the total length of the frame (data + padding) */ - tot = sipc5_get_total_len(hdr); - if (unlikely(tot > rest)) { - char str[MIF_MAX_STR_LEN]; - snprintf(str, MIF_MAX_STR_LEN, "%s: BAD LENGTH", - ld->mc->name); - mif_err("%s: ERR! %s tot %d > rest %d\n", - ld->name, get_dev_name(dev), tot, rest); - pr_ipc(1, str, hdr, 4); - rcvd = -EBADMSG; -#if defined(CONFIG_MACH_C1_KOR_SKT) || defined(CONFIG_MACH_C1_KOR_KT) || defined(CONFIG_MACH_C1_KOR_LGT) - return rcvd; -#else - goto exit; -#endif - } - - /* Allocate an skb */ - skb = dev_alloc_skb(tot); - if (!skb) { - mif_err("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); - rcvd = -ENOMEM; - goto exit; - } - - /* Set the attribute of the skb as "single frame" */ - skbpriv(skb)->single_frame = true; - - /* Read the frame from the RXQ */ - dst = skb_put(skb, tot); - if (unlikely(dpld->strict_io_access)) - circ_read16_from_io(dst, src, qsize, idx, tot); - else - circ_read(dst, src, qsize, idx, tot); - -#ifdef DEBUG_MODEM_IF - /* Take a log for debugging */ - if (unlikely(dev == IPC_FMT)) { - size_t len = (skb->len > 32) ? 32 : skb->len; - char str[MIF_MAX_STR_LEN]; - snprintf(str, MIF_MAX_STR_LEN, "%s: CP2MIF", - ld->mc->name); - pr_ipc(0, str, skb->data, len); - } -#endif - -#ifdef DEBUG_MODEM_IF - /* Verify data copied to the skb */ - if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { - mif_err("%s: memcmp16_to_io fail\n", ld->name); - rcvd = -EIO; - goto exit; - } -#endif - - /* Store the skb to the corresponding skb_rxq */ - skb_queue_tail(ld->skb_rxq[dev], skb); - - /* Calculate new idx value */ - rest -= tot; - idx += tot; - if (unlikely(idx >= qsize)) - idx -= qsize; - } - -exit: -#ifdef DEBUG_MODEM_IF - if (rcvd < 0) - trigger_forced_cp_crash(dpld); -#endif - - /* Update tail (out) pointer to empty out the RXQ */ - set_rxq_tail(dpld, dev, dcst.in); - - return rcvd; -} - -/** - * msg_handler: receives IPC messages from every RXQ - * @dpld: pointer to an instance of dpram_link_device structure - * @stat: pointer to an instance of mem_status structure - * - * 1) Receives all IPC message frames currently in every DPRAM RXQ. - * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. - * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if - * there is any RES_ACK response. - */ -static void msg_handler(struct dpram_link_device *dpld, struct mem_status *stat) -{ - struct link_device *ld = &dpld->ld; - int i = 0; - int ret = 0; - u16 mask = 0; - u16 intr = stat->int2ap; - - if (!ipc_active(dpld)) - return; - - /* Read data from DPRAM */ - for (i = 0; i < ld->max_ipc_dev; i++) { - /* Invoke an RX function only when there is data in the RXQ */ - if (unlikely(stat->head[i][RX] == stat->tail[i][RX])) { - mif_debug("%s: %s_RXQ is empty\n", - ld->name, get_dev_name(i)); - } else { - if (unlikely(ld->ipc_version < SIPC_VER_50)) - ret = rx_sipc4_frames(dpld, i, stat); - else - ret = rx_sipc5_frames(dpld, i, stat); - if (ret < 0) - reset_rxq_circ(dpld, i); - } - } - - /* Schedule soft IRQ for RX */ - queue_delayed_work(system_nrt_wq, &dpld->rx_dwork, 0); - - /* Check and process REQ_ACK (at this time, in == out) */ - if (unlikely(intr & INT_MASK_REQ_ACK_SET)) { - for (i = 0; i < ld->max_ipc_dev; i++) { - if (intr & get_mask_req_ack(dpld, i)) { - mif_debug("%s: set %s_RES_ACK\n", - ld->name, get_dev_name(i)); - mask |= get_mask_res_ack(dpld, i); - } - } - - send_int2cp(dpld, INT_NON_CMD(mask)); - } - - /* Check and process RES_ACK */ - if (unlikely(intr & INT_MASK_RES_ACK_SET)) { - for (i = 0; i < ld->max_ipc_dev; i++) { - if (intr & get_mask_res_ack(dpld, i)) { -#ifdef DEBUG_MODEM_IF - mif_info("%s: recv %s_RES_ACK\n", - ld->name, get_dev_name(i)); - print_circ_status(ld, i, stat); -#endif - complete(&dpld->req_ack_cmpl[i]); - } - } - } -} - -/** - * cmd_msg_handler: processes a DPRAM command or receives IPC messages - * @dpld: pointer to an instance of dpram_link_device structure - * @stat: pointer to an instance of mem_status structure - * - * Invokes cmd_handler for a DPRAM command or msg_handler for IPC messages. - */ -static inline void cmd_msg_handler(struct dpram_link_device *dpld, - struct mem_status *stat) -{ - struct dpram_ext_op *ext_op = dpld->ext_op; - struct mem_status *mst = msq_get_free_slot(&dpld->stat_list); - u16 intr = stat->int2ap; - - memcpy(mst, stat, sizeof(struct mem_status)); - - if (unlikely(INT_CMD_VALID(intr))) { - if (ext_op && ext_op->cmd_handler) - ext_op->cmd_handler(dpld, intr); - else - cmd_handler(dpld, intr); - } else { - msg_handler(dpld, stat); - } -} - -/** - * intr_handler: processes an interrupt from a CP - * @dpld: pointer to an instance of dpram_link_device structure - * @stat: pointer to an instance of mem_status structure - * - * Call flow for normal interrupt handling: - * cmd_msg_handler -> cmd_handler -> cmd_xxx_handler - * cmd_msg_handler -> msg_handler -> rx_sipc5_frames -> ... - */ -static inline void intr_handler(struct dpram_link_device *dpld, - struct mem_status *stat) -{ - char *name = dpld->ld.name; - u16 intr = stat->int2ap; - - if (unlikely(intr == INT_POWERSAFE_FAIL)) { - mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); - return; - } - - if (unlikely(EXT_UDL_CMD(intr))) { - if (likely(EXT_INT_VALID(intr))) { - if (UDL_CMD_VALID(intr)) - udl_command_handler(dpld, intr); - else if (EXT_CMD_VALID(intr)) - ext_command_handler(dpld, intr); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", - name, intr); - } else { - mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); - } - - return; - } - - if (likely(INT_VALID(intr))) - cmd_msg_handler(dpld, stat); - else - mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); -} - -/** - * ap_idpram_irq_handler: interrupt handler for an internal DPRAM in an AP - * @irq: IRQ number - * @data: pointer to a data - * - * 1) Reads the interrupt value - * 2) Performs interrupt handling - */ -static irqreturn_t ap_idpram_irq_handler(int irq, void *data) -{ - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - struct modemlink_dpram_data *dpram = dpld->dpram; - struct mem_status stat; - - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; - - get_dpram_status(dpld, RX, &stat); - - intr_handler(dpld, &stat); - - if (likely(dpram->clear_int2ap)) - dpram->clear_int2ap(); - - return IRQ_HANDLED; -} - -/** - * cp_idpram_irq_handler: interrupt handler for an internal DPRAM in a CP - * @irq: IRQ number - * @data: pointer to a data - * - * 1) Wakes up the DPRAM - * 2) Reads the interrupt value - * 3) Performs interrupt handling - * 4) Clears the interrupt port (port = memory or register) - * 5) Allows the DPRAM to sleep - */ -static irqreturn_t cp_idpram_irq_handler(int irq, void *data) -{ - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - struct dpram_ext_op *ext_op = dpld->ext_op; - struct mem_status stat; - - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { - mif_err("%s: ERR! ld->mode == LINK_MODE_OFFLINE\n", ld->name); - get_dpram_status(dpld, RX, &stat); -#ifdef DEBUG_MODEM_IF - print_mem_status(ld, &stat); -#endif - return IRQ_HANDLED; - } - - if (dpram_wake_up(dpld) < 0) { - trigger_forced_cp_crash(dpld); - return IRQ_HANDLED; - } - - get_dpram_status(dpld, RX, &stat); - - intr_handler(dpld, &stat); - - if (likely(ext_op && ext_op->clear_int2ap)) - ext_op->clear_int2ap(dpld); - - dpram_allow_sleep(dpld); - - return IRQ_HANDLED; -} - -/** - * ext_dpram_irq_handler: interrupt handler for a normal external DPRAM - * @irq: IRQ number - * @data: pointer to a data - * - * 1) Reads the interrupt value - * 2) Performs interrupt handling - */ -static irqreturn_t ext_dpram_irq_handler(int irq, void *data) -{ - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - struct mem_status stat; - - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; - - get_dpram_status(dpld, RX, &stat); - - intr_handler(dpld, &stat); - - return IRQ_HANDLED; -} - -/** - * get_txq_space - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * OUT @stat: pointer to an instance of circ_status structure - * - * Stores {start address of the buffer in a TXQ, size of the buffer, in & out - * pointer values, size of free space} into the 'stat' instance. - * - * Returns the size of free space in the buffer or an error code. - */ -static int get_txq_space(struct dpram_link_device *dpld, int dev, - struct circ_status *stat) +static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) { struct link_device *ld = &dpld->ld; - int cnt = 0; - u32 qsize; - u32 head; - u32 tail; - int space; - - while (1) { - qsize = get_txq_buff_size(dpld, dev); - head = get_txq_head(dpld, dev); - tail = get_txq_tail(dpld, dev); - space = circ_get_space(qsize, head, tail); - - mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", - ld->name, get_dev_name(dev), qsize, head, tail, space); - - if (circ_valid(qsize, head, tail)) - break; + u16 resp; - cnt++; - mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " - "space:%d}, count %d\n", - ld->name, get_dev_name(dev), qsize, head, tail, - space, cnt); - if (cnt >= MAX_RETRY_CNT) { - space = -EIO; - break; + switch (EXT_CMD_MASK(cmd)) { + case EXT_CMD_SET_SPEED_LOW: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); + send_intr(dpld, resp); } + break; - udelay(100); - } - - stat->buff = get_txq_buff(dpld, dev); - stat->qsize = qsize; - stat->in = head; - stat->out = tail; - stat->size = space; - - return space; -} - -/** - * write_ipc_to_txq - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @stat: pointer to an instance of circ_status structure - * @skb: pointer to an instance of sk_buff structure - * - * Must be invoked only when there is enough space in the TXQ. - */ -static void write_ipc_to_txq(struct dpram_link_device *dpld, int dev, - struct circ_status *stat, struct sk_buff *skb) -{ - struct link_device *ld = &dpld->ld; - u8 __iomem *buff = stat->buff; - u32 qsize = stat->qsize; - u32 in = stat->in; - u8 *src = skb->data; - u32 len = skb->len; - struct mif_irq_map map; - - /* Write data to the TXQ */ - if (unlikely(dpld->strict_io_access)) - circ_write16_to_io(buff, src, qsize, in, len); - else - circ_write(buff, src, qsize, in, len); - - /* Update new head (in) pointer */ - set_txq_head(dpld, dev, circ_new_pointer(qsize, in, len)); - - /* Take a log for debugging */ - if (dev == IPC_FMT) { -#ifdef DEBUG_MODEM_IF - char tag[MIF_MAX_STR_LEN]; - snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2CP", ld->mc->name); - pr_ipc(0, tag, src, (len > 32 ? 32 : len)); -#endif - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); - mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); - } - -#ifdef DEBUG_MODEM_IF - /* Verify data written to the TXQ */ - if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { - mif_err("%s: memcmp16_to_io fail\n", ld->name); - trigger_forced_cp_crash(dpld); - } -#endif -} - -/** - * xmit_ipc_msg - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. - * - * Returns total length of IPC messages transmitted or an error code. - */ -static int xmit_ipc_msg(struct dpram_link_device *dpld, int dev) -{ - struct link_device *ld = &dpld->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - struct sk_buff *skb; - unsigned long flags; - struct circ_status stat; - int space; - int copied = 0; - - /* Acquire the spin lock for a TXQ */ - spin_lock_irqsave(&dpld->tx_lock[dev], flags); - - while (1) { - /* Get the size of free space in the TXQ */ - space = get_txq_space(dpld, dev, &stat); - if (unlikely(space < 0)) { -#ifdef DEBUG_MODEM_IF - /* Trigger a enforced CP crash */ - trigger_forced_cp_crash(dpld); -#endif - /* Empty out the TXQ */ - reset_txq_circ(dpld, dev); - copied = -EIO; - break; + case EXT_CMD_SET_SPEED_MID: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_MID); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); + send_intr(dpld, resp); } + break; - skb = skb_dequeue(txq); - if (unlikely(!skb)) - break; - - /* Check the free space size comparing with skb->len */ - if (unlikely(space < skb->len)) { -#ifdef DEBUG_MODEM_IF - struct mem_status mst; -#endif - /* Set res_required flag for the "dev" */ - atomic_set(&dpld->res_required[dev], 1); - - /* Take the skb back to the skb_txq */ - skb_queue_head(txq, skb); - - mif_info("%s: NOSPC in %s_TXQ" - "{qsize:%u in:%u out:%u}, free:%u < len:%u\n", - ld->name, CALLER, get_dev_name(dev), - stat.qsize, stat.in, stat.out, space, skb->len); -#ifdef DEBUG_MODEM_IF - get_dpram_status(dpld, TX, &mst); - print_circ_status(ld, dev, &mst); -#endif - copied = -ENOSPC; - break; + case EXT_CMD_SET_SPEED_HIGH: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); + send_intr(dpld, resp); } + break; - /* TX only when there is enough space in the TXQ */ - write_ipc_to_txq(dpld, dev, &stat, skb); - copied += skb->len; - dev_kfree_skb_any(skb); + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + break; } - - /* Release the spin lock */ - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - - return copied; } -/** - * wait_for_res_ack - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * 1) Sends an REQ_ACK interrupt for @dev to CP. - * 2) Waits for the corresponding RES_ACK for @dev from CP. - * - * Returns the return value from wait_for_completion_interruptible_timeout(). - */ -static int wait_for_res_ack(struct dpram_link_device *dpld, int dev) +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) { struct link_device *ld = &dpld->ld; - struct completion *cmpl = &dpld->req_ack_cmpl[dev]; - unsigned long timeout = msecs_to_jiffies(dpld->res_ack_wait_timeout); - int ret; - u16 mask; - -#ifdef DEBUG_MODEM_IF - mif_info("%s: send %s_REQ_ACK\n", ld->name, get_dev_name(dev)); -#endif - - mask = get_mask_req_ack(dpld, dev); - send_int2cp(dpld, INT_NON_CMD(mask)); - - ret = wait_for_completion_interruptible_timeout(cmpl, timeout); - /* ret == 0 on timeout, ret < 0 if interrupted */ - if (ret < 0) { - mif_info("%s: %s: wait_for_completion interrupted! (ret %d)\n", - ld->name, get_dev_name(dev), ret); - goto exit; - } - - if (ret == 0) { - struct mem_status mst; - get_dpram_status(dpld, TX, &mst); - - mif_info("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", - ld->name, get_dev_name(dev)); - /* - ** The TXQ must be checked whether or not it is empty, because - ** an interrupt mask can be overwritten by the next interrupt. - */ - if (mst.head[dev][TX] == mst.tail[dev][TX]) { - ret = get_txq_buff_size(dpld, dev); -#ifdef DEBUG_MODEM_IF - mif_info("%s: %s_TXQ has been emptied\n", - ld->name, get_dev_name(dev)); - print_circ_status(ld, dev, &mst); -#endif - } - } - -exit: - return ret; -} - -/** - * process_res_ack - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() - * function. - * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). - * 3) Restarts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. - * - * Returns the return value from xmit_ipc_msg(). - */ -static int process_res_ack(struct dpram_link_device *dpld, int dev) -{ - int ret; - u16 mask; - - ret = xmit_ipc_msg(dpld, dev); - if (ret > 0) { - mask = get_mask_send(dpld, dev); - send_int2cp(dpld, INT_NON_CMD(mask)); - get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); - } - - if (ret >= 0) - atomic_set(&dpld->res_required[dev], 0); - - return ret; -} - -/** - * fmt_tx_work: performs TX for FMT IPC device under DPRAM flow control - * @work: pointer to an instance of the work_struct structure - * - * 1) Starts waiting for RES_ACK of FMT IPC device. - * 2) Returns immediately if the wait is interrupted. - * 3) Restarts DPRAM flow control if there is a timeout from the wait. - * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. - */ -static void fmt_tx_work(struct work_struct *work) -{ - struct link_device *ld; - struct dpram_link_device *dpld; - unsigned long delay = 0; - int ret; - - ld = container_of(work, struct link_device, fmt_tx_dwork.work); - dpld = to_dpram_link_device(ld); - - ret = wait_for_res_ack(dpld, IPC_FMT); - /* ret < 0 if interrupted */ - if (ret < 0) - return; - - /* ret == 0 on timeout */ - if (ret == 0) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); - return; - } - - ret = process_res_ack(dpld, IPC_FMT); - if (ret >= 0) { - dpram_allow_sleep(dpld); - return; - } - - /* At this point, ret < 0 */ - if (ret == -ENOSPC) - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], delay); -} - -/** - * raw_tx_work: performs TX for RAW IPC device under DPRAM flow control. - * @work: pointer to an instance of the work_struct structure - * - * 1) Starts waiting for RES_ACK of RAW IPC device. - * 2) Returns immediately if the wait is interrupted. - * 3) Restarts DPRAM flow control if there is a timeout from the wait. - * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. - */ -static void raw_tx_work(struct work_struct *work) -{ - struct link_device *ld; - struct dpram_link_device *dpld; - unsigned long delay = 0; - int ret; - - ld = container_of(work, struct link_device, raw_tx_dwork.work); - dpld = to_dpram_link_device(ld); - - ret = wait_for_res_ack(dpld, IPC_RAW); - /* ret < 0 if interrupted */ - if (ret < 0) - return; - - /* ret == 0 on timeout */ - if (ret == 0) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); + if (cmd & UDL_RESULT_FAIL) { + mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); return; } - ret = process_res_ack(dpld, IPC_RAW); - if (ret >= 0) { - dpram_allow_sleep(dpld); - mif_netif_wake(ld); - return; + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); + send_intr(dpld, CMD_IMG_START_REQ); + break; + default: + complete_all(&dpld->udl_cmd_complete); } - - /* At this point, ret < 0 */ - if (ret == -ENOSPC) - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], delay); } -/** - * rfs_tx_work: performs TX for RFS IPC device under DPRAM flow control - * @work: pointer to an instance of the work_struct structure - * - * 1) Starts waiting for RES_ACK of RFS IPC device. - * 2) Returns immediately if the wait is interrupted. - * 3) Restarts DPRAM flow control if there is a timeout from the wait. - * 4) Otherwise, it performs processing RES_ACK for RFS IPC device. - */ -static void rfs_tx_work(struct work_struct *work) +static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr) { - struct link_device *ld; - struct dpram_link_device *dpld; - unsigned long delay = 0; - int ret; - - ld = container_of(work, struct link_device, rfs_tx_dwork.work); - dpld = to_dpram_link_device(ld); + if (unlikely(INT_CMD_VALID(intr))) + command_handler(dpld, intr); + else + non_command_handler(dpld, intr); +} - ret = wait_for_res_ack(dpld, IPC_RFS); - /* ret < 0 if interrupted */ - if (ret < 0) - return; +static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr) +{ + char *name = dpld->ld.name; - /* ret == 0 on timeout */ - if (ret == 0) { - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], 0); + if (unlikely(intr == INT_POWERSAFE_FAIL)) { + mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); return; } - ret = process_res_ack(dpld, IPC_RFS); - if (ret >= 0) { - dpram_allow_sleep(dpld); + if (unlikely(EXT_UDL_CMD(intr))) { + if (likely(EXT_INT_VALID(intr))) { + if (UDL_CMD_VALID(intr)) + udl_command_handler(dpld, intr); + else if (EXT_CMD_VALID(intr)) + ext_command_handler(dpld, intr); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", + name, intr); + } else { + mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); + } return; } - /* At this point, ret < 0 */ - if (ret == -ENOSPC) - queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], delay); + if (likely(INT_VALID(intr))) + dpram_ipc_rx(dpld, intr); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); } -/** - * dpram_send_ipc - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() - * function. - * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). - * 3) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. - */ -static int dpram_send_ipc(struct dpram_link_device *dpld, int dev) +static irqreturn_t ap_idpram_irq_handler(int irq, void *data) { - struct link_device *ld = &dpld->ld; - int ret; - u16 mask; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap = recv_intr(dpld); - if (atomic_read(&dpld->res_required[dev]) > 0) { - mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); - return 0; - } + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; - if (dpram_wake_up(dpld) < 0) { - trigger_forced_cp_crash(dpld); - return -EIO; - } + dpram_intr_handler(dpld, int2ap); - if (!ipc_active(dpld)) { - mif_info("%s: IPC is NOT active\n", ld->name); - ret = -EIO; - goto exit; - } + return IRQ_HANDLED; +} - ret = xmit_ipc_msg(dpld, dev); - if (likely(ret > 0)) { - mask = get_mask_send(dpld, dev); - send_int2cp(dpld, INT_NON_CMD(mask)); - get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); - goto exit; - } +static irqreturn_t cp_idpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap; - /* If there was no TX, just exit */ - if (ret == 0) - goto exit; + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; - /* At this point, ret < 0 */ - if (ret == -ENOSPC) { - /* Prohibit DPRAM from sleeping until the TXQ buffer is empty */ - if (dpram_wake_up(dpld) < 0) { - trigger_forced_cp_crash(dpld); - goto exit; - } + if (dpram_wake_up(dpld) < 0) { + log_dpram_status(dpld); + trigger_force_cp_crash(dpld); + return IRQ_HANDLED; + } - /*----------------------------------------------------*/ - /* dpld->res_required[dev] was set in xmit_ipc_msg(). */ - /*----------------------------------------------------*/ + int2ap = recv_intr(dpld); - if (dev == IPC_RAW) - mif_netif_stop(ld); + dpram_intr_handler(dpld, int2ap); - queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], 0); - } + clear_intr(dpld); -exit: dpram_allow_sleep(dpld); - return ret; + + return IRQ_HANDLED; } -/** - * pm_tx_work: performs TX while DPRAM PM is locked - * @work: pointer to an instance of the work_struct structure - */ -static void pm_tx_work(struct work_struct *work) +static irqreturn_t ext_dpram_irq_handler(int irq, void *data) { - struct idpram_pm_data *pm_data; - struct idpram_pm_op *pm_op; - struct dpram_link_device *dpld; - struct link_device *ld; - struct workqueue_struct *pm_wq = system_nrt_wq; - int i; - int ret; - unsigned long delay = 0; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap = recv_intr(dpld); - pm_data = container_of(work, struct idpram_pm_data, tx_dwork.work); - dpld = container_of(pm_data, struct dpram_link_device, pm_data); - ld = &dpld->ld; - pm_op = dpld->pm_op; + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; - if (pm_op->locked(dpld)) { - queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); - return; - } + dpram_intr_handler(dpld, int2ap); - /* Here, PM is not locked. */ - for (i = 0; i < ld->max_ipc_dev; i++) { - ret = dpram_send_ipc(dpld, i); - if (ret < 0) { - struct io_device *iod = dpld->iod[i]; - mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", - iod->name, ld->name, ret); - } - } + return IRQ_HANDLED; } -/** - * dpram_try_send_ipc - * @dpld: pointer to an instance of dpram_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @iod: pointer to an instance of the io_device structure - * @skb: pointer to an skb that will be transmitted - * - * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. - * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() - * function. - * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). - * 4) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. - */ -static void dpram_try_send_ipc(struct dpram_link_device *dpld, int dev, +static void dpram_send_ipc(struct link_device *ld, int dev, struct io_device *iod, struct sk_buff *skb) { - struct link_device *ld = &dpld->ld; - struct idpram_pm_data *pm_data = &dpld->pm_data; - struct idpram_pm_op *pm_op = dpld->pm_op; - struct workqueue_struct *pm_wq = system_nrt_wq; - unsigned long delay = msecs_to_jiffies(10); + struct dpram_link_device *dpld = to_dpram_link_device(ld); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; - - if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { - mif_info("%s: %s txq->qlen %d >= %d\n", ld->name, - get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); - dev_kfree_skb_any(skb); - return; - } + u16 mask; skb_queue_tail(txq, skb); + if (txq->qlen > 1024) { + mif_debug("%s: %s txq->qlen %d > 1024\n", + ld->name, get_dev_name(dev), txq->qlen); + } - if (pm_op && pm_op->locked) { - if (pm_op->locked(dpld)) { - queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); + if (dpld->dp_type == CP_IDPRAM) { + if (dpram_wake_up(dpld) < 0) { + trigger_force_cp_crash(dpld); return; } + } - /* Here, PM is not locked. */ - if (work_pending(&pm_data->tx_dwork.work)) - cancel_delayed_work_sync(&pm_data->tx_dwork); + if (!dpram_ipc_active(dpld)) + goto exit; + + if (atomic_read(&dpld->res_required[dev]) > 0) { + mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + goto exit; } - ret = dpram_send_ipc(dpld, dev); - if (ret < 0) { - mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", - iod->name, ld->name, ret); + ret = dpram_try_ipc_tx(dpld, dev); + if (ret > 0) { + mask = get_mask_send(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); + } else if (ret == -ENOSPC) { + mask = get_mask_req_ack(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); + mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); + } else { + mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); } + +exit: + if (dpld->dp_type == CP_IDPRAM) + dpram_allow_sleep(dpld); } static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - if (dpld->ext_op && dpld->ext_op->xmit_binary) - return dpld->ext_op->xmit_binary(dpld, skb); + if (dpld->ext_op && dpld->ext_op->download_binary) + return dpld->ext_op->download_binary(dpld, skb); else return -ENODEV; } -/** - * dpram_send - * @ld: pointer to an instance of the link_device structure - * @iod: pointer to an instance of the io_device structure - * @skb: pointer to an skb that will be transmitted - * - * Returns the length of data transmitted or an error code. - * - * Normal call flow for an IPC message: - * dpram_try_send_ipc -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq - * - * Call flow on PM lock in a DPRAM IPC TXQ: - * dpram_try_send_ipc ,,, queue_delayed_work - * => pm_tx_work -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq - * - * Call flow on congestion in a DPRAM IPC TXQ: - * dpram_try_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work - * => xxx_tx_work -> wait_for_res_ack - * => msg_handler - * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) - */ static int dpram_send(struct link_device *ld, struct io_device *iod, - struct sk_buff *skb) + struct sk_buff *skb) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); - int dev = iod->format; + enum dev_format dev = iod->format; int len = skb->len; switch (dev) { @@ -2266,7 +1379,7 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_try_send_ipc(dpld, dev, iod, skb); + dpram_send_ipc(ld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); @@ -2283,45 +1396,23 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_xmit_boot(struct link_device *ld, struct io_device *iod, - unsigned long arg) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - if (dpld->ext_op && dpld->ext_op->xmit_boot) - return dpld->ext_op->xmit_boot(dpld, arg); - else - return -ENODEV; -} - -static int dpram_set_dload_magic(struct link_device *ld, struct io_device *iod) +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - - ld->mode = LINK_MODE_DLOAD; - - mif_err("%s: magic = 0x%08X\n", ld->name, DP_MAGIC_DMDL); - iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); - + trigger_force_cp_crash(dpld); return 0; } -static int dpram_dload_firmware(struct link_device *ld, struct io_device *iod, - unsigned long arg) +static void dpram_dump_memory(struct link_device *ld, char *buff) { struct dpram_link_device *dpld = to_dpram_link_device(ld); + u8 __iomem *base = dpld->dpctl->dp_base; + u32 size = dpld->dpctl->dp_size; - if (dpld->ext_op && dpld->ext_op->firm_update) - return dpld->ext_op->firm_update(dpld, arg); - else - return -ENODEV; -} + if (dpld->dp_type == CP_IDPRAM) + dpram_wake_up(dpld); -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - trigger_forced_cp_crash(dpld); - return 0; + memcpy(buff, base, size); } static int dpram_dump_start(struct link_device *ld, struct io_device *iod) @@ -2340,24 +1431,13 @@ static int dpram_dump_update(struct link_device *ld, struct io_device *iod, struct dpram_link_device *dpld = to_dpram_link_device(ld); if (dpld->ext_op && dpld->ext_op->dump_update) - return dpld->ext_op->dump_update(dpld, arg); - else - return -ENODEV; -} - -static int dpram_dump_finish(struct link_device *ld, struct io_device *iod, - unsigned long arg) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - if (dpld->ext_op && dpld->ext_op->dump_finish) - return dpld->ext_op->dump_finish(dpld, arg); + return dpld->ext_op->dump_update(dpld, (void *)arg); else return -ENODEV; } static int dpram_ioctl(struct link_device *ld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); int err = 0; @@ -2367,21 +1447,17 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->init_status; - - case IOCTL_MIF_DPRAM_DUMP: - if (copy_to_user((void __user *)arg, &dpld->size, sizeof(u32))) - return -EFAULT; - - capture_dpram_snapshot(ld, iod); - break; + return dpld->dpram_init_status; default: - if (dpld->ext_ioctl) - return dpld->ext_ioctl(dpld, iod, cmd, arg); + if (dpld->ext_ioctl) { + err = dpld->ext_ioctl(dpld, iod, cmd, arg); + } else { + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + } - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - return -EINVAL; + break; } return err; @@ -2392,9 +1468,9 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) struct dpram_ipc_16k_map *dpram_map; struct dpram_ipc_device *dev; - dpram_map = (struct dpram_ipc_16k_map *)dpld->base; + dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base; - /* "magic code" and "access enable" fields */ + /* magic code and access enable fields */ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; @@ -2443,201 +1519,167 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp; } -static int dpram_init_boot_map(struct dpram_link_device *dpld) +static int dpram_table_init(struct dpram_link_device *dpld) { - u8 __iomem *dp_base = dpld->base; - u32 magic_size = DP_DLOAD_MAGIC_SIZE; - u32 mbx_size = DP_MBX_SET_SIZE; + struct link_device *ld = &dpld->ld; + u8 __iomem *dp_base; + int i; + + if (!dpld->dp_base) { + mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); + return -EINVAL; + } + dp_base = dpld->dp_base; + + /* Map for IPC */ + if (dpld->dpctl->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, + sizeof(struct dpram_ipc_map)); + } else { + if (dpld->dp_size == DPRAM_SIZE_16KB) + dpram_remap_std_16k_region(dpld); + else + return -EINVAL; + } + + dpld->magic = dpld->ipc_map.magic; + dpld->access = dpld->ipc_map.access; + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->dev[i] = &dpld->ipc_map.dev[i]; + dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; + dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + /* Map for booting */ if (dpld->ext_op && dpld->ext_op->init_boot_map) { dpld->ext_op->init_boot_map(dpld); } else { dpld->bt_map.magic = (u32 *)(dp_base); - dpld->bt_map.buff = (u8 *)(dp_base + magic_size); - dpld->bt_map.space = dpld->size - (magic_size + mbx_size); + dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); + dpld->bt_map.size = dpld->dp_size - 8; } - return 0; -} - -static int dpram_init_dload_map(struct dpram_link_device *dpld) -{ - u8 __iomem *dp_base = dpld->base; - u32 magic_size = DP_DLOAD_MAGIC_SIZE; - u32 mbx_size = DP_MBX_SET_SIZE; - + /* Map for download (FOTA, UDL, etc.) */ if (dpld->ext_op && dpld->ext_op->init_dl_map) { dpld->ext_op->init_dl_map(dpld); } else { dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + magic_size); - dpld->dl_map.space = dpld->size - (magic_size + mbx_size); + dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); } - return 0; -} - -static int dpram_init_uload_map(struct dpram_link_device *dpld) -{ - u8 __iomem *dp_base = dpld->base; - u32 magic_size = DP_DLOAD_MAGIC_SIZE; - u32 mbx_size = DP_MBX_SET_SIZE; - + /* Map for upload mode */ if (dpld->ext_op && dpld->ext_op->init_ul_map) { dpld->ext_op->init_ul_map(dpld); } else { dpld->ul_map.magic = (u32 *)(dp_base); dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); - dpld->ul_map.space = dpld->size - (magic_size + mbx_size); - } - - return 0; -} - -static int dpram_init_ipc_map(struct dpram_link_device *dpld) -{ - int i; - struct link_device *ld = &dpld->ld; - - if (dpld->ext_op && dpld->ext_op->init_ipc_map) { - dpld->ext_op->init_ipc_map(dpld); - } else if (dpld->dpram->ipc_map) { - memcpy(&dpld->ipc_map, dpld->dpram->ipc_map, - sizeof(struct dpram_ipc_map)); - } else { - if (dpld->size == DPRAM_SIZE_16KB) - dpram_remap_std_16k_region(dpld); - else - return -EINVAL; } - dpld->magic = dpld->ipc_map.magic; - dpld->access = dpld->ipc_map.access; - for (i = 0; i < ld->max_ipc_dev; i++) - dpld->dev[i] = &dpld->ipc_map.dev[i]; - dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; - dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; - return 0; } static void dpram_setup_common_op(struct dpram_link_device *dpld) { - dpld->recv_intr = recv_int2ap; - dpld->send_intr = send_int2cp; + dpld->clear_intr = clear_intr; + dpld->recv_intr = recv_intr; + dpld->send_intr = send_intr; dpld->get_magic = get_magic; dpld->set_magic = set_magic; dpld->get_access = get_access; dpld->set_access = set_access; - dpld->get_txq_head = get_txq_head; - dpld->get_txq_tail = get_txq_tail; - dpld->set_txq_head = set_txq_head; - dpld->set_txq_tail = set_txq_tail; - dpld->get_txq_buff = get_txq_buff; - dpld->get_txq_buff_size = get_txq_buff_size; - dpld->get_rxq_head = get_rxq_head; - dpld->get_rxq_tail = get_rxq_tail; - dpld->set_rxq_head = set_rxq_head; - dpld->set_rxq_tail = set_rxq_tail; - dpld->get_rxq_buff = get_rxq_buff; - dpld->get_rxq_buff_size = get_rxq_buff_size; + dpld->get_tx_head = get_tx_head; + dpld->get_tx_tail = get_tx_tail; + dpld->set_tx_head = set_tx_head; + dpld->set_tx_tail = set_tx_tail; + dpld->get_tx_buff = get_tx_buff; + dpld->get_tx_buff_size = get_tx_buff_size; + dpld->get_rx_head = get_rx_head; + dpld->get_rx_tail = get_rx_tail; + dpld->set_rx_head = set_rx_head; + dpld->set_rx_tail = set_rx_tail; + dpld->get_rx_buff = get_rx_buff; + dpld->get_rx_buff_size = get_rx_buff_size; dpld->get_mask_req_ack = get_mask_req_ack; dpld->get_mask_res_ack = get_mask_res_ack; dpld->get_mask_send = get_mask_send; - dpld->get_dpram_status = get_dpram_status; - dpld->ipc_rx_handler = cmd_msg_handler; - dpld->reset_dpram_ipc = reset_dpram_ipc; + dpld->ipc_rx_handler = dpram_ipc_rx; } -static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +static int dpram_link_init(struct link_device *ld, struct io_device *iod) { - if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { - if (!atomic_read(&iod->opened)) { - ld->mode = LINK_MODE_OFFLINE; - mif_err("%s: %s: link mode is changed: IPC->OFFLINE\n", - iod->name, ld->name); - } - } + return 0; +} +static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +{ return; } struct link_device *dpram_create_link_device(struct platform_device *pdev) { + struct modem_data *mdm_data = NULL; struct dpram_link_device *dpld = NULL; struct link_device *ld = NULL; - struct modem_data *modem = NULL; - struct modemlink_dpram_data *dpram = NULL; struct resource *res = NULL; resource_size_t res_size; + struct modemlink_dpram_control *dpctl = NULL; + unsigned long task_data; int ret = 0; int i = 0; + int bsize; + int qsize; - /* - ** Alloc an instance of dpram_link_device structure - */ - dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); - if (!dpld) { - mif_err("ERR! kzalloc dpld fail\n"); + /* Get the platform data */ + mdm_data = (struct modem_data *)pdev->dev.platform_data; + if (!mdm_data) { + mif_info("ERR! mdm_data == NULL\n"); goto err; } - ld = &dpld->ld; + mif_info("modem = %s\n", mdm_data->name); + mif_info("link device = %s\n", mdm_data->link_name); - /* - ** Get the modem (platform) data - */ - modem = (struct modem_data *)pdev->dev.platform_data; - if (!modem) { - mif_err("ERR! modem == NULL\n"); + if (!mdm_data->dpram_ctl) { + mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); goto err; } - mif_info("modem = %s\n", modem->name); - mif_info("link device = %s\n", modem->link_name); - - /* - ** Retrieve modem data and DPRAM control data from the modem data - */ - ld->mdm_data = modem; - ld->name = modem->link_name; - ld->ipc_version = modem->ipc_version; - - if (!modem->dpram) { - mif_err("ERR! no modem->dpram\n"); + dpctl = mdm_data->dpram_ctl; + + /* Alloc DPRAM link device structure */ + dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); + if (!dpld) { + mif_info("ERR! kzalloc dpld fail\n"); goto err; } - dpram = modem->dpram; + ld = &dpld->ld; + + /* Retrieve modem data and DPRAM control data from the modem data */ + ld->mdm_data = mdm_data; + ld->name = mdm_data->link_name; + ld->ipc_version = mdm_data->ipc_version; - dpld->dpram = dpram; - dpld->type = dpram->type; - dpld->ap = dpram->ap; - dpld->strict_io_access = dpram->strict_io_access; + /* Retrieve the most basic data for IPC from the modem data */ + dpld->dpctl = dpctl; + dpld->dp_type = dpctl->dp_type; - if (ld->ipc_version < SIPC_VER_50) { - if (!modem->max_ipc_dev) { - mif_err("%s: ERR! no max_ipc_dev\n", ld->name); + if (mdm_data->ipc_version < SIPC_VER_50) { + if (!dpctl->max_ipc_dev) { + mif_info("ERR! no max_ipc_dev\n"); goto err; } - ld->aligned = dpram->aligned; - ld->max_ipc_dev = modem->max_ipc_dev; + ld->aligned = dpctl->aligned; + dpld->max_ipc_dev = dpctl->max_ipc_dev; } else { ld->aligned = 1; - ld->max_ipc_dev = MAX_SIPC5_DEV; + dpld->max_ipc_dev = MAX_SIPC5_DEV; } - /* - ** Set attributes as a link device - */ + /* Set attributes as a link device */ + ld->init_comm = dpram_link_init; ld->terminate_comm = dpram_link_terminate; ld->send = dpram_send; - ld->xmit_boot = dpram_xmit_boot; - ld->dload_start = dpram_set_dload_magic; - ld->firm_update = dpram_dload_firmware; ld->force_dump = dpram_force_dump; ld->dump_start = dpram_dump_start; ld->dump_update = dpram_dump_update; - ld->dump_finish = dpram_dump_finish; - /* IOCTL extension */ ld->ioctl = dpram_ioctl; INIT_LIST_HEAD(&ld->list); @@ -2649,217 +1691,129 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; - skb_queue_head_init(&ld->sk_fmt_rx_q); - skb_queue_head_init(&ld->sk_raw_rx_q); - skb_queue_head_init(&ld->sk_rfs_rx_q); - ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; - ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; - ld->skb_rxq[IPC_RFS] = &ld->sk_rfs_rx_q; - - init_completion(&ld->init_cmpl); - init_completion(&ld->pif_cmpl); - - /* - ** Set up function pointers - */ + /* Set up function pointers */ dpram_setup_common_op(dpld); - dpld->ext_op = dpram_get_ext_op(modem->modem_type); - if (dpld->ext_op) { - if (dpld->ext_op->ioctl) - dpld->ext_ioctl = dpld->ext_op->ioctl; - - if (dpld->ext_op->wakeup && dpld->ext_op->sleep) - dpld->need_wake_up = true; - } - - /* - ** Retrieve DPRAM resource - */ - if (!dpram->base) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - STR_DPRAM_BASE); + dpld->dpram_dump = dpram_dump_memory; + dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); + if (dpld->ext_op && dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; + + /* Retrieve DPRAM resource */ + if (!dpctl->dp_base) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - mif_err("%s: ERR! no DPRAM resource\n", ld->name); - goto err; - } - res_size = resource_size(res); - - dpram->base = ioremap_nocache(res->start, res_size); - if (!dpram->base) { - mif_err("%s: ERR! ioremap_nocache for BASE fail\n", + mif_info("%s: ERR! platform_get_resource fail\n", ld->name); goto err; } - dpram->size = res_size; - } - dpld->base = dpram->base; - dpld->size = dpram->size; - - mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", - ld->name, dpld->type, ld->aligned, (int)dpld->base, dpld->size); - - /* - ** Retrieve DPRAM SFR resource if exists - */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - STR_DPRAM_SFR_BASE); - if (res) { res_size = resource_size(res); - dpld->sfr_base = ioremap_nocache(res->start, res_size); - if (!dpld->sfr_base) { - mif_err("%s: ERR! ioremap_nocache for SFR fail\n", - ld->name); - goto err; - } - } - - /* - ** Initialize DPRAM maps (physical map -> logical map) - */ - ret = dpram_init_boot_map(dpld); - if (ret < 0) { - mif_err("%s: ERR! dpram_init_boot_map fail (err %d)\n", - ld->name, ret); - goto err; - } - ret = dpram_init_dload_map(dpld); - if (ret < 0) { - mif_err("%s: ERR! dpram_init_dload_map fail (err %d)\n", - ld->name, ret); - goto err; + dpctl->dp_base = ioremap_nocache(res->start, res_size); + dpctl->dp_size = res_size; } + dpld->dp_base = dpctl->dp_base; + dpld->dp_size = dpctl->dp_size; - ret = dpram_init_uload_map(dpld); - if (ret < 0) { - mif_err("%s: ERR! dpram_init_uload_map fail (err %d)\n", - ld->name, ret); - goto err; - } + mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", + ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, + dpld->dp_size); - ret = dpram_init_ipc_map(dpld); + /* Initialize DPRAM map (physical map -> logical map) */ + ret = dpram_table_init(dpld); if (ret < 0) { - mif_err("%s: ERR! dpram_init_ipc_map fail (err %d)\n", + mif_info("%s: ERR! dpram_table_init fail (err %d)\n", ld->name, ret); goto err; } - if (dpram->res_ack_wait_timeout > 0) - dpld->res_ack_wait_timeout = dpram->res_ack_wait_timeout; - else - dpld->res_ack_wait_timeout = RES_ACK_WAIT_TIMEOUT; - /* Disable IPC */ - if (!dpram->disabled) { - set_magic(dpld, 0); - set_access(dpld, 0); - } - dpld->init_status = DPRAM_INIT_STATE_NONE; + set_magic(dpld, 0); + set_access(dpld, 0); + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - /* - ** Initialize locks, completions, and bottom halves - */ - snprintf(dpld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); + /* Initialize locks, completions, and bottom halves */ + snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); - init_completion(&dpld->udl_cmpl); - init_completion(&dpld->crash_cmpl); - - for (i = 0; i < ld->max_ipc_dev; i++) - init_completion(&dpld->req_ack_cmpl[i]); - - INIT_DELAYED_WORK(&dpld->rx_dwork, ipc_rx_work); - - for (i = 0; i < ld->max_ipc_dev; i++) { - spin_lock_init(&dpld->tx_lock[i]); - atomic_set(&dpld->res_required[i], 0); - } - - ld->tx_wq = create_singlethread_workqueue("dpram_tx_wq"); - if (!ld->tx_wq) { - mif_err("%s: ERR! fail to create tx_wq\n", ld->name); - goto err; + init_completion(&dpld->dpram_init_cmd); + init_completion(&dpld->modem_pif_init_done); + init_completion(&dpld->udl_start_complete); + init_completion(&dpld->udl_cmd_complete); + init_completion(&dpld->dump_start_complete); + init_completion(&dpld->dump_recv_done); + + task_data = (unsigned long)dpld; + tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); + + /* Prepare SKB queue head for RX processing */ + for (i = 0; i < dpld->max_ipc_dev; i++) + skb_queue_head_init(&dpld->skb_rxq[i]); + + /* Prepare RXB queue */ + qsize = DPRAM_MAX_RXBQ_SIZE; + for (i = 0; i < dpld->max_ipc_dev; i++) { + bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); + dpld->rxbq[i].size = qsize; + dpld->rxbq[i].in = 0; + dpld->rxbq[i].out = 0; + dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); + if (!dpld->rxbq[i].rxb) { + mif_info("%s: ERR! %s rxbq_create_pool fail\n", + ld->name, get_dev_name(i)); + goto err; + } + mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n", + ld->name, get_dev_name(i), bsize, qsize); } - INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); - INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); - INIT_DELAYED_WORK(&ld->rfs_tx_dwork, rfs_tx_work); - ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; - ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; - ld->tx_dwork[IPC_RFS] = &ld->rfs_tx_dwork; - -#ifdef DEBUG_MODEM_IF - spin_lock_init(&dpld->stat_list.lock); - spin_lock_init(&dpld->trace_list.lock); -#endif /* Prepare a multi-purpose miscellaneous buffer */ - dpld->buff = kzalloc(dpld->size, GFP_KERNEL); + dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); if (!dpld->buff) { - mif_err("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); goto err; } - /* - ** Retrieve DPRAM IRQ GPIO#, IRQ#, and IRQ flags - */ - dpld->gpio_int2ap = modem->gpio_ipc_int2ap; - dpld->gpio_cp_status = modem->gpio_cp_status; - dpld->gpio_cp_wakeup = modem->gpio_cp_wakeup; - if (dpram->type == AP_IDPRAM) { - if (!modem->gpio_ipc_int2cp) { - mif_err("%s: ERR! no gpio_ipc_int2cp\n", ld->name); - goto err; - } - dpld->gpio_int2cp = modem->gpio_ipc_int2cp; - } - - dpld->irq = modem->irq_ipc_int2ap; + /* Retrieve DPRAM IRQ GPIO# */ + dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; - if (modem->irqf_ipc_int2ap) - dpld->irq_flags = modem->irqf_ipc_int2ap; - else - dpld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); - - /* - ** Initialize power management (PM) for AP_IDPRAM - */ - if (dpld->type == AP_IDPRAM) { - dpld->pm_op = idpram_get_pm_op(dpld->ap); - if (!dpld->pm_op) { - mif_err("%s: no pm_op for AP_IDPRAM\n", ld->name); - goto err; - } - - ret = dpld->pm_op->pm_init(dpld, modem, pm_tx_work); - if (ret) { - mif_err("%s: pm_init fail (err %d)\n", ld->name, ret); + /* Retrieve DPRAM IRQ# */ + if (!dpctl->dpram_irq) { + dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); + if (dpctl->dpram_irq < 0) { + mif_info("%s: ERR! platform_get_irq_byname fail\n", + ld->name); goto err; } } + dpld->irq = dpctl->dpram_irq; + + /* Retrieve DPRAM IRQ flags */ + if (!dpctl->dpram_irq_flags) + dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + dpld->irq_flags = dpctl->dpram_irq_flags; - /* - ** Register DPRAM interrupt handler - */ - snprintf(dpld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); + /* Register DPRAM interrupt handler */ + snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); if (dpld->ext_op && dpld->ext_op->irq_handler) dpld->irq_handler = dpld->ext_op->irq_handler; - else if (dpld->type == CP_IDPRAM) + else if (dpld->dp_type == CP_IDPRAM) dpld->irq_handler = cp_idpram_irq_handler; - else if (dpld->type == AP_IDPRAM) + else if (dpld->dp_type == AP_IDPRAM) dpld->irq_handler = ap_idpram_irq_handler; else dpld->irq_handler = ext_dpram_irq_handler; - ret = mif_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, + ret = dpram_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, dpld->irq_name, dpld); if (ret) goto err; - - return ld; + else + return ld; err: if (dpld) { - kfree(dpld->buff); + if (dpld->buff) + kfree(dpld->buff); kfree(dpld); } diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h index d9ef251..c651e51 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.h +++ b/drivers/misc/modem_if/modem_link_device_dpram.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -11,11 +12,227 @@ * GNU General Public License for more details. * */ - #ifndef __MODEM_LINK_DEVICE_DPRAM_H__ #define __MODEM_LINK_DEVICE_DPRAM_H__ -#include "modem_link_device_memory.h" +#include +#include +#include +#include +#include + +#include "modem_prj.h" + +#define DPRAM_MAGIC_CODE 0xAA + +/* interrupt masks.*/ +#define INT_MASK_VALID 0x0080 +#define INT_MASK_CMD 0x0040 +#define INT_VALID(x) ((x) & INT_MASK_VALID) +#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) +#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) +#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) + +#define EXT_UDL_MASK 0xF000 +#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) +#define EXT_INT_VALID_MASK 0x8000 +#define EXT_CMD_VALID_MASK 0x4000 +#define UDL_CMD_VALID_MASK 0x2000 +#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) +#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) +#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) +#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) + +#define EXT_CMD_MASK(x) ((x) & 0x0FFF) +#define EXT_CMD_SET_SPEED_LOW 0x0011 +#define EXT_CMD_SET_SPEED_MID 0x0012 +#define EXT_CMD_SET_SPEED_HIGH 0x0013 + +#define UDL_RESULT_SUCCESS 0x1 +#define UDL_RESULT_FAIL 0x2 + +#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) +#define UDL_CMD_RECV_READY 0x1 +#define UDL_CMD_DL_START_REQ 0x2 +#define UDL_CMD_DL_START_RESP 0x3 +#define UDL_CMD_IMAGE_SEND_REQ 0x4 +#define UDL_CMD_SEND_DONE_RESP 0x5 +#define UDL_CMD_SEND_DONE_REQ 0x6 +#define UDL_CMD_UPDATE_DONE 0x7 +#define UDL_CMD_STATUS_UPDATE 0x8 +#define UDL_CMD_IMAGE_SEND_RESP 0x9 +#define UDL_CMD_EFS_CLEAR_RESP 0xB +#define UDL_CMD_ALARM_BOOT_OK 0xC +#define UDL_CMD_ALARM_BOOT_FAIL 0xD + +#define CMD_IMG_START_REQ 0x9200 +#define CMD_IMG_SEND_REQ 0x9400 +#define CMD_DL_SEND_DONE_REQ 0x9600 +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_REQ_ACK_F 0x0020 +#define INT_MASK_REQ_ACK_R 0x0010 +#define INT_MASK_RES_ACK_F 0x0008 +#define INT_MASK_RES_ACK_R 0x0004 +#define INT_MASK_SEND_F 0x0002 +#define INT_MASK_SEND_R 0x0001 + +#define INT_MASK_RES_ACK_SET \ + (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) + +#define INT_MASK_SEND_SET \ + (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) + +#define INT_CMD_MASK(x) ((x) & 0xF) +#define INT_CMD_INIT_START 0x1 +#define INT_CMD_INIT_END 0x2 +#define INT_CMD_REQ_ACTIVE 0x3 +#define INT_CMD_RES_ACTIVE 0x4 +#define INT_CMD_REQ_TIME_SYNC 0x5 +#define INT_CMD_CRASH_RESET 0x7 +#define INT_CMD_PHONE_START 0x8 +#define INT_CMD_ERR_DISPLAY 0x9 +#define INT_CMD_CRASH_EXIT 0x9 +#define INT_CMD_CP_DEEP_SLEEP 0xA +#define INT_CMD_NV_REBUILDING 0xB +#define INT_CMD_EMER_DOWN 0xC +#define INT_CMD_PIF_INIT_DONE 0xD +#define INT_CMD_SILENT_NV_REBUILDING 0xE +#define INT_CMD_NORMAL_PWR_OFF 0xF + +#define START_FLAG 0x7F +#define END_FLAG 0x7E + +#define DP_MAGIC_DMDL 0x4445444C +#define DP_MAGIC_UMDL 0x4445444D +#define DP_DPRAM_SIZE 0x4000 +#define DP_DEFAULT_WRITE_LEN 8168 +#define DP_DEFAULT_DUMP_LEN 16128 +#define DP_DUMP_HEADER_SIZE 7 + +#define UDL_TIMEOUT (50 * HZ) +#define UDL_SEND_TIMEOUT (200 * HZ) +#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) +#define DUMP_TIMEOUT (30 * HZ) +#define DUMP_START_TIMEOUT (100 * HZ) +#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ + +enum host_boot_mode { + HOST_BOOT_MODE_NORMAL, + HOST_BOOT_MODE_DUMP, +}; + +enum dpram_init_status { + DPRAM_INIT_STATE_NONE, + DPRAM_INIT_STATE_READY, +}; + +struct dpram_boot_img { + char *addr; + int size; + enum host_boot_mode mode; + unsigned req; + unsigned resp; +}; + +#define MAX_PAYLOAD_SIZE 0x2000 +struct dpram_boot_frame { + unsigned req; /* AP->CP request */ + unsigned resp; /* response expected by AP */ + ssize_t len; /* data size in the buffer */ + unsigned offset; /* offset to write into DPRAM */ + char data[MAX_PAYLOAD_SIZE]; +}; + +/* buffer type for modem image */ +struct dpram_dump_arg { + char *buff; /* pointer to the buffer */ + int buff_size; /* buffer size */ + unsigned req; /* AP->CP request */ + unsigned resp; /* CP->AP response */ + bool cmd; /* AP->CP command */ +}; + +struct dpram_boot_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 __iomem *req; + u32 __iomem *resp; + u32 size; +}; + +struct qc_dpram_boot_map { + u8 __iomem *buff; + u16 __iomem *frame_size; + u16 __iomem *tag; + u16 __iomem *count; +}; + +struct dpram_dload_map { + u32 __iomem *magic; + u8 __iomem *buff; +}; + +struct dpram_uload_map { + u32 __iomem *magic; + u8 __iomem *buff; +}; + +struct ul_header { + u8 bop; + u16 total_frame; + u16 curr_frame; + u16 len; +} __packed; + +struct dpram_udl_param { + unsigned char *addr; + unsigned int size; + unsigned int count; + unsigned int tag; +}; + +struct dpram_udl_check { + unsigned int total_size; + unsigned int rest_size; + unsigned int send_size; + unsigned int copy_start; + unsigned int copy_complete; + unsigned int boot_complete; +}; + +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 + +#define MAX_WQ_NAME_LENGTH 64 + +#define DPRAM_MAX_RXBQ_SIZE 256 + +struct dpram_rxb { + u8 *buff; + unsigned size; + + u8 *data; + unsigned len; +}; + +struct dpram_rxb_queue { + int size; + int in; + int out; + struct dpram_rxb *rxb; +}; /* magic_code + @@ -65,110 +282,38 @@ struct dpram_ipc_16k_map { u16 mbx_ap2cp; }; -enum dpram_init_status { - DPRAM_INIT_STATE_NONE, - DPRAM_INIT_STATE_READY, -}; - -struct dpram_boot_frame { - unsigned req; /* AP->CP request */ - unsigned resp; /* response expected by AP */ - ssize_t len; /* data size in the buffer */ - unsigned offset; /* offset to write into DPRAM */ - char data[DP_MAX_PAYLOAD_SIZE]; -}; - -struct dpram_dump_arg { - char *buff; /* pointer to the buffer */ - int buff_size; /* buffer size */ - unsigned req; /* AP->CP request */ - unsigned resp; /* CP->AP response */ - int cmd; /* AP->CP command */ -}; - -/* DPRAM upload/download header */ -struct dpram_udl_header { - u8 bop; - u16 num_frames; - u16 curr_frame; - u16 len; -#ifdef CONFIG_CDMA_MODEM_CBP82 - u8 pad; -#endif -} __packed; - -#define MAX_DUMP_SKB_SIZE 4096 - -enum idpram_link_pm_states { - IDPRAM_PM_SUSPEND_PREPARE, - IDPRAM_PM_DPRAM_POWER_DOWN, - IDPRAM_PM_SUSPEND_START, - IDPRAM_PM_RESUME_START, - IDPRAM_PM_ACTIVE, -}; - -struct idpram_pm_data { - atomic_t pm_lock; - - enum idpram_link_pm_states pm_state; - - struct completion down_cmpl; - - struct wake_lock ap_wlock; - struct wake_lock hold_wlock; +#define DP_MAX_NAME_LEN 32 - struct delayed_work tx_dwork; - struct delayed_work resume_dwork; - - struct notifier_block pm_noti; - - unsigned resume_try_cnt; - - /* the last value in the mbx_cp2ap */ - unsigned last_msg; -}; - -struct dpram_link_device; struct dpram_ext_op; -struct idpram_pm_op; struct dpram_link_device { struct link_device ld; - enum dpram_type type; /* DPRAM type */ - enum ap_type ap; /* AP type for AP_IDPRAM */ - - /* Stirct I/O access (e.g. ioread16(), etc.) is required */ - bool strict_io_access; - /* DPRAM address and size */ - u8 __iomem *base; /* Virtual address of DPRAM */ - u32 size; /* DPRAM size */ - - /* DPRAM SFR */ - u8 __iomem *sfr_base; /* Virtual address of SFR */ - - /* Whether or not this DPRAM can go asleep */ - bool need_wake_up; + u8 __iomem *dp_base; /* DPRAM base virtual address */ + u32 dp_size; /* DPRAM size */ + enum dpram_type dp_type; /* DPRAM type */ /* DPRAM IRQ GPIO# */ - unsigned gpio_int2ap; - unsigned gpio_cp_status; - unsigned gpio_cp_wakeup; - unsigned gpio_int2cp; + unsigned gpio_dpram_int; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[MIF_MAX_NAME_LEN]; + char irq_name[DP_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - struct modemlink_dpram_data *dpram; + int max_ipc_dev; + struct modemlink_dpram_control *dpctl; /* Physical configuration -> logical configuration */ - struct memif_boot_map bt_map; - struct memif_dload_map dl_map; - struct memif_uload_map ul_map; + union { + struct dpram_boot_map bt_map; + struct qc_dpram_boot_map qc_bt_map; + }; + + struct dpram_dload_map dl_map; + struct dpram_uload_map ul_map; /* IPC device map */ struct dpram_ipc_map ipc_map; @@ -182,40 +327,40 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[MIF_MAX_NAME_LEN]; + char wlock_name[DP_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; + struct completion dpram_init_cmd; + struct completion modem_pif_init_done; /* For UDL */ + struct tasklet_struct ul_tsk; struct tasklet_struct dl_tsk; - struct completion udl_cmpl; + struct completion udl_start_complete; + struct completion udl_cmd_complete; + struct dpram_udl_check udl_check; + struct dpram_udl_param udl_param; - /* - ** For CP crash dump - */ - bool forced_cp_crash; + /* For CP RAM dump */ struct timer_list crash_ack_timer; - struct timer_list crash_timer; - struct completion crash_cmpl; - /* If this field is wanted to be used, it must be initialized only in - * the "ld->dump_start" method. - */ - struct delayed_work crash_dwork; - /* Count of CP crash dump packets received */ - int crash_rcvd; + struct completion dump_start_complete; + struct completion dump_recv_done; + struct timer_list dump_timer; + int dump_rcvd; /* Count of dump packets received */ /* For locking TX process */ spinlock_t tx_lock[MAX_IPC_DEV]; - /* For retransmission under DPRAM flow control after TXQ full state */ - unsigned long res_ack_wait_timeout; - atomic_t res_required[MAX_IPC_DEV]; - struct completion req_ack_cmpl[MAX_IPC_DEV]; - /* For efficient RX process */ - struct delayed_work rx_dwork; + struct tasklet_struct rx_tsk; + struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; struct io_device *iod[MAX_IPC_DEV]; + bool use_skb; + struct sk_buff_head skb_rxq[MAX_IPC_DEV]; + + /* For retransmission after buffer full state */ + atomic_t res_required[MAX_IPC_DEV]; /* For wake-up/sleep control */ atomic_t accessing; @@ -224,53 +369,45 @@ struct dpram_link_device { u8 *buff; /* DPRAM IPC initialization status */ - int init_status; + int dpram_init_status; /* Alias to device-specific IOCTL function */ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg); /* Alias to DPRAM IRQ handler */ - irq_handler_t irq_handler; + irqreturn_t (*irq_handler)(int irq, void *data); - /* For DPRAM logging */ - struct mem_status_queue stat_list; - struct trace_data_queue trace_list; + /* For DPRAM dump */ + void (*dpram_dump)(struct link_device *ld, char *buff); /* Common operations for each DPRAM */ + void (*clear_intr)(struct dpram_link_device *dpld); u16 (*recv_intr)(struct dpram_link_device *dpld); void (*send_intr)(struct dpram_link_device *dpld, u16 mask); u16 (*get_magic)(struct dpram_link_device *dpld); void (*set_magic)(struct dpram_link_device *dpld, u16 value); u16 (*get_access)(struct dpram_link_device *dpld); void (*set_access)(struct dpram_link_device *dpld, u16 value); - u32 (*get_txq_head)(struct dpram_link_device *dpld, int id); - u32 (*get_txq_tail)(struct dpram_link_device *dpld, int id); - void (*set_txq_head)(struct dpram_link_device *dpld, int id, u32 in); - void (*set_txq_tail)(struct dpram_link_device *dpld, int id, u32 out); - u8 *(*get_txq_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_txq_buff_size)(struct dpram_link_device *dpld, int id); - u32 (*get_rxq_head)(struct dpram_link_device *dpld, int id); - u32 (*get_rxq_tail)(struct dpram_link_device *dpld, int id); - void (*set_rxq_head)(struct dpram_link_device *dpld, int id, u32 in); - void (*set_rxq_tail)(struct dpram_link_device *dpld, int id, u32 out); - u8 *(*get_rxq_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_rxq_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); + void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); + void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); - void (*get_dpram_status)(struct dpram_link_device *dpld, - enum circ_dir_type, struct mem_status *stat); - void (*ipc_rx_handler)(struct dpram_link_device *dpld, - struct mem_status *stat); - void (*reset_dpram_ipc)(struct dpram_link_device *dpld); + void (*ipc_rx_handler)(struct dpram_link_device *dpld, u16 int2ap); /* Extended operations for various modems */ struct dpram_ext_op *ext_op; - - /* Power management (PM) for AP_IDPRAM */ - struct idpram_pm_data pm_data; - struct idpram_pm_op *pm_op; }; /* converts from struct link_device* to struct xxx_link_device* */ @@ -278,65 +415,27 @@ struct dpram_link_device { container_of(linkdev, struct dpram_link_device, ld) struct dpram_ext_op { - /* flag for checking whether or not a dpram_ext_op instance exists */ int exist; - /* methods for setting up DPRAM maps */ void (*init_boot_map)(struct dpram_link_device *dpld); void (*init_dl_map)(struct dpram_link_device *dpld); void (*init_ul_map)(struct dpram_link_device *dpld); - void (*init_ipc_map)(struct dpram_link_device *dpld); - /* methods for CP booting */ - int (*xmit_boot)(struct dpram_link_device *dpld, unsigned long arg); - int (*xmit_binary)(struct dpram_link_device *dpld, struct sk_buff *skb); + int (*download_binary)(struct dpram_link_device *dpld, + struct sk_buff *skb); - /* methods for DPRAM command handling */ - void (*cmd_handler)(struct dpram_link_device *dpld, u16 cmd); void (*cp_start_handler)(struct dpram_link_device *dpld); - /* method for CP firmware upgrade */ - int (*firm_update)(struct dpram_link_device *dpld, unsigned long arg); - - /* methods for CP crash dump */ void (*crash_log)(struct dpram_link_device *dpld); int (*dump_start)(struct dpram_link_device *dpld); - int (*dump_update)(struct dpram_link_device *dpld, unsigned long arg); - int (*dump_finish)(struct dpram_link_device *dpld, unsigned long arg); + int (*dump_update)(struct dpram_link_device *dpld, void *arg); - /* IOCTL extension */ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); - /* methods for interrupt handling */ - irq_handler_t irq_handler; - void (*clear_int2ap)(struct dpram_link_device *dpld); - - /* methods for power management */ - int (*wakeup)(struct dpram_link_device *dpld); - void (*sleep)(struct dpram_link_device *dpld); + irqreturn_t (*irq_handler)(int irq, void *data); }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); -struct idpram_pm_op { - /* flag for checking whether or not a idpram_pm_op instance exists */ - int exist; - int (*pm_init)(struct dpram_link_device *dpld, struct modem_data *modem, - void (*pm_tx_func)(struct work_struct *work)); - void (*power_down)(struct dpram_link_device *dpld); - void (*power_up)(struct dpram_link_device *dpld); - void (*halt_suspend)(struct dpram_link_device *dpld); - bool (*locked)(struct dpram_link_device *dpld); - bool (*int2cp_possible)(struct dpram_link_device *dpld); -}; - -struct idpram_pm_op *idpram_get_pm_op(enum ap_type id); - -#if 1 -#endif - -extern void set_sromc_access(bool access); - #endif - diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c index b03dce5..1475a46 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c @@ -25,82 +25,103 @@ #include #include #include -#include -#include -#include - #include + #include "modem_prj.h" -#include "modem_utils.h" #include "modem_link_device_dpram.h" +#include "modem_utils.h" + +#if defined(CONFIG_LTE_MODEM_CMC221) +/* +** For host (flashless) booting via DPRAM +*/ +#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 +#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 +#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A +#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD + +#define CMC22x_HOST_DOWN_START 0x1234 +#define CMC22x_HOST_DOWN_END 0x4321 +#define CMC22x_REG_NV_DOWN_END 0xABCD +#define CMC22x_CAL_NV_DOWN_END 0xDCBA + +#define CMC22x_1ST_BUFF_READY 0xAAAA +#define CMC22x_2ND_BUFF_READY 0xBBBB +#define CMC22x_1ST_BUFF_FULL 0x1111 +#define CMC22x_2ND_BUFF_FULL 0x2222 + +#define CMC22x_CP_RECV_NV_END 0x8888 +#define CMC22x_CP_CAL_OK 0x4F4B +#define CMC22x_CP_CAL_BAD 0x4552 +#define CMC22x_CP_DUMP_END 0xFADE -#if defined(CONFIG_CDMA_MODEM_CBP72) || defined(CONFIG_CDMA_MODEM_CBP82) -static void cbp_init_boot_map(struct dpram_link_device *dpld) +#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */ +#endif + +#if defined(CONFIG_CDMA_MODEM_CBP72) +static void cbp72_init_boot_map(struct dpram_link_device *dpld) { - struct memif_boot_map *bt_map = &dpld->bt_map; + struct dpram_boot_map *bt_map = &dpld->bt_map; - bt_map->magic = (u32 *)dpld->base; - bt_map->buff = (u8 *)(dpld->base + DP_BOOT_BUFF_OFFSET); - bt_map->space = dpld->size - 4; + bt_map->magic = (u32 *)dpld->dp_base; + bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET); + bt_map->size = dpld->dp_size - 4; } -static void cbp_init_dl_map(struct dpram_link_device *dpld) +static void cbp72_init_dl_map(struct dpram_link_device *dpld) { - dpld->dl_map.magic = (u32 *)dpld->base; - dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.magic = (u32 *)dpld->dp_base; + dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET); } -static int cbp_udl_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp) { + struct link_device *ld = &dpld->ld; int ret; - int int2ap; + int int2cp; - mif_debug("wait for 0x%04X\n", resp); - ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); + ret = wait_for_completion_interruptible_timeout( + &dpld->udl_cmd_complete, UDL_TIMEOUT); if (!ret) { - mif_info("ERR! No UDL_CMD_RESP!!!\n"); - return -EIO; + mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name); + return -ENXIO; } - int2ap = dpld->recv_intr(dpld); - if (resp == int2ap || int2ap == CMD_UL_RECV_DONE_REQ) { - mif_debug("int2ap = 0x%04X\n", int2ap); - return int2ap; - } else { - mif_err("ERR! int2ap 0x%04X != resp 0x%04X\n", int2ap, resp); + int2cp = dpld->recv_intr(dpld); + mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp); + if (resp == int2cp || int2cp == 0xA700) + return int2cp; + else return -EINVAL; - } } -static int cbp_xmit_binary(struct dpram_link_device *dpld, +static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld, struct sk_buff *skb) { + struct link_device *ld = &dpld->ld; struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; u8 __iomem *buff = dpld->bt_map.buff; int err = 0; - if (bf->len > dpld->bt_map.space) { - mif_info("ERR! Out of DPRAM boundary\n"); - err = -ERANGE; + if (bf->len > dpld->bt_map.size) { + mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); + err = -EINVAL; goto exit; } if (bf->len) - memcpy16_to_io(buff, bf->data, bf->len); + memcpy(buff, bf->data, bf->len); -#ifdef CONFIG_CDMA_MODEM_CBP72 - init_completion(&dpld->udl_cmpl); -#endif + init_completion(&dpld->udl_cmd_complete); - if (bf->req) { + if (bf->req) dpld->send_intr(dpld, (u16)bf->req); - mif_debug("send intr 0x%04X\n", (u16)bf->req); - } if (bf->resp) { - err = cbp_udl_wait_resp(dpld, bf->resp); + err = _cbp72_edpram_wait_resp(dpld, bf->resp); if (err < 0) { - mif_err("ERR! cbp_udl_wait_resp fail (err %d)\n", err); + mif_info("%s: ERR! wait_response fail (%d)\n", + ld->name, err); goto exit; } else if (err == bf->resp) { err = skb->len; @@ -112,14 +133,24 @@ exit: return err; } -static int cbp_dump_start(struct dpram_link_device *dpld) +static int cbp72_download_binary(struct dpram_link_device *dpld, + struct sk_buff *skb) { + if (dpld->dp_type == EXT_DPRAM) + return _cbp72_edpram_download_bin(dpld, skb); + else + return -ENODEV; +} + +static int cbp72_dump_start(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; u8 *dest = dpld->ul_map.buff; int ret; - dpld->ld.mode = LINK_MODE_ULOAD; + ld->mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->crash_timer); + ret = del_timer(&dpld->dump_timer); wake_lock(&dpld->wlock); iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic); @@ -130,65 +161,53 @@ static int cbp_dump_start(struct dpram_link_device *dpld) iowrite8((u8)0x0, dest + 3); iowrite8((u8)END_FLAG, dest + 4); - init_completion(&dpld->crash_cmpl); + init_completion(&dpld->dump_start_complete); return 0; } -static int cbp_dump_update(struct dpram_link_device *dpld, unsigned long arg) +static int _cbp72_edpram_upload(struct dpram_link_device *dpld, + struct dpram_dump_arg *dump, unsigned char __user *target) { - struct dpram_dump_arg dump; - struct dpram_udl_header header; - unsigned char __user *target = (unsigned char __user *)arg; - int err = 0; - int resp = 0; + struct link_device *ld = &dpld->ld; + struct ul_header header; u8 *dest = NULL; u8 *buff = NULL; - u8 *header_buff = NULL; - int buff_size = 0; u16 plen = 0; + int err = 0; + int ret = 0; + int buff_size = 0; - err = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); - if (err < 0) { - mif_err("ERR! ARG copy_from_user fail (err %d)\n", err); - goto exit; - } - mif_debug("req %x, resp %x", dump.req, dump.resp); + mif_debug("\n"); + + init_completion(&dpld->udl_cmd_complete); - if (dump.req) - dpld->send_intr(dpld, (u16)dump.req); + mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp); - if (dump.resp) { - resp = err = cbp_udl_wait_resp(dpld, dump.resp); + if (dump->req) + dpld->send_intr(dpld, (u16)dump->req); + + if (dump->resp) { + err = _cbp72_edpram_wait_resp(dpld, dump->resp); if (err < 0) { - mif_info("ERR! wait_response fail (err %d)\n", err); + mif_info("%s: ERR! wait_response fail (%d)\n", + ld->name, err); goto exit; } } - if (dump.cmd) - goto exit; + if (dump->cmd) + return err; dest = (u8 *)dpld->ul_map.buff; - header_buff = vmalloc(sizeof(struct dpram_udl_header)); - if (!header_buff) { - err = -ENOMEM; - goto exit; - } - - memcpy16_from_io(header_buff, dest, sizeof(struct dpram_udl_header)); - - header.bop = *(u8 *)(header_buff); - header.num_frames = *(u16 *)(header_buff + 1); - header.curr_frame = *(u16 *)(header_buff + 3); - header.len = *(u16 *)(header_buff + 5); -#ifdef CONFIG_CDMA_MODEM_CBP82 - header.pad = *(u8 *)(header_buff + 7); -#endif + header.bop = *(u8 *)(dest); + header.total_frame = *(u16 *)(dest + 1); + header.curr_frame = *(u16 *)(dest + 3); + header.len = *(u16 *)(dest + 5); - mif_debug("total frames:%d, current frame:%d, data len:%d\n", - header.num_frames, header.curr_frame, header.len); + mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n", + ld->name, header.total_frame, header.curr_frame, header.len); plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN); @@ -198,26 +217,22 @@ static int cbp_dump_update(struct dpram_link_device *dpld, unsigned long arg) goto exit; } - memcpy16_from_io(buff, dest + sizeof(struct dpram_udl_header), plen); - err = copy_to_user(dump.buff, buff, plen); - if (err) { - mif_info("ERR! DUMP copy_to_user fail\n"); + memcpy(buff, dest + sizeof(struct ul_header), plen); + ret = copy_to_user(dump->buff, buff, plen); + if (ret < 0) { + mif_info("%s: ERR! dump copy_to_user fail\n", ld->name); err = -EIO; goto exit; } - buff_size = plen; - err = copy_to_user(target + 4, &buff_size, sizeof(int)); - if (err) { - mif_info("ERR! SIZE copy_to_user fail\n"); + + ret = copy_to_user(target + 4, &buff_size, sizeof(int)); + if (ret < 0) { + mif_info("%s: ERR! size copy_to_user fail\n", ld->name); err = -EIO; goto exit; } - /* Return response value */ - if (err == 0) - err = resp; - vfree(buff); return err; @@ -228,169 +243,82 @@ exit: wake_unlock(&dpld->wlock); return err; } -#endif - -#if defined(CONFIG_LTE_MODEM_CMC221) -/* -** For CMC221 SFR for IDPRAM -*/ -#define CMC_INT2CP_REG 0x10 /* Interrupt to CP */ -#define CMC_INT2AP_REG 0x50 -#define CMC_CLR_INT_REG 0x28 /* Clear Interrupt to AP */ -#define CMC_RESET_REG 0x3C -#define CMC_PUT_REG 0x40 /* AP->CP reg for hostbooting */ -#define CMC_GET_REG 0x50 /* CP->AP reg for hostbooting */ - -/* -** For host (flashless) booting via DPRAM -*/ -#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 -#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 -#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A -#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD -#define CMC22x_HOST_DOWN_START 0x1234 -#define CMC22x_HOST_DOWN_END 0x4321 -#define CMC22x_REG_NV_DOWN_END 0xABCD -#define CMC22x_CAL_NV_DOWN_END 0xDCBA +static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg) +{ + struct link_device *ld = &dpld->ld; + struct dpram_dump_arg dump; + int ret; -#define CMC22x_1ST_BUFF_READY 0xAAAA -#define CMC22x_2ND_BUFF_READY 0xBBBB -#define CMC22x_1ST_BUFF_FULL 0x1111 -#define CMC22x_2ND_BUFF_FULL 0x2222 + ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); + if (ret < 0) { + mif_info("%s: ERR! copy_from_user fail\n", ld->name); + return ret; + } -#define CMC22x_CP_RECV_NV_END 0x8888 -#define CMC22x_CP_CAL_OK 0x4F4B -#define CMC22x_CP_CAL_BAD 0x4552 -#define CMC22x_CP_DUMP_END 0xFADE + return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg); +} -#define CMC22x_DUMP_BUFF_SIZE 8192 +static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); -/* CMC221 IDPRAM SFR */ -struct cmc221_idpram_sfr { - u16 __iomem *int2cp; - u16 __iomem *int2ap; - u16 __iomem *clr_int2ap; - u16 __iomem *reset; - u16 __iomem *msg2cp; - u16 __iomem *msg2ap; -}; + ld->mode = LINK_MODE_DLOAD; -struct cmc221_boot_img { - char *addr; - int size; - enum cp_boot_mode mode; - unsigned req; - unsigned resp; -}; + iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); -static struct cmc221_idpram_sfr cmc_sfr; + return 0; +} -static void cmc221_init_boot_map(struct dpram_link_device *dpld) +static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg) { - struct memif_boot_map *bt_map = &dpld->bt_map; + struct link_device *ld = &dpld->ld; + int err = 0; - bt_map->buff = dpld->base; - bt_map->space = dpld->size; - bt_map->req = (u32 *)(dpld->base + DP_BOOT_REQ_OFFSET); - bt_map->resp = (u32 *)(dpld->base + DP_BOOT_RESP_OFFSET); -} + switch (cmd) { + case IOCTL_MODEM_DL_START: + err = cbp72_set_dl_magic(ld, iod); + if (err < 0) + mif_err("%s: ERR! set_dl_magic fail\n", ld->name); + break; -static void cmc221_init_dl_map(struct dpram_link_device *dpld) -{ - dpld->dl_map.magic = (u32 *)dpld->base; - dpld->dl_map.buff = (u8 *)dpld->base; -} + default: + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + break; + } -static void cmc221_init_ul_map(struct dpram_link_device *dpld) -{ - dpld->ul_map.magic = (u32 *)dpld->base; - dpld->ul_map.buff = (u8 *)dpld->base; + return err; } +#endif -static void cmc221_init_ipc_map(struct dpram_link_device *dpld) +#if defined(CONFIG_LTE_MODEM_CMC221) +static void cmc221_init_boot_map(struct dpram_link_device *dpld) { - struct dpram_ipc_16k_map *dpram_map; - struct dpram_ipc_device *dev; - u8 __iomem *sfr_base = dpld->sfr_base; - - dpram_map = (struct dpram_ipc_16k_map *)dpld->base; - - /* Magic code and access enable fields */ - dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; - dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; - - /* FMT */ - dev = &dpld->ipc_map.dev[IPC_FMT]; - - strcpy(dev->name, "FMT"); - dev->id = IPC_FMT; - - dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head; - dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail; - dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0]; - dev->txq.size = DP_16K_FMT_TX_BUFF_SZ; - - dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head; - dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail; - dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0]; - dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ; - - dev->mask_req_ack = INT_MASK_REQ_ACK_F; - dev->mask_res_ack = INT_MASK_RES_ACK_F; - dev->mask_send = INT_MASK_SEND_F; - - /* RAW */ - dev = &dpld->ipc_map.dev[IPC_RAW]; - - strcpy(dev->name, "RAW"); - dev->id = IPC_RAW; - - dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head; - dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail; - dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0]; - dev->txq.size = DP_16K_RAW_TX_BUFF_SZ; - - dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head; - dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail; - dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0]; - dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ; - - dev->mask_req_ack = INT_MASK_REQ_ACK_R; - dev->mask_res_ack = INT_MASK_RES_ACK_R; - dev->mask_send = INT_MASK_SEND_R; - - /* SFR */ - cmc_sfr.int2cp = (u16 __iomem *)(sfr_base + CMC_INT2CP_REG); - cmc_sfr.int2ap = (u16 __iomem *)(sfr_base + CMC_INT2AP_REG); - cmc_sfr.clr_int2ap = (u16 __iomem *)(sfr_base + CMC_CLR_INT_REG); - cmc_sfr.reset = (u16 __iomem *)(sfr_base + CMC_RESET_REG); - cmc_sfr.msg2cp = (u16 __iomem *)(sfr_base + CMC_PUT_REG); - cmc_sfr.msg2ap = (u16 __iomem *)(sfr_base + CMC_GET_REG); - - /* Interrupt ports */ - dpld->ipc_map.mbx_cp2ap = cmc_sfr.int2ap; - dpld->ipc_map.mbx_ap2cp = cmc_sfr.int2cp; -} + struct dpram_boot_map *bt_map = &dpld->bt_map; -static inline void cmc221_idpram_reset(struct dpram_link_device *dpld) -{ - iowrite16(1, cmc_sfr.reset); + bt_map->buff = dpld->dp_base; + bt_map->size = dpld->dp_size; + bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET); + bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET); } -static inline u16 cmc221_idpram_recv_msg(struct dpram_link_device *dpld) +static void cmc221_init_dl_map(struct dpram_link_device *dpld) { - return ioread16(cmc_sfr.msg2ap); + dpld->dl_map.magic = (u32 *)dpld->dp_base; + dpld->dl_map.buff = (u8 *)dpld->dp_base; } -static inline void cmc221_idpram_send_msg(struct dpram_link_device *dpld, - u16 msg) +static void cmc221_init_ul_map(struct dpram_link_device *dpld) { - iowrite16(msg, cmc_sfr.msg2cp); + dpld->ul_map.magic = (u32 *)dpld->dp_base; + dpld->ul_map.buff = (u8 *)dpld->dp_base; } -static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) { + struct link_device *ld = &dpld->ld; int count = 50000; u32 rcvd = 0; @@ -400,14 +328,16 @@ static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) if (rcvd == resp) break; - rcvd = cmc221_idpram_recv_msg(dpld); + rcvd = dpld->dpctl->recv_msg(); if (rcvd == 0x9999) { - mif_info("invalid resp 0x%04X\n", rcvd); + mif_info("%s: Invalid resp 0x%04X\n", + ld->name, rcvd); panic("CP Crash ... BAD CRC in CP"); } if (count-- < 0) { - mif_info("invalid resp 0x%08X\n", rcvd); + mif_info("%s: Invalid resp 0x%08X\n", + ld->name, rcvd); return -EAGAIN; } @@ -415,19 +345,20 @@ static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) } } else { while (1) { - rcvd = cmc221_idpram_recv_msg(dpld); + rcvd = dpld->dpctl->recv_msg(); if (rcvd == resp) break; if (resp == CMC22x_CP_RECV_NV_END && rcvd == CMC22x_CP_CAL_BAD) { - mif_info("invalid resp CMC22x_CP_CAL_BAD\n"); + mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name); break; } if (count-- < 0) { - mif_info("invalid resp 0x%04X\n", rcvd); + mif_info("%s: Invalid resp 0x%04X\n", + ld->name, rcvd); return -EAGAIN; } @@ -438,104 +369,110 @@ static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) return rcvd; } -static int cmc221_xmit_boot(struct dpram_link_device *dpld, unsigned long arg) +static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg) { struct link_device *ld = &dpld->ld; u8 __iomem *bt_buff = dpld->bt_map.buff; - struct cmc221_boot_img cp_img; + struct dpram_boot_img cp_img; u8 *img_buff = NULL; int err = 0; int cnt = 0; - mif_info("+++\n"); ld->mode = LINK_MODE_BOOT; - dpld->dpram->setup_speed(DPRAM_SPEED_LOW); + dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); /* Test memory... After testing, memory is cleared. */ - if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.space) < 0) { - mif_info("ERR! mif_test_dpram fail!\n"); + if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) { + mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name); ld->mode = LINK_MODE_OFFLINE; return -EIO; } - memset(&cp_img, 0, sizeof(struct cmc221_boot_img)); + memset(&cp_img, 0, sizeof(struct dpram_boot_img)); /* Get information about the boot image */ - err = copy_from_user(&cp_img, (void __user *)arg, sizeof(cp_img)); - mif_info("CP image addr = 0x%08X, size = %d\n", - (int)cp_img.addr, cp_img.size); + err = copy_from_user(&cp_img, arg, sizeof(cp_img)); + mif_info("%s: CP image addr = 0x%08X, size = %d\n", + ld->name, (int)cp_img.addr, cp_img.size); /* Alloc a buffer for the boot image */ - img_buff = kzalloc(dpld->bt_map.space, GFP_KERNEL); + img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL); if (!img_buff) { - mif_info("ERR! kzalloc fail\n"); + mif_info("%s: ERR! kzalloc fail\n", ld->name); ld->mode = LINK_MODE_OFFLINE; return -ENOMEM; } /* Copy boot image from the user space to the image buffer */ - err = copy_from_user(img_buff, (void __user *)cp_img.addr, cp_img.size); + err = copy_from_user(img_buff, cp_img.addr, cp_img.size); /* Copy boot image to DPRAM and verify it */ memcpy(bt_buff, img_buff, cp_img.size); if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) { - mif_info("ERR! Boot may be broken!!!\n"); + mif_info("%s: ERR! Boot may be broken!!!\n", ld->name); goto err; } - cmc221_idpram_reset(dpld); + dpld->dpctl->reset(); usleep_range(1000, 2000); - if (cp_img.mode == CP_BOOT_MODE_NORMAL) { - mif_info("CP_BOOT_MODE_NORMAL\n"); - mif_info("send req 0x%08X\n", cp_img.req); + if (cp_img.mode == HOST_BOOT_MODE_NORMAL) { + mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name); + mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req); iowrite32(cp_img.req, dpld->bt_map.req); /* Wait for cp_img.resp for up to 2 seconds */ - mif_info("wait resp 0x%08X\n", cp_img.resp); + mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp); while (ioread32(dpld->bt_map.resp) != cp_img.resp) { cnt++; usleep_range(1000, 2000); if (cnt > 1000) { - mif_info("ERR! invalid resp 0x%08X\n", - ioread32(dpld->bt_map.resp)); + mif_info("%s: ERR! Invalid resp 0x%08X\n", + ld->name, ioread32(dpld->bt_map.resp)); goto err; } } } else { - mif_info("CP_BOOT_MODE_DUMP\n"); + mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name); } kfree(img_buff); - mif_info("send BOOT done\n"); + mif_info("%s: Send BOOT done\n", ld->name); - dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); - mif_info("---\n"); return 0; err: ld->mode = LINK_MODE_OFFLINE; kfree(img_buff); - mif_err("FAIL!!!\n"); - mif_info("---\n"); + mif_info("%s: ERR! Boot send fail!!!\n", ld->name); return -EIO; } -static int cmc221_idpram_download_bin(struct dpram_link_device *dpld, +static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg) +{ + if (dpld->dp_type == CP_IDPRAM) + return _cmc221_idpram_send_boot(dpld, arg); + else + return -ENODEV; +} + +static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, struct sk_buff *skb) { int err = 0; int ret = 0; + struct link_device *ld = &dpld->ld; struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; u8 __iomem *buff = (dpld->bt_map.buff + bf->offset); - if ((bf->offset + bf->len) > dpld->bt_map.space) { - mif_info("ERR! out of DPRAM boundary\n"); + if ((bf->offset + bf->len) > dpld->bt_map.size) { + mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); err = -EINVAL; goto exit; } @@ -544,16 +481,17 @@ static int cmc221_idpram_download_bin(struct dpram_link_device *dpld, memcpy(buff, bf->data, bf->len); if (bf->req) - cmc221_idpram_send_msg(dpld, (u16)bf->req); + dpld->dpctl->send_msg((u16)bf->req); if (bf->resp) { - err = cmc221_idpram_wait_resp(dpld, bf->resp); + err = _cmc221_idpram_wait_resp(dpld, bf->resp); if (err < 0) - mif_info("ERR! wait_resp fail (err %d)\n", err); + mif_info("%s: ERR! wait_response fail (err %d)\n", + ld->name, err); } if (bf->req == CMC22x_CAL_NV_DOWN_END) - mif_info("request CMC22x_CAL_NV_DOWN_END\n"); + mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name); exit: if (err < 0) @@ -566,84 +504,89 @@ exit: return ret; } -static int cmc221_xmit_binary(struct dpram_link_device *dpld, +static int cmc221_download_binary(struct dpram_link_device *dpld, struct sk_buff *skb) { - if (dpld->type == CP_IDPRAM) - return cmc221_idpram_download_bin(dpld, skb); + if (dpld->dp_type == CP_IDPRAM) + return _cmc221_idpram_download_bin(dpld, skb); else return -ENODEV; } static int cmc221_dump_start(struct dpram_link_device *dpld) { - dpld->ld.mode = LINK_MODE_ULOAD; + struct link_device *ld = &dpld->ld; + int ret; + + ld->mode = LINK_MODE_ULOAD; - del_timer(&dpld->crash_timer); + ret = del_timer(&dpld->dump_timer); wake_lock(&dpld->wlock); - dpld->crash_rcvd = 0; + dpld->dump_rcvd = 0; iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic); - init_completion(&dpld->crash_cmpl); + init_completion(&dpld->dump_start_complete); return 0; } -static void cmc221_idpram_wait_dump(unsigned long arg) +static void _cmc221_idpram_wait_dump(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; u16 msg; - msg = cmc221_idpram_recv_msg(dpld); + msg = dpld->dpctl->recv_msg(); if (msg == CMC22x_CP_DUMP_END) { - complete_all(&dpld->crash_cmpl); + complete_all(&dpld->dump_recv_done); return; } - if (((dpld->crash_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { - complete_all(&dpld->crash_cmpl); + if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { + complete_all(&dpld->dump_recv_done); return; } - if (((dpld->crash_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { - complete_all(&dpld->crash_cmpl); + if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { + complete_all(&dpld->dump_recv_done); return; } - mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, - cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, + _cmc221_idpram_wait_dump, (unsigned long)dpld); } -static int cmc221_idpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dumparg) +static int _cmc221_idpram_upload(struct dpram_link_device *dpld, + struct dpram_dump_arg *dumparg) { + struct link_device *ld = &dpld->ld; int ret; u8 __iomem *src; int buff_size = CMC22x_DUMP_BUFF_SIZE; - if ((dpld->crash_rcvd & 0x1) == 0) - cmc221_idpram_send_msg(dpld, CMC22x_1ST_BUFF_READY); + if ((dpld->dump_rcvd & 0x1) == 0) + dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY); else - cmc221_idpram_send_msg(dpld, CMC22x_2ND_BUFF_READY); + dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY); - init_completion(&dpld->crash_cmpl); + init_completion(&dpld->dump_recv_done); - mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, - cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, + _cmc221_idpram_wait_dump, (unsigned long)dpld); - ret = wait_for_completion_timeout(&dpld->crash_cmpl, DUMP_TIMEOUT); + ret = wait_for_completion_interruptible_timeout( + &dpld->dump_recv_done, DUMP_TIMEOUT); if (!ret) { - mif_info("ERR! no dump from CP!!!\n"); + mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name); goto err_out; } - if (cmc221_idpram_recv_msg(dpld) == CMC22x_CP_DUMP_END) { - mif_info("recv CMC22x_CP_DUMP_END\n"); + if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) { + mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name); return 0; } - if ((dpld->crash_rcvd & 0x1) == 0) + if ((dpld->dump_rcvd & 0x1) == 0) src = dpld->ul_map.buff; else src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE; @@ -652,64 +595,52 @@ static int cmc221_idpram_upload(struct dpram_link_device *dpld, ret = copy_to_user(dumparg->buff, dpld->buff, buff_size); if (ret < 0) { - mif_info("ERR! copy_to_user fail\n"); + mif_info("%s: ERR! copy_to_user fail\n", ld->name); goto err_out; } - dpld->crash_rcvd++; + dpld->dump_rcvd++; return buff_size; err_out: return -EIO; } -static int cmc221_dump_update(struct dpram_link_device *dpld, unsigned long arg) +static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg) { + struct link_device *ld = &dpld->ld; struct dpram_dump_arg dump; int ret; ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); if (ret < 0) { - mif_info("ERR! copy_from_user fail\n"); + mif_info("%s: ERR! copy_from_user fail\n", ld->name); return ret; } - return cmc221_idpram_upload(dpld, &dump); -} - -static void cmc221_idpram_clr_int2ap(struct dpram_link_device *dpld) -{ - iowrite16(0xFFFF, cmc_sfr.clr_int2ap); + return _cmc221_idpram_upload(dpld, &dump); } -static int cmc221_idpram_wakeup(struct dpram_link_device *dpld) +static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg) { - int cnt = 0; - - gpio_set_value(dpld->gpio_cp_wakeup, 1); + struct link_device *ld = &dpld->ld; + int err = 0; - while (!gpio_get_value(dpld->gpio_cp_status)) { - if (cnt++ > 10) { - if (in_irq()) - mif_err("ERR! gpio_cp_status == 0 in IRQ\n"); - else - mif_err("ERR! gpio_cp_status == 0\n"); - return -EACCES; - } + switch (cmd) { + case IOCTL_DPRAM_SEND_BOOT: + err = cmc221_download_boot(dpld, (void *)arg); + if (err < 0) + mif_info("%s: ERR! download_boot fail\n", ld->name); + break; - mif_info("gpio_cp_status == 0 (cnt %d)\n", cnt); - if (in_interrupt()) - udelay(1000); - else - usleep_range(1000, 2000); + default: + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + break; } - return 0; -} - -static void cmc221_idpram_sleep(struct dpram_link_device *dpld) -{ - gpio_set_value(dpld->gpio_cp_wakeup, 0); + return err; } #endif @@ -721,44 +652,17 @@ enum qc_dload_tag { QC_DLOAD_TAG_MAX }; -struct qc_dpram_boot_map { - u8 __iomem *buff; - u16 __iomem *frame_size; - u16 __iomem *tag; - u16 __iomem *count; -}; - -struct qc_dpram_udl_param { - unsigned char *addr; - unsigned int size; - unsigned int count; - unsigned int tag; -}; - -struct qc_dpram_udl_check { - unsigned int total_size; - unsigned int rest_size; - unsigned int send_size; - unsigned int copy_start; - unsigned int copy_complete; - unsigned int boot_complete; -}; - -static struct qc_dpram_boot_map qc_bt_map; -static struct qc_dpram_udl_param qc_udl_param; -static struct qc_dpram_udl_check qc_udl_check; - static void qc_dload_task(unsigned long data); static void qc_init_boot_map(struct dpram_link_device *dpld) { - struct qc_dpram_boot_map *qbt_map = &qc_bt_map; - struct modemlink_dpram_data *dpram = dpld->dpram; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct modemlink_dpram_control *dpctl = dpld->dpctl; - qbt_map->buff = dpld->base; - qbt_map->frame_size = (u16 *)(dpld->base + dpram->boot_size_offset); - qbt_map->tag = (u16 *)(dpld->base + dpram->boot_tag_offset); - qbt_map->count = (u16 *)(dpld->base + dpram->boot_count_offset); + bt_map->buff = dpld->dp_base; + bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); + bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); + bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); } @@ -769,15 +673,15 @@ static int qc_prepare_download(struct dpram_link_device *dpld) int count = 0; while (1) { - if (qc_udl_check.copy_start) { - qc_udl_check.copy_start = 0; + if (dpld->udl_check.copy_start) { + dpld->udl_check.copy_start = 0; break; } - usleep_range(10000, 11000); + msleep_interruptible(10); count++; - if (count > 300) { + if (count > 200) { mif_err("ERR! count %d\n", count); return -1; } @@ -786,33 +690,32 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void qc_do_download(struct dpram_link_device *dpld, - struct qc_dpram_udl_param *param) +static void _qc_do_download(struct dpram_link_device *dpld, + struct dpram_udl_param *param) { - struct qc_dpram_boot_map *qbt_map = &qc_bt_map; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - if (param->size <= dpld->dpram->max_boot_frame_size) { - memcpy(qbt_map->buff, param->addr, param->size); - iowrite16(param->size, qbt_map->frame_size); - iowrite16(param->tag, qbt_map->tag); - iowrite16(param->count, qbt_map->count); + if (param->size <= dpld->dpctl->max_boot_frame_size) { + memcpy(bt_map->buff, param->addr, param->size); + iowrite16(param->size, bt_map->frame_size); + iowrite16(param->tag, bt_map->tag); + iowrite16(param->count, bt_map->count); dpld->send_intr(dpld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int qc_download(struct dpram_link_device *dpld, void *arg, +static int _qc_download(struct dpram_link_device *dpld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct qc_dpram_udl_param param; + struct dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void __user *)arg, - sizeof(param)); + retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail\n"); return -1; @@ -826,24 +729,24 @@ static int qc_download(struct dpram_link_device *dpld, void *arg, memset(img, 0, param.size); memcpy(img, param.addr, param.size); - qc_udl_check.total_size = param.size; - qc_udl_check.rest_size = param.size; - qc_udl_check.send_size = 0; - qc_udl_check.copy_complete = 0; + dpld->udl_check.total_size = param.size; + dpld->udl_check.rest_size = param.size; + dpld->udl_check.send_size = 0; + dpld->udl_check.copy_complete = 0; - qc_udl_param.addr = img; - qc_udl_param.size = dpld->dpram->max_boot_frame_size; + dpld->udl_param.addr = img; + dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - qc_udl_param.count = 1; + dpld->udl_param.count = 1; else - qc_udl_param.count = param.count; - qc_udl_param.tag = tag; + dpld->udl_param.count = param.count; + dpld->udl_param.tag = tag; - if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) - qc_udl_param.size = qc_udl_check.rest_size; + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; /* Download image (binary or NV) */ - qc_do_download(dpld, &qc_udl_param); + _qc_do_download(dpld, &dpld->udl_param); /* Wait for completion */ @@ -853,18 +756,18 @@ static int qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (qc_udl_check.copy_complete) { - qc_udl_check.copy_complete = 0; + if (dpld->udl_check.copy_complete) { + dpld->udl_check.copy_complete = 0; retval = 0; break; } - usleep_range(10000, 11000); + msleep(10); count++; if (count > cnt_limit) { - qc_udl_check.total_size = 0; - qc_udl_check.rest_size = 0; + dpld->udl_check.total_size = 0; + dpld->udl_check.rest_size = 0; mif_err("ERR! count %d\n", count); retval = -1; break; @@ -878,51 +781,51 @@ static int qc_download(struct dpram_link_device *dpld, void *arg, static int qc_download_binary(struct dpram_link_device *dpld, void *arg) { - return qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); } static int qc_download_nv(struct dpram_link_device *dpld, void *arg) { - return qc_download(dpld, arg, QC_DLOAD_TAG_NV); + return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { struct dpram_link_device *dpld = (struct dpram_link_device *)data; - qc_udl_check.send_size += qc_udl_param.size; - qc_udl_check.rest_size -= qc_udl_param.size; + dpld->udl_check.send_size += dpld->udl_param.size; + dpld->udl_check.rest_size -= dpld->udl_param.size; - qc_udl_param.addr += qc_udl_param.size; + dpld->udl_param.addr += dpld->udl_param.size; - if (qc_udl_check.send_size >= qc_udl_check.total_size) { - qc_udl_check.copy_complete = 1; - qc_udl_param.tag = 0; + if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { + dpld->udl_check.copy_complete = 1; + dpld->udl_param.tag = 0; return; } - if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) - qc_udl_param.size = qc_udl_check.rest_size; + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; - qc_udl_param.count += 1; + dpld->udl_param.count += 1; - qc_do_download(dpld, &qc_udl_param); + _qc_do_download(dpld, &dpld->udl_param); } static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) { switch (cmd) { case 0x1234: - qc_udl_check.copy_start = 1; + dpld->udl_check.copy_start = 1; break; case 0xDBAB: - if (qc_udl_check.total_size) + if (dpld->udl_check.total_size) tasklet_schedule(&dpld->dl_tsk); break; case 0xABCD: - qc_udl_check.boot_complete = 1; + dpld->udl_check.boot_complete = 1; break; default: @@ -940,12 +843,12 @@ static int qc_boot_start(struct dpram_link_device *dpld) dpld->send_intr(dpld, mask); while (1) { - if (qc_udl_check.boot_complete) { - qc_udl_check.boot_complete = 0; + if (dpld->udl_check.boot_complete) { + dpld->udl_check.boot_complete = 0; break; } - usleep_range(10000, 11000); + msleep_interruptible(10); count++; if (count > 200) { @@ -967,7 +870,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) break; } - usleep_range(10000, 11000); + msleep_interruptible(10); count++; if (count > 200) { @@ -997,26 +900,27 @@ static void qc_start_handler(struct dpram_link_device *dpld) static void qc_crash_log(struct dpram_link_device *dpld) { + struct link_device *ld = &dpld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = dpld->get_rxq_buff(dpld, IPC_FMT); + data = dpld->get_rx_buff(dpld, IPC_FMT); memcpy(buf, data, (sizeof(buf) - 1)); - mif_info("PHONE ERR MSG\t| %s Crash\n", dpld->ld.mc->name); + mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); mif_info("PHONE ERR MSG\t| %s\n", buf); } -static int qc_data_upload(struct dpram_link_device *dpld, - struct qc_dpram_udl_param *param) +static int _qc_data_upload(struct dpram_link_device *dpld, + struct dpram_udl_param *param) { - struct qc_dpram_boot_map *qbt_map = &qc_bt_map; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(dpld->gpio_int2ap)) { + if (!gpio_get_value(dpld->gpio_dpram_int)) { intval = dpld->recv_intr(dpld); if (intval == 0xDBAB) { break; @@ -1026,7 +930,7 @@ static int qc_data_upload(struct dpram_link_device *dpld, } } - usleep_range(1000, 2000); + msleep_interruptible(1); count++; if (count > 200) { @@ -1035,10 +939,10 @@ static int qc_data_upload(struct dpram_link_device *dpld, } } - param->size = ioread16(qbt_map->frame_size); - memcpy(param->addr, qbt_map->buff, param->size); - param->tag = ioread16(qbt_map->tag); - param->count = ioread16(qbt_map->count); + param->size = ioread16(bt_map->frame_size); + memcpy(param->addr, bt_map->buff, param->size); + param->tag = ioread16(bt_map->tag); + param->count = ioread16(bt_map->count); dpld->send_intr(dpld, 0xDB12); @@ -1057,7 +961,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(dpld->gpio_int2ap)) { + if (!gpio_get_value(dpld->gpio_dpram_int)) { intval = dpld->recv_intr(dpld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { @@ -1068,7 +972,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - usleep_range(1000, 2000); + msleep_interruptible(1); count++; if (count > 200) { @@ -1089,18 +993,17 @@ static int qc_uload_step1(struct dpram_link_device *dpld) static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) { int retval = 0; - struct qc_dpram_udl_param param; + struct dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void __user *)arg, - sizeof(param)); + retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail (err %d)\n", retval); return -1; } - retval = qc_data_upload(dpld, ¶m); + retval = _qc_data_upload(dpld, ¶m); if (retval < 0) { - mif_err("ERR! qc_data_upload fail (err %d)\n", retval); + mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); return -1; } @@ -1122,39 +1025,40 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) } static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { + struct link_device *ld = &dpld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: err = qc_prepare_download(dpld); if (err < 0) - mif_info("ERR! prepare_download fail\n"); + mif_info("%s: ERR! prepare_download fail\n", ld->name); break; case IOCTL_DPRAM_PHONEIMG_LOAD: err = qc_download_binary(dpld, (void *)arg); if (err < 0) - mif_info("ERR! download_binary fail\n"); + mif_info("%s: ERR! download_binary fail\n", ld->name); break; case IOCTL_DPRAM_NVDATA_LOAD: err = qc_download_nv(dpld, (void *)arg); if (err < 0) - mif_info("ERR! download_nv fail\n"); + mif_info("%s: ERR! download_nv fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_BOOTSTART: err = qc_boot_start(dpld); if (err < 0) { - mif_info("ERR! boot_start fail\n"); + mif_info("%s: ERR! boot_start fail\n", ld->name); break; } err = qc_boot_post_process(dpld); if (err < 0) - mif_info("ERR! boot_post_process fail\n"); + mif_info("%s: ERR! boot_post_process fail\n", ld->name); break; @@ -1163,7 +1067,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step1(dpld); if (err < 0) { enable_irq(dpld->irq); - mif_info("ERR! upload_step1 fail\n"); + mif_info("%s: ERR! upload_step1 fail\n", ld->name); } break; @@ -1171,12 +1075,12 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step2(dpld, (void *)arg); if (err < 0) { enable_irq(dpld->irq); - mif_info("ERR! upload_step2 fail\n"); + mif_info("%s: ERR! upload_step2 fail\n", ld->name); } break; default: - mif_err("ERR! invalid cmd 0x%08X\n", cmd); + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); err = -EINVAL; break; } @@ -1187,402 +1091,44 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, static irqreturn_t qc_dpram_irq_handler(int irq, void *data) { struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = &dpld->ld; - struct mem_status stat; + struct link_device *ld = (struct link_device *)&dpld->ld; u16 int2ap = 0; - if (ld->mode == LINK_MODE_OFFLINE) { - int2ap = dpld->recv_intr(dpld); + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - } - dpld->get_dpram_status(dpld, RX, &stat); - int2ap = stat.int2ap; + int2ap = dpld->recv_intr(dpld); if (int2ap == INT_POWERSAFE_FAIL) { - mif_info("int2ap == INT_POWERSAFE_FAIL\n"); + mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); goto exit; } if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { qc_dload_cmd_handler(dpld, int2ap); goto exit; - } else if (int2ap == 0x4321 || int2ap == 0x5432) { - mif_err("ERR! CP error command (0x%04X)\n", int2ap); - goto exit; } if (likely(INT_VALID(int2ap))) - dpld->ipc_rx_handler(dpld, &stat); + dpld->ipc_rx_handler(dpld, int2ap); else - mif_info("ERR! invalid intr 0x%04X\n", int2ap); + mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap); exit: return IRQ_HANDLED; } #endif -#if defined(CONFIG_CDMA_MODEM_QSC6085) -#define CMD_CP_RAMDUMP_START_REQ 0x9200 -#define CMD_CP_RAMDUMP_SEND_REQ 0x9400 -#define CMD_CP_RAMDUMP_SEND_DONE_REQ 0x9600 - -#define CMD_CP_RAMDUMP_START_RESP 0x0300 -#define CMD_CP_RAMDUMP_SEND_RESP 0x0500 -#define CMD_CP_RAMDUMP_SEND_DONE_RESP 0x0700 - -#define QSC_UPLOAD_MODE (0x444D554C) -#define QSC_UPLOAD_MODE_COMPLETE (0xABCDEF90) - -#define RAMDUMP_CMD_TIMEOUT (5 * HZ) -#define QSC6085_RAM_SIZE (32 * 1024 * 1024) /* 32MB */ - -struct qsc6085_dump_command { - u32 addr; - u32 size; - u32 copyto_offset; -}; - -struct qsc6085_dump_status { - u32 dump_size; - u32 addr; - u32 rcvd; - u32 rest; -}; - -static struct qsc6085_dump_status qsc_dump_stat; - -static void qsc6085_dump_work(struct work_struct *work); - -static void qsc6085_init_dl_map(struct dpram_link_device *dpld) -{ - dpld->dl_map.magic = (u32 *)dpld->base; - dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); -} - -static void qsc6085_init_ul_map(struct dpram_link_device *dpld) -{ - int magic_size = DP_ULOAD_MAGIC_SIZE; - int cmd_size = sizeof(struct qsc6085_dump_command); - int mbx_size = DP_MBX_SET_SIZE; - - dpld->ul_map.magic = (u32 *)dpld->base; - dpld->ul_map.cmd = dpld->base + magic_size; - dpld->ul_map.cmd_size = cmd_size; - dpld->ul_map.buff = dpld->base + magic_size + cmd_size; - dpld->ul_map.space = dpld->size - (magic_size + cmd_size + mbx_size); -} - -static void qsc6085_req_active_handler(struct dpram_link_device *dpld) -{ - struct modem_ctl *mc = dpld->ld.mc; - mif_info("pda_active = %d\n", gpio_get_value(mc->gpio_pda_active)); - dpld->send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); -} - -static void qsc6085_error_display_handler(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - struct io_device *iod; - - mif_err("recv 0xC9 (CRASH_EXIT)\n"); - mif_err("CP Crash: %s\n", dpld->get_rxq_buff(dpld, IPC_FMT)); - - iod = link_get_iod_with_format(ld, IPC_FMT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_EXIT); -} - -static void qsc6085_start_handler(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - struct io_device *iod; - - mif_info("recv 0xC8 (CP_START)\n"); - - mif_info("send 0xC1 (INIT_START)\n"); - dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_START)); - - dpld->reset_dpram_ipc(dpld); - - iod = link_get_iod_with_format(ld, IPC_FMT); - if (!iod) { - mif_err("ERR! no iod\n"); - return; - } - iod->modem_state_changed(iod, STATE_ONLINE); - - mif_info("send 0xC2 (INIT_END)\n"); - dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); -} - -static void qsc6085_command_handler(struct dpram_link_device *dpld, u16 cmd) -{ - switch (INT_CMD_MASK(cmd)) { - case INT_CMD_REQ_ACTIVE: - qsc6085_req_active_handler(dpld); - break; - - case INT_CMD_ERR_DISPLAY: -#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM - /* If modem crashes while PDA_SLEEP is in progres */ - dpld->pm_op->halt_suspend(dpld); -#endif - qsc6085_error_display_handler(dpld); - break; - - case INT_CMD_PHONE_START: - qsc6085_start_handler(dpld); - complete_all(&ld->init_cmpl); - break; - -#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM - case INT_CMD_IDPRAM_SUSPEND_ACK: - dpld->pm_op->power_down(dpld); - break; - - case INT_CMD_IDPRAM_WAKEUP_START: - dpld->pm_op->power_up(dpld); - break; -#endif - - case INT_CMD_NORMAL_POWER_OFF: - complete(&dpld->crash_cmpl); - qsc6085_error_display_handler(dpld); - break; - - default: - mif_err("unknown command 0x%04X\n", cmd); - break; - } -} - -static int qsc6085_download_firmware(struct dpram_link_device *dpld, - struct modem_firmware *fw) -{ - int ret = 0; - char __user *src = fw->binary; - int rest = fw->size; - char __iomem *dst = NULL; - unsigned long timeout; - u16 curr_frame = 0; - u16 len = 0; - struct dpram_udl_header header; - - header.bop = START_FLAG; - header.num_frames = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN); - mif_err("FW %d bytes = %d frames\n", fw->size, header.num_frames); - - while (rest > 0) { - curr_frame++; - len = min(rest, DP_DEFAULT_WRITE_LEN); - - header.curr_frame = curr_frame; - header.len = len; - mif_info(">>> frame# %u, len %u\n", curr_frame, len); - - dst = dpld->dl_map.buff; - memcpy(dst, &header, sizeof(header)); - - dst += sizeof(header); - ret = copy_from_user(dst, (void __user *)src, len); - if (ret < 0) { - mif_err("copy_from_user fail\n"); - return -EIO; - } - - dst += len; - src += len; - rest -= len; - - iowrite8(END_FLAG, (dst+3)); - - if (curr_frame == 1) { - dpld->send_intr(dpld, 0); - timeout = UDL_TIMEOUT; - } else { - dpld->send_intr(dpld, CMD_DL_SEND_REQ); - timeout = UDL_SEND_TIMEOUT; - } - - ret = wait_for_completion_timeout(&dpld->udl_cmpl, timeout); - if (!ret) { - mif_err("ERR! no response from CP\n"); - return -EIO; - } - } - - mif_err("send CMD_DL_DONE_REQ to CP\n"); - dpld->send_intr(dpld, CMD_DL_DONE_REQ); - - ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); - if (!ret) { - mif_err("ERR! no response from CP\n"); - return -EIO; - } - - return 0; -} - -static int qsc6085_dload_firmware(struct dpram_link_device *dpld, - unsigned long arg) -{ - int ret; - struct modem_firmware fw; - mif_err("+++\n"); - - ret = copy_from_user(&fw, (void __user *)arg, sizeof(fw)); - if (ret < 0) { - mif_err("ERR! copy_from_user fail!\n"); - return ret; - } - - ret = qsc6085_download_firmware(dpld, &fw); - - mif_err("---\n"); - return ret; -} - -static int qsc6085_dump_start(struct dpram_link_device *dpld) -{ - int ret; - struct link_device *ld = &dpld->ld; - struct modem_ctl *mc = ld->mc; - struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; - mif_err("+++\n"); - - init_completion(&dpld->crash_cmpl); - INIT_DELAYED_WORK(&dpld->crash_dwork, qsc6085_dump_work); - - iowrite32(QSC_UPLOAD_MODE, &dpld->ul_map.magic); - - /* reset modem so that it goes to upload mode */ - /* ap does not need to reset cp during CRASH_EXIT case */ - if (gpio_get_value(mc->gpio_phone_active)) - mc->ops.modem_reset(mc); - - dpld->send_intr(dpld, CMD_CP_RAMDUMP_START_REQ); - ret = wait_for_completion_timeout(&dpld->crash_cmpl, - RAMDUMP_CMD_TIMEOUT); - if (!ret) { - mif_err("ERR! no response to CP_RAMDUMP_START_REQ\n"); - dump_stat->dump_size = 0; - } else { - dump_stat->dump_size = QSC6085_RAM_SIZE; - dump_stat->addr = 0; - dump_stat->rcvd = 0; - dump_stat->rest = dump_stat->dump_size; - } - - queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); - - mif_err("---\n"); - return 0; -} - -static int qsc6085_dump_update(struct dpram_link_device *dpld, - unsigned long arg) -{ - int ret; - struct link_device *ld = &dpld->ld; - struct io_device *iod = link_get_iod_with_format(ld, IPC_RAMDUMP); - struct memif_uload_map *ul_map = &dpld->ul_map; - struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; - struct qsc6085_dump_command dump_cmd; - - while (iod->sk_rx_q.qlen > 0) - usleep_range(1000, 1100); - - memset(&dump_cmd, 0, sizeof(dump_cmd)); - dump_cmd.addr = dump_stat->addr; - dump_cmd.size = min(dump_stat->rest, ul_map->space); - dump_cmd.copyto_offset = 0x38000010; - - memcpy_toio(ul_map->cmd, &dump_cmd, ul_map->cmd_size); - - dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_REQ); - ret = wait_for_completion_timeout(&dpld->crash_cmpl, - RAMDUMP_CMD_TIMEOUT); - if (!ret) { - dump_stat->dump_size = 0; - mif_err("ERR! no response to CP_RAMDUMP_SEND_REQ\n"); - ret = -EIO; - goto exit; - } - - memcpy_fromio(dpld->buff, ul_map->buff, dump_cmd.size); - - ret = iod->recv(iod, ld, dpld->buff, dump_cmd.size); - if (ret < 0) - goto exit; - - dump_stat->addr += dump_cmd.size; - dump_stat->rcvd += dump_cmd.size; - dump_stat->rest -= dump_cmd.size; - mif_info("rest = %u bytes\n", dump_stat->rest); - - ret = dump_cmd.size; - -exit: - return ret; -} - -static void qsc6085_dump_work(struct work_struct *work) -{ - struct dpram_link_device *dpld; - struct link_device *ld; - struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; - int ret; - - dpld = container_of(work, struct dpram_link_device, crash_dwork.work); - ld = &dpld->ld; - - ret = qsc6085_dump_update(dpld, 0); - if (ret > 0 && dump_stat->rest > 0) - queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); -} - -static int qsc6085_dump_finish(struct dpram_link_device *dpld, - unsigned long arg) -{ - int ret; - struct completion *cmpl = &dpld->crash_cmpl; - mif_err("+++\n"); - - init_completion(cmpl); - - dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_DONE_REQ); - - ret = wait_for_completion_timeout(cmpl, RAMDUMP_CMD_TIMEOUT); - if (!ret) { - mif_err("ERR! no response to CP_RAMDUMP_SEND_DONE_REQ\n"); - ret = -EIO; - } - - mif_err("---\n"); - return ret; -} -#endif - -static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = { +static struct dpram_ext_op ext_op_set[] = { #ifdef CONFIG_CDMA_MODEM_CBP72 [VIA_CBP72] = { .exist = 1, - .init_boot_map = cbp_init_boot_map, - .init_dl_map = cbp_init_dl_map, - .xmit_binary = cbp_xmit_binary, - .dump_start = cbp_dump_start, - .dump_update = cbp_dump_update, - }, -#endif -#ifdef CONFIG_CDMA_MODEM_CBP82 - [VIA_CBP82] = { - .exist = 1, - .init_boot_map = cbp_init_boot_map, - .init_dl_map = cbp_init_dl_map, - .xmit_binary = cbp_xmit_binary, - .dump_start = cbp_dump_start, - .dump_update = cbp_dump_update, + .init_boot_map = cbp72_init_boot_map, + .init_dl_map = cbp72_init_dl_map, + .download_binary = cbp72_download_binary, + .dump_start = cbp72_dump_start, + .dump_update = cbp72_dump_update, + .ioctl = cbp72_ioctl, }, #endif #ifdef CONFIG_LTE_MODEM_CMC221 @@ -1591,14 +1137,10 @@ static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = { .init_boot_map = cmc221_init_boot_map, .init_dl_map = cmc221_init_dl_map, .init_ul_map = cmc221_init_ul_map, - .init_ipc_map = cmc221_init_ipc_map, - .xmit_boot = cmc221_xmit_boot, - .xmit_binary = cmc221_xmit_binary, + .download_binary = cmc221_download_binary, .dump_start = cmc221_dump_start, .dump_update = cmc221_dump_update, - .clear_int2ap = cmc221_idpram_clr_int2ap, - .wakeup = cmc221_idpram_wakeup, - .sleep = cmc221_idpram_sleep, + .ioctl = cmc221_ioctl, }, #endif #if defined(CONFIG_CDMA_MODEM_MDM6600) @@ -1621,18 +1163,6 @@ static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = { .irq_handler = qc_dpram_irq_handler, }, #endif -#if defined(CONFIG_CDMA_MODEM_QSC6085) - [QC_QSC6085] = { - .exist = 1, - .init_dl_map = qsc6085_init_dl_map, - .init_ul_map = qsc6085_init_ul_map, - .cmd_handler = qsc6085_command_handler, - .firm_update = qsc6085_dload_firmware, - .dump_start = qsc6085_dump_start, - .dump_update = qsc6085_dump_update, - .dump_finish = qsc6085_dump_finish, - }, -#endif }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) @@ -1643,422 +1173,3 @@ struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) return NULL; } -#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM -#define GPIO_IDPRAM_SFN S3C_GPIO_SFN(2) - -#define MAX_CHECK_RETRY_CNT 5 -#define MAX_RESUME_TRY_CNT 5 - -static bool s5p_idpram_is_pm_locked(struct dpram_link_device *dpld) -{ - struct modem_ctl *mc = dpld->ld.mc; - struct idpram_pm_data *pm_data = &dpld->pm_data; - - /* If PM is in SUSPEND */ - if (atomic_read(&pm_data->pm_lock) > 0) { - mif_info("in SUSPEND\n"); - return true; - } - - /* If AP is in or into LPA */ - if (!gpio_get_value(mc->gpio_pda_active)) { - mif_info("in LPA\n"); - return true; - } - - return false; -} - -static void s5p_idpram_set_pm_lock(struct dpram_link_device *dpld, int lock) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - - /* 0 = unlock, 1 = lock */ - switch (lock) { - case 0: - if (atomic_read(&pm_data->pm_lock)) - atomic_set(&pm_data->pm_lock, lock); - break; - - case 1: - if (!atomic_read(&pm_data->pm_lock)) - atomic_set(&pm_data->pm_lock, lock); - break; - - default: - break; - } -} - -static void s5p_idpram_try_resume(struct work_struct *work) -{ - struct idpram_pm_data *pm_data; - struct dpram_link_device *dpld; - struct link_device *ld; - unsigned long delay; - u16 cmd; - mif_info("+++\n"); - - pm_data = container_of(work, struct idpram_pm_data, resume_dwork.work); - dpld = container_of(pm_data, struct dpram_link_device, pm_data); - ld = &dpld->ld; - - if (pm_data->last_msg == INT_CMD(INT_CMD_IDPRAM_RESUME_REQ)) { - pm_data->last_msg = 0; - - s5p_idpram_set_pm_lock(dpld, 0); - wake_unlock(&pm_data->hold_wlock); - - delay = msecs_to_jiffies(10); - schedule_delayed_work(&pm_data->tx_dwork, delay); - - mif_info("%s resumed\n", ld->name); - goto exit; - } - - if (pm_data->resume_try_cnt++ < MAX_RESUME_TRY_CNT) { - mif_info("%s not resumed yet\n", ld->name); - - cmd = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); - mif_info("send IDPRAM_RESUME_REQ (0x%X)\n", cmd); - dpld->send_intr(dpld, cmd); - - delay = msecs_to_jiffies(200); - schedule_delayed_work(&pm_data->resume_dwork, delay); - } else { - struct io_device *iod; - mif_err("ERR! %s resume T-I-M-E-O-U-T\n", ld->name); - - iod = link_get_iod_with_format(ld, IPC_FMT); - if (iod) - iod->modem_state_changed(iod, STATE_CRASH_EXIT); - - wake_unlock(&pm_data->hold_wlock); - - /* hold wakelock until uevnet sent to rild */ - wake_lock_timeout(&pm_data->hold_wlock, HZ*7); - s5p_idpram_set_pm_lock(dpld, 0); - } - -exit: - mif_info("---\n"); -} - -static irqreturn_t s5p_cp_dump_irq_handler(int irq, void *data) -{ - return IRQ_HANDLED; -} - -static irqreturn_t s5p_ap_wakeup_irq_handler(int irq, void *data) -{ - struct idpram_pm_data *pm_data = data; - wake_lock_timeout(&pm_data->ap_wlock, HZ*5); - return IRQ_HANDLED; -} - -static void s5p_idpram_power_down(struct dpram_link_device *dpld) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - mif_info("+++\n"); - - pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK); - complete(&pm_data->down_cmpl); - - mif_info("---\n"); -} - -static void s5p_idpram_power_up(struct dpram_link_device *dpld) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - mif_info("+++\n"); - - pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); - pm_data->pm_state = IDPRAM_PM_ACTIVE; - - mif_info("---\n"); -} - -static void s5p_idpram_halt_suspend(struct dpram_link_device *dpld) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - mif_info("+++\n"); - - complete(&pm_data->down_cmpl); - - mif_info("---\n"); -} - -static int s5p_idpram_prepare_suspend(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - struct idpram_pm_data *pm_data = &dpld->pm_data; - struct modem_ctl *mc = dpld->ld.mc; - struct completion *cmpl; - unsigned long timeout; - unsigned long rest; - int cnt = 0; - u16 cmd = INT_CMD(INT_CMD_IDPRAM_SUSPEND_REQ); - mif_info("+++\n"); - - pm_data->pm_state = IDPRAM_PM_SUSPEND_PREPARE; - pm_data->last_msg = 0; - s5p_idpram_set_pm_lock(dpld, 1); - - /* - * Because, if dpram was powered down, cp dpram random intr was - * ocurred. so, fixed by muxing cp dpram intr pin to GPIO output - * high,.. - */ - gpio_set_value(dpld->gpio_int2cp, 1); - s3c_gpio_cfgpin(dpld->gpio_int2cp, S3C_GPIO_OUTPUT); - - /* prevent PDA_ACTIVE status is low */ - gpio_set_value(mc->gpio_pda_active, 1); - - cmpl = &pm_data->down_cmpl; - timeout = IDPRAM_SUSPEND_REQ_TIMEOUT; - cnt = 0; - do { - init_completion(cmpl); - - mif_info("send IDPRAM_SUSPEND_REQ (0x%X)\n", cmd); - dpld->send_intr(dpld, cmd); - - rest = wait_for_completion_timeout(cmpl, timeout); - if (rest == 0) { - cnt++; - mif_err("timeout!!! (count = %d)\n", cnt); - if (cnt >= 3) { - mif_err("ERR! no response from CP\n"); - break; - } - } - } while (rest == 0); - - switch (pm_data->last_msg) { - case INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK): - mif_info("recv IDPRAM_SUSPEND_ACK (0x%X)\n", pm_data->last_msg); - pm_data->pm_state = IDPRAM_PM_DPRAM_POWER_DOWN; - break; - - default: - mif_err("ERR! %s down or not ready!!! (intr 0x%04X)\n", - ld->name, dpld->recv_intr(dpld)); - timeout = msecs_to_jiffies(500); - wake_lock_timeout(&pm_data->hold_wlock, timeout); - s5p_idpram_set_pm_lock(dpld, 0); - break; - } - - mif_info("---\n"); - return 0; -} - -static int s5p_idpram_resume_init(struct dpram_link_device *dpld) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - mif_info("+++\n"); - - pm_data->pm_state = IDPRAM_PM_RESUME_START; - pm_data->last_msg = 0; - - dpld->reset_dpram_ipc(dpld); - - /* re-initialize internal dpram gpios */ - s3c_gpio_cfgpin(dpld->gpio_int2cp, GPIO_IDPRAM_SFN); - - mif_info("---\n"); - return 0; -} - -static int s5p_idpram_start_resume(struct dpram_link_device *dpld) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - struct modem_ctl *mc = dpld->ld.mc; - unsigned long delay; - mif_info("+++ (pm_state = %d)\n", pm_data->pm_state); - - switch (pm_data->pm_state) { - /* schedule_work */ - case IDPRAM_PM_DPRAM_POWER_DOWN: - gpio_set_value(mc->gpio_pda_active, 0); - msleep(50); - - s5p_idpram_resume_init(dpld); - msleep(50); - - gpio_set_value(mc->gpio_pda_active, 1); - msleep(20); - - pm_data->resume_try_cnt = 0; - wake_lock(&pm_data->hold_wlock); - - delay = msecs_to_jiffies(20); - schedule_delayed_work(&pm_data->resume_dwork, delay); - break; - - case IDPRAM_PM_RESUME_START: - case IDPRAM_PM_SUSPEND_PREPARE: - default: - break; - } - - mif_info("---\n"); - return 0; -} - -static int s5p_idpram_notify_pm_event(struct notifier_block *this, - unsigned long event, void *v) -{ - struct idpram_pm_data *pm_data; - struct dpram_link_device *dpld; - int err; - mif_info("+++ (event 0x%08X)\n", (int)event); - - pm_data = container_of(this, struct idpram_pm_data, pm_noti); - dpld = container_of(pm_data, struct dpram_link_device, pm_data); - - switch (event) { - case PM_SUSPEND_PREPARE: - err = s5p_idpram_prepare_suspend(dpld); - break; - - case PM_POST_SUSPEND: - err = s5p_idpram_start_resume(dpld); - break; - - default: - break; - } - - mif_info("---\n"); - return NOTIFY_DONE; -} - -static int s5p_idpram_pm_init(struct dpram_link_device *dpld, - struct modem_data *modem, void (*pm_tx_func)(struct work_struct *work)) -{ - struct idpram_pm_data *pm_data = &dpld->pm_data; - int err; - unsigned gpio; - unsigned irq; - mif_info("+++\n"); - - atomic_set(&pm_data->pm_lock, 0); - - init_completion(&pm_data->down_cmpl); - - wake_lock_init(&pm_data->ap_wlock, WAKE_LOCK_SUSPEND, "ap_wakeup"); - wake_lock_init(&pm_data->hold_wlock, WAKE_LOCK_SUSPEND, "dpram_hold"); - - INIT_DELAYED_WORK(&pm_data->tx_dwork, pm_tx_func); - INIT_DELAYED_WORK(&pm_data->resume_dwork, s5p_idpram_try_resume); - - pm_data->resume_try_cnt = 0; - - /* register PM notifier */ - pm_data->pm_noti.notifier_call = s5p_idpram_notify_pm_event; - register_pm_notifier(&pm_data->pm_noti); - - /* - ** Register gpio_ap_wakeup interrupt handler - */ - gpio = modem->gpio_ap_wakeup; - irq = gpio_to_irq(gpio); - mif_info("gpio_ap_wakeup: GPIO# %d, IRQ# %d\n", gpio, irq); - - err = request_irq(irq, s5p_ap_wakeup_irq_handler, IRQF_TRIGGER_RISING, - "idpram_ap_wakeup", (void *)pm_data); - if (err) { - mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); - goto exit; - } - - err = enable_irq_wake(irq); - if (err) { - mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); - free_irq(irq, (void *)pm_data); - goto exit; - } - - /* - ** Register gpio_cp_dump_int interrupt handler for LPA mode - */ - gpio = modem->gpio_cp_dump_int; - irq = gpio_to_irq(gpio); - mif_info("gpio_cp_dump_int: GPIO# %d, IRQ# %d\n", gpio, irq); - - err = request_irq(irq, s5p_cp_dump_irq_handler, IRQF_TRIGGER_RISING, - "idpram_cp_dump", (void *)pm_data); - if (err) { - mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); - free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); - goto exit; - } - - err = enable_irq_wake(irq); - if (err) { - mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); - free_irq(gpio_to_irq(modem->gpio_cp_dump_int), (void *)pm_data); - free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); - goto exit; - } - -exit: - mif_err("---\n"); - return err; -} - -static bool s5p_idpram_int2cp_possible(struct dpram_link_device *dpld) -{ - struct modem_ctl *mc = dpld->ld.mc; - int i; - int level; - - for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { - level = gpio_get_value(dpld->gpio_int2cp); - if (level) - break; - - /* CP has not yet received previous command. */ - mif_info("gpio_ipc_int2cp == 0 (count %d)\n", i); - - usleep_range(1000, 1100); - } - - for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { - level = gpio_get_value(mc->gpio_pda_active); - if (level) - break; - - /* AP is in transition to LPA mode. */ - mif_info("gpio_pda_active == 0 (count %d)\n", i); - - usleep_range(1000, 1100); - } - - return true; -} -#endif - -static struct idpram_pm_op idpram_pm_op_set[MAX_AP_TYPE] = { -#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM - [S5P] = { - .pm_init = s5p_idpram_pm_init, - .power_down = s5p_idpram_power_down, - .power_up = s5p_idpram_power_up, - .halt_suspend = s5p_idpram_halt_suspend, - .locked = s5p_idpram_is_pm_locked, - .int2cp_possible = s5p_idpram_int2cp_possible, - }, -#endif -}; - -struct idpram_pm_op *idpram_get_pm_op(enum ap_type ap) -{ - if (idpram_pm_op_set[ap].exist) - return &idpram_pm_op_set[ap]; - else - return NULL; -} - diff --git a/drivers/misc/modem_if/modem_link_device_hsic.c b/drivers/misc/modem_if/modem_link_device_hsic.c index e5bcf8a..eb5dfc6 100644 --- a/drivers/misc/modem_if/modem_link_device_hsic.c +++ b/drivers/misc/modem_if/modem_link_device_hsic.c @@ -269,7 +269,7 @@ static void usb_rx_complete(struct urb *urb) switch (pipe_data->format) { case IF_USB_FMT_EP: if (usb_ld->if_usb_is_main) { - pr_urb("IPC-RX", urb); +// pr_urb("IPC-RX", urb); iod_format = IPC_FMT; } else { iod_format = IPC_BOOT; @@ -477,13 +477,13 @@ static int _usb_tx_work(struct sk_buff *skb) if (!pipe_data) return -ENOENT; - +/* if (iod->format == IPC_FMT && usb_ld->if_usb_is_main) pr_skb("IPC-TX", skb); if (iod->format == IPC_RAW) mif_debug("TX[RAW]\n"); - +*/ return usb_tx_urb_with_skb(usb_ld->usbdev, skb, pipe_data); } @@ -741,11 +741,11 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data) != HOSTWAKE_TRIGLEVEL) { if (gpio_get_value(pm_data->gpio_link_slavewake)) { gpio_set_value(pm_data->gpio_link_slavewake, 0); - mif_info("gpio [SWK] set [0]\n"); + mif_debug("gpio [SWK] set [0]\n"); mdelay(5); } gpio_set_value(pm_data->gpio_link_slavewake, 1); - mif_info("gpio [SWK] set [1]\n"); + mif_debug("gpio [SWK] set [1]\n"); mdelay(5); /* wait host wake signal*/ @@ -860,7 +860,7 @@ static irqreturn_t link_pm_irq_handler(int irq, void *data) runtime pm status changes to ACTIVE */ value = gpio_get_value(pm_data->gpio_link_hostwake); - mif_info("gpio [HWK] get [%d]\n", value); + mif_debug("gpio [HWK] get [%d]\n", value); /* * igonore host wakeup interrupt at suspending kernel @@ -975,7 +975,9 @@ static int link_pm_notifier_event(struct notifier_block *this, { struct link_pm_data *pm_data = container_of(this, struct link_pm_data, pm_notifier); +#ifdef CONFIG_UMTS_MODEM_XMM6262 struct modem_ctl *mc = if_usb_get_modemctl(pm_data); +#endif switch (event) { case PM_SUSPEND_PREPARE: @@ -984,11 +986,13 @@ static int link_pm_notifier_event(struct notifier_block *this, case PM_RESTORE_PREPARE: #endif pm_data->dpm_suspending = true; +#ifdef CONFIG_UMTS_MODEM_XMM6262 /* set PDA Active High if previous state was LPA */ if (!gpio_get_value(pm_data->gpio_link_active)) { mif_info("PDA active High to LPA suspend spot\n"); gpio_set_value(mc->gpio_pda_active, 1); } +#endif mif_debug("dpm suspending set to true\n"); return NOTIFY_OK; case PM_POST_SUSPEND: @@ -1002,14 +1006,15 @@ static int link_pm_notifier_event(struct notifier_block *this, queue_delayed_work(pm_data->wq, &pm_data->link_pm_work, 0); mif_info("post resume\n"); - } else { + } +#ifdef CONFIG_UMTS_MODEM_XMM6262 /* LPA to Kernel suspend and User Freezing task fail resume, restore to LPA GPIO states. */ - if (!gpio_get_value(pm_data->gpio_link_active)) { - mif_info("PDA active low to LPA GPIO state\n"); - gpio_set_value(mc->gpio_pda_active, 0); - } + if (!gpio_get_value(pm_data->gpio_link_active)) { + mif_info("PDA active low to LPA GPIO state\n"); + gpio_set_value(mc->gpio_pda_active, 0); } +#endif mif_debug("dpm suspending set to false\n"); return NOTIFY_OK; } @@ -1453,7 +1458,8 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) struct modem_data *pdata = (struct modem_data *)pdev->dev.platform_data; struct modemlink_pm_data *pm_pdata; - struct link_pm_data *pm_data; + struct link_pm_data *pm_data = + kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); if (!pdata || !pdata->link_pm_data) { mif_err("platform data is NULL\n"); @@ -1461,7 +1467,6 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) } pm_pdata = pdata->link_pm_data; - pm_data = kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); if (!pm_data) { mif_err("link_pm_data is NULL\n"); return -ENOMEM; diff --git a/drivers/misc/modem_if/modem_link_device_memory.c b/drivers/misc/modem_if/modem_link_device_memory.c deleted file mode 100644 index 9231135..0000000 --- a/drivers/misc/modem_if/modem_link_device_memory.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2011 Samsung Electronics. - * - * 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 "modem_prj.h" -#include "modem_utils.h" -#include "modem_link_device_memory.h" -#ifdef CONFIG_LINK_DEVICE_DPRAM -#include "modem_link_device_dpram.h" -#endif - -/** - * msq_get_free_slot - * @trq : pointer to an instance of mem_status_queue structure - * - * Succeeds always by dropping the oldest slot if a "msq" is full. - */ -struct mem_status *msq_get_free_slot(struct mem_status_queue *msq) -{ - int qsize = MAX_MEM_LOG_CNT; - int in; - int out; - unsigned long flags; - struct mem_status *stat; - - spin_lock_irqsave(&msq->lock, flags); - - in = msq->in; - out = msq->out; - - if (circ_get_space(qsize, in, out) < 1) { - /* Make the oldest slot empty */ - out++; - msq->out = (out == qsize) ? 0 : out; - } - - /* Get a free slot */ - stat = &msq->stat[in]; - - /* Make it as "data" slot */ - in++; - msq->in = (in == qsize) ? 0 : in; - - spin_unlock_irqrestore(&msq->lock, flags); - - memset(stat, 0, sizeof(struct mem_status)); - - return stat; -} - -struct mem_status *msq_get_data_slot(struct mem_status_queue *msq) -{ - int qsize = MAX_MEM_LOG_CNT; - int in; - int out; - unsigned long flags; - struct mem_status *stat; - - spin_lock_irqsave(&msq->lock, flags); - - in = msq->in; - out = msq->out; - - if (in == out) { - stat = NULL; - goto exit; - } - - /* Get a data slot */ - stat = &msq->stat[out]; - - /* Make it "free" slot */ - out++; - msq->out = (out == qsize) ? 0 : out; - -exit: - spin_unlock_irqrestore(&msq->lock, flags); - return stat; -} - -/** - * memcpy16_from_io - * @to: pointer to "real" memory - * @from: pointer to IO memory - * @count: data length in bytes to be copied - * - * Copies data from IO memory space to "real" memory space. - */ -void memcpy16_from_io(const void *to, const void __iomem *from, u32 count) -{ - u16 *d = (u16 *)to; - u16 *s = (u16 *)from; - u32 words = count >> 1; - while (words--) - *d++ = ioread16(s++); -} - -/** - * memcpy16_to_io - * @to: pointer to IO memory - * @from: pointer to "real" memory - * @count: data length in bytes to be copied - * - * Copies data from "real" memory space to IO memory space. - */ -void memcpy16_to_io(const void __iomem *to, const void *from, u32 count) -{ - u16 *d = (u16 *)to; - u16 *s = (u16 *)from; - u32 words = count >> 1; - while (words--) - iowrite16(*s++, d++); -} - -/** - * memcmp16_to_io - * @to: pointer to IO memory - * @from: pointer to "real" memory - * @count: data length in bytes to be compared - * - * Compares data from "real" memory space to IO memory space. - */ -int memcmp16_to_io(const void __iomem *to, const void *from, u32 count) -{ - u16 *d = (u16 *)to; - u16 *s = (u16 *)from; - int words = count >> 1; - int diff = 0; - int i; - u16 d1; - u16 s1; - - for (i = 0; i < words; i++) { - d1 = ioread16(d); - s1 = *s; - if (d1 != s1) { - diff++; - mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); - } - d++; - s++; - } - - return diff; -} - -/** - * circ_read16_from_io - * @dst: start address of the destination buffer - * @src: start address of the buffer in a circular queue - * @qsize: size of the circular queue - * @out: offset to read - * @len: length of data to be read - * - * Should be invoked after checking data length - */ -void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len) -{ - if ((out + len) <= qsize) { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - memcpy16_from_io(dst, (src + out), len); - } else { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - unsigned len1 = qsize - out; - - /* 1) data start (out) ~ buffer end */ - memcpy16_from_io(dst, (src + out), len1); - - /* 2) buffer start ~ data end (in - 1) */ - memcpy16_from_io((dst + len1), src, (len - len1)); - } -} - -/** - * circ_write16_to_io - * @dst: pointer to the start of the circular queue - * @src: pointer to the source - * @qsize: size of the circular queue - * @in: offset to write - * @len: length of data to be written - * - * Should be invoked after checking free space - */ -void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len) -{ - u32 space; - - if ((in + len) < qsize) { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - memcpy16_to_io((dst + in), src, len); - } else { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - - /* 1) space start (in) ~ buffer end */ - space = qsize - in; - memcpy16_to_io((dst + in), src, ((len > space) ? space : len)); - - /* 2) buffer start ~ data end */ - if (len > space) - memcpy16_to_io(dst, (src + space), (len - space)); - } -} - -/** - * copy_circ_to_user - * @dst: start address of the destination buffer - * @src: start address of the buffer in a circular queue - * @qsize: size of the circular queue - * @out: offset to read - * @len: length of data to be read - * - * Should be invoked after checking data length - */ -int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len) -{ - if ((out + len) <= qsize) { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - if (copy_to_user(dst, (src + out), len)) { - mif_err("ERR! copy_to_user fail\n", - CALLER); - return -EFAULT; - } - } else { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - unsigned len1 = qsize - out; - - /* 1) data start (out) ~ buffer end */ - if (copy_to_user(dst, (src + out), len1)) { - mif_err("ERR! copy_to_user fail\n", - CALLER); - return -EFAULT; - } - - /* 2) buffer start ~ data end (in?) */ - if (copy_to_user((dst + len1), src, (len - len1))) { - mif_err("ERR! copy_to_user fail\n", - CALLER); - return -EFAULT; - } - } - - return 0; -} - -/** - * copy_user_to_circ - * @dst: pointer to the start of the circular queue - * @src: pointer to the source - * @qsize: size of the circular queue - * @in: offset to write - * @len: length of data to be written - * - * Should be invoked after checking free space - */ -int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len) -{ - u32 space; - u32 len1; - - if ((in + len) < qsize) { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - if (copy_from_user((dst + in), src, len)) { - mif_err("ERR! copy_from_user fail\n", - CALLER); - return -EFAULT; - } - } else { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - - /* 1) space start (in) ~ buffer end */ - space = qsize - in; - len1 = (len > space) ? space : len; - if (copy_from_user((dst + in), src, len1)) { - mif_err("ERR! copy_from_user fail\n", - CALLER); - return -EFAULT; - } - - /* 2) buffer start ~ data end */ - if (len > len1) { - if (copy_from_user(dst, (src + space), (len - len1))) { - mif_err("ERR! copy_from_user " - "fail\n", CALLER); - return -EFAULT; - } - } - } - - return 0; -} - -/** - * print_mem_status - * @ld: pointer to an instance of link_device structure - * @mst: pointer to an instance of mem_status structure - * - * Prints a snapshot of the status of a SHM. - */ -void print_mem_status(struct link_device *ld, struct mem_status *mst) -{ - struct utc_time utc; - int us = ns2us(mst->ts.tv_nsec); - - ts2utc(&mst->ts, &utc); - pr_info("%s: %s: [%02d:%02d:%02d.%06d] " - "[%s] ACC{%X %d} " - "FMT{TI:%u TO:%u RI:%u RO:%u} " - "RAW{TI:%u TO:%u RI:%u RO:%u} " - "INTR{RX:0x%X TX:0x%X}\n", - MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, - get_dir_str(mst->dir), mst->magic, mst->access, - mst->head[IPC_FMT][TX], mst->tail[IPC_FMT][TX], - mst->head[IPC_FMT][RX], mst->tail[IPC_FMT][RX], - mst->head[IPC_RAW][TX], mst->tail[IPC_RAW][TX], - mst->head[IPC_RAW][RX], mst->tail[IPC_RAW][RX], - mst->int2ap, mst->int2cp); -} - -/** - * print_circ_status - * @ld: pointer to an instance of link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * - * Prints a snapshot of the status of a memory - */ -void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst) -{ - struct utc_time utc; - int us = ns2us(mst->ts.tv_nsec); - - if (dev > IPC_RAW) - return; - - ts2utc(&mst->ts, &utc); - pr_info("%s: %s: [%02d:%02d:%02d.%06d] " - "[%s] %s | TXQ{in:%u out:%u} RXQ{in:%u out:%u}\n", - MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, - get_dir_str(mst->dir), get_dev_name(dev), - mst->head[dev][TX], mst->tail[dev][TX], - mst->head[dev][RX], mst->tail[dev][RX]); -} - -/** - * print_ipc_trace - * @ld: pointer to an instance of link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @stat: pointer to an instance of circ_status structure - * @ts: pointer to an instance of timespec structure - * @buff: start address of a buffer into which RX IPC messages were copied - * @rcvd: size of data in the buffer - * - * Prints IPC messages in a local memory buffer to a kernel log. - */ -void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, - struct timespec *ts, u8 *buff, u32 rcvd) -{ - struct utc_time utc; - - ts2utc(ts, &utc); - - pr_info("%s: [%d-%02d-%02d %02d:%02d:%02d.%03d] " - "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", - MIF_TAG, utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec, - utc.msec, ld->name, get_dev_name(dev), stat->in, stat->out, - stat->size); - - mif_print_dump(buff, rcvd, 4); -} - -/** - * capture_mem_dump - * @ld: pointer to an instance of link_device structure - * @base: base virtual address to a memory interface medium - * @size: size of the memory interface medium - * - * Captures a dump for a memory interface medium. - * - * Returns the pointer to a memory dump buffer. - */ -u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size) -{ - u8 *buff = kzalloc(size, GFP_ATOMIC); - if (!buff) { - mif_err("%s: ERR! kzalloc(%d) fail\n", ld->name, size); - return NULL; - } else { - memcpy16_from_io(buff, base, size); - return buff; - } -} - -/** - * trq_get_free_slot - * @trq : pointer to an instance of trace_data_queue structure - * - * Succeeds always by dropping the oldest slot if a "trq" is full. - */ -struct trace_data *trq_get_free_slot(struct trace_data_queue *trq) -{ - int qsize = MAX_TRACE_SIZE; - int in; - int out; - unsigned long flags; - struct trace_data *trd; - - spin_lock_irqsave(&trq->lock, flags); - - in = trq->in; - out = trq->out; - - /* The oldest slot can be dropped. */ - if (circ_get_space(qsize, in, out) < 1) { - /* Free the data buffer in the oldest slot */ - trd = &trq->trd[out]; - kfree(trd->data); - - /* Make the oldest slot empty */ - out++; - trq->out = (out == qsize) ? 0 : out; - } - - /* Get a free slot and make it occupied */ - trd = &trq->trd[in++]; - trq->in = (in == qsize) ? 0 : in; - - spin_unlock_irqrestore(&trq->lock, flags); - - memset(trd, 0, sizeof(struct trace_data)); - - return trd; -} - -struct trace_data *trq_get_data_slot(struct trace_data_queue *trq) -{ - int qsize = MAX_TRACE_SIZE; - int in; - int out; - unsigned long flags; - struct trace_data *trd; - - spin_lock_irqsave(&trq->lock, flags); - - in = trq->in; - out = trq->out; - - if (circ_get_usage(qsize, in, out) < 1) { - spin_unlock_irqrestore(&trq->lock, flags); - return NULL; - } - - /* Get a data slot and make it empty */ - trd = &trq->trd[out++]; - trq->out = (out == qsize) ? 0 : out; - - spin_unlock_irqrestore(&trq->lock, flags); - - return trd; -} - diff --git a/drivers/misc/modem_if/modem_link_device_memory.h b/drivers/misc/modem_if/modem_link_device_memory.h deleted file mode 100644 index 9976b23..0000000 --- a/drivers/misc/modem_if/modem_link_device_memory.h +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (C) 2010 Samsung Electronics. - * - * 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 __MODEM_LINK_DEVICE_MEMORY_H__ -#define __MODEM_LINK_DEVICE_MEMORY_H__ - -#include -#include -#include -#include -#include -#if defined(CONFIG_HAS_EARLYSUSPEND) -#include -#elif defined(CONFIG_FB) -#include -#endif - -#include -#include "modem_prj.h" - -#define DPRAM_MAGIC_CODE 0xAA - -/* interrupt masks.*/ -#define INT_MASK_VALID 0x0080 -#define INT_MASK_CMD 0x0040 -#define INT_VALID(x) ((x) & INT_MASK_VALID) -#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) -#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) -#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) - -#define EXT_UDL_MASK 0xF000 -#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) -#define EXT_INT_VALID_MASK 0x8000 -#define EXT_CMD_VALID_MASK 0x4000 -#define UDL_CMD_VALID_MASK 0x2000 -#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) -#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) -#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) -#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) - -#define EXT_CMD_MASK(x) ((x) & 0x0FFF) -#define EXT_CMD_SET_SPEED_LOW 0x0011 -#define EXT_CMD_SET_SPEED_MID 0x0012 -#define EXT_CMD_SET_SPEED_HIGH 0x0013 - -#define UDL_RESULT_SUCCESS 0x1 -#define UDL_RESULT_FAIL 0x2 - -#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) -#define UDL_CMD_RECV_READY 0x1 -#define UDL_CMD_DL_START_REQ 0x2 -#define UDL_CMD_DL_START_RESP 0x3 -#define UDL_CMD_IMAGE_SEND_REQ 0x4 -#define UDL_CMD_SEND_DONE_RESP 0x5 -#define UDL_CMD_SEND_DONE_REQ 0x6 -#define UDL_CMD_UPDATE_DONE 0x7 -#define UDL_CMD_STATUS_UPDATE 0x8 -#define UDL_CMD_IMAGE_SEND_RESP 0x9 -#define UDL_CMD_EFS_CLEAR_RESP 0xB -#define UDL_CMD_ALARM_BOOT_OK 0xC -#define UDL_CMD_ALARM_BOOT_FAIL 0xD - -#define CMD_DL_READY 0xA100 -#define CMD_DL_START_REQ 0x9200 -#define CMD_DL_START_RESP 0xA301 -#define CMD_DL_SEND_REQ 0x9400 -#define CMD_DL_SEND_RESP 0xA501 -#define CMD_DL_DONE_REQ 0x9600 -#define CMD_DL_DONE_RESP 0xA701 - -#define CMD_UL_RECV_RESP 0x9601 -#define CMD_UL_RECV_DONE_REQ 0xA700 -#define CMD_UL_RECV_DONE_RESP 0x9801 - -/* special interrupt cmd indicating modem boot failure. */ -#define INT_POWERSAFE_FAIL 0xDEAD - -#define INT_MASK_REQ_ACK_F 0x0020 -#define INT_MASK_REQ_ACK_R 0x0010 -#define INT_MASK_RES_ACK_F 0x0008 -#define INT_MASK_RES_ACK_R 0x0004 -#define INT_MASK_SEND_F 0x0002 -#define INT_MASK_SEND_R 0x0001 - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_REQ_ACK_SET \ - (INT_MASK_REQ_ACK_F | INT_MASK_REQ_ACK_R | INT_MASK_REQ_ACK_RFS) - -#define INT_MASK_RES_ACK_SET \ - (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) - -#define INT_CMD_MASK(x) ((x) & 0xF) -#define INT_CMD_INIT_START 0x1 -#define INT_CMD_INIT_END 0x2 -#define INT_CMD_REQ_ACTIVE 0x3 -#define INT_CMD_RES_ACTIVE 0x4 -#define INT_CMD_REQ_TIME_SYNC 0x5 -#define INT_CMD_CRASH_RESET 0x7 -#define INT_CMD_PHONE_START 0x8 -#define INT_CMD_ERR_DISPLAY 0x9 -#define INT_CMD_CRASH_EXIT 0x9 -#define INT_CMD_CP_DEEP_SLEEP 0xA -#define INT_CMD_NV_REBUILDING 0xB -#define INT_CMD_EMER_DOWN 0xC -#define INT_CMD_PIF_INIT_DONE 0xD -#define INT_CMD_SILENT_NV_REBUILDING 0xE -#define INT_CMD_NORMAL_POWER_OFF 0xF - -/* AP_IDPRAM PM control command with QSC6085 */ -#define INT_CMD_IDPRAM_SUSPEND_REQ 0xD -#define INT_CMD_IDPRAM_SUSPEND_ACK 0xB -#define INT_CMD_IDPRAM_WAKEUP_START 0xE -#define INT_CMD_IDPRAM_RESUME_REQ 0xC - -#define START_FLAG 0x7F -#define END_FLAG 0x7E - -#define DP_MAGIC_DMDL 0x4445444C -#define DP_MAGIC_UMDL 0x4445444D -#define DP_DPRAM_SIZE 0x4000 -#define DP_DEFAULT_WRITE_LEN 8168 -#define DP_DEFAULT_DUMP_LEN 16128 -#define DP_DUMP_HEADER_SIZE 7 - -#define UDL_TIMEOUT (50 * HZ) -#define UDL_SEND_TIMEOUT (200 * HZ) -#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) -#define DUMP_TIMEOUT (30 * HZ) -#define DUMP_START_TIMEOUT (100 * HZ) -#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ - -#define IDPRAM_SUSPEND_REQ_TIMEOUT (50 * HZ) - -#define RES_ACK_WAIT_TIMEOUT 10 /* 10 ms */ -#define REQ_ACK_DELAY 10 /* 10 ms */ - -#ifdef DEBUG_MODEM_IF -#define MAX_RETRY_CNT 1 -#else -#define MAX_RETRY_CNT 3 -#endif - -#define MAX_SKB_TXQ_DEPTH 1024 - -struct memif_boot_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 __iomem *req; - u32 __iomem *resp; - u32 space; -}; - -struct memif_dload_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 space; -}; - -struct memif_uload_map { - u32 __iomem *magic; - u8 __iomem *cmd; - u32 cmd_size; - u8 __iomem *buff; - u32 space; -}; - -#define DP_BOOT_BUFF_OFFSET 4 -#define DP_DLOAD_MAGIC_SIZE 4 -#define DP_DLOAD_BUFF_OFFSET 4 -#define DP_ULOAD_MAGIC_SIZE 4 -#define DP_ULOAD_BUFF_OFFSET 4 -#define DP_BOOT_REQ_OFFSET 0 -#define DP_BOOT_RESP_OFFSET 8 -#define DP_MBX_SET_SIZE 4 -#define DP_MAX_PAYLOAD_SIZE 0x2000 - -enum circ_dir_type { - TX, - RX, - MAX_DIR, -}; - -enum circ_ptr_type { - HEAD, - TAIL, -}; - -static inline bool circ_valid(u32 qsize, u32 in, u32 out) -{ - if (in >= qsize) - return false; - - if (out >= qsize) - return false; - - return true; -} - -static inline u32 circ_get_space(u32 qsize, u32 in, u32 out) -{ - return (in < out) ? (out - in - 1) : (qsize + out - in - 1); -} - -static inline u32 circ_get_usage(u32 qsize, u32 in, u32 out) -{ - return (in >= out) ? (in - out) : (qsize - out + in); -} - -static inline u32 circ_new_pointer(u32 qsize, u32 p, u32 len) -{ - p += len; - return (p < qsize) ? p : (p - qsize); -} - -/** - * circ_read - * @dst: start address of the destination buffer - * @src: start address of the buffer in a circular queue - * @qsize: size of the circular queue - * @out: offset to read - * @len: length of data to be read - * - * Should be invoked after checking data length - */ -static inline void circ_read(void *dst, void *src, u32 qsize, u32 out, u32 len) -{ - unsigned len1; - - if ((out + len) <= qsize) { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - memcpy(dst, (src + out), len); - } else { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - - /* 1) data start (out) ~ buffer end */ - len1 = qsize - out; - memcpy(dst, (src + out), len1); - - /* 2) buffer start ~ data end (in?) */ - memcpy((dst + len1), src, (len - len1)); - } -} - -/** - * circ_write - * @dst: pointer to the start of the circular queue - * @src: pointer to the source - * @qsize: size of the circular queue - * @in: offset to write - * @len: length of data to be written - * - * Should be invoked after checking free space - */ -static inline void circ_write(void *dst, void *src, u32 qsize, u32 in, u32 len) -{ - u32 space; - - if ((in + len) < qsize) { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - memcpy((dst + in), src, len); - } else { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - - /* 1) space start (in) ~ buffer end */ - space = qsize - in; - memcpy((dst + in), src, ((len > space) ? space : len)); - - /* 2) buffer start ~ data end */ - if (len > space) - memcpy(dst, (src + space), (len - space)); - } -} - -/** - * circ_dir - * @dir: communication direction (enum circ_dir_type) - * - * Returns the direction of a circular queue - * - */ -static const inline char *circ_dir(enum circ_dir_type dir) -{ - if (dir == TX) - return "TXQ"; - else - return "RXQ"; -} - -/** - * circ_ptr - * @ptr: circular queue pointer (enum circ_ptr_type) - * - * Returns the name of a circular queue pointer - * - */ -static const inline char *circ_ptr(enum circ_ptr_type ptr) -{ - if (ptr == HEAD) - return "head"; - else - return "tail"; -} - -/** - * get_dir_str - * @dir: communication direction (enum circ_dir_type) - * - * Returns the direction of a circular queue - * - */ -static const inline char *get_dir_str(enum circ_dir_type dir) -{ - if (dir == TX) - return "AP->CP"; - else - return "CP->AP"; -} - -void memcpy16_from_io(const void *to, const void __iomem *from, u32 count); -void memcpy16_to_io(const void __iomem *to, const void *from, u32 count); -int memcmp16_to_io(const void __iomem *to, const void *from, u32 count); -void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len); -void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len); -int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len); -int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len); - -#define MAX_MEM_LOG_CNT 8192 -#define MAX_TRACE_SIZE 1024 - -struct mem_status { - /* Timestamp */ - struct timespec ts; - - /* Direction (TX or RX) */ - enum circ_dir_type dir; - - /* The status of memory interface at the time */ - u32 magic; - u32 access; - - u32 head[MAX_IPC_DEV][MAX_DIR]; - u32 tail[MAX_IPC_DEV][MAX_DIR]; - - u16 int2ap; - u16 int2cp; -}; - -struct mem_status_queue { - spinlock_t lock; - u32 in; - u32 out; - struct mem_status stat[MAX_MEM_LOG_CNT]; -}; - -struct circ_status { - u8 *buff; - u32 qsize; /* the size of a circular buffer */ - u32 in; - u32 out; - u32 size; /* the size of free space or received data */ -}; - -struct trace_data { - struct timespec ts; - enum dev_format dev; - struct circ_status circ_stat; - u8 *data; - u32 size; -}; - -struct trace_data_queue { - spinlock_t lock; - u32 in; - u32 out; - struct trace_data trd[MAX_TRACE_SIZE]; -}; - -struct mem_status *msq_get_free_slot(struct mem_status_queue *msq); -struct mem_status *msq_get_data_slot(struct mem_status_queue *msq); - -void print_mem_status(struct link_device *ld, struct mem_status *mst); -void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst); -void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, - struct timespec *ts, u8 *buff, u32 rcvd); - -u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size); -struct trace_data *trq_get_free_slot(struct trace_data_queue *trq); -struct trace_data *trq_get_data_slot(struct trace_data_queue *trq); - -#endif - diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c index b8b6a3b..b68040e 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.c +++ b/drivers/misc/modem_if/modem_link_device_pld.c @@ -25,76 +25,190 @@ #include #include #include - #include + #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" + /* ** Function prototypes for basic DPRAM operations */ -static inline void clear_intr(struct pld_link_device *pld); -static inline u16 recv_intr(struct pld_link_device *pld); -static inline void send_intr(struct pld_link_device *pld, u16 mask); - -static inline u16 get_magic(struct pld_link_device *pld); -static inline void set_magic(struct pld_link_device *pld, u16 val); -static inline u16 get_access(struct pld_link_device *pld); -static inline void set_access(struct pld_link_device *pld, u16 val); - -static inline u32 get_tx_head(struct pld_link_device *pld, int id); -static inline u32 get_tx_tail(struct pld_link_device *pld, int id); -static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in); -static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out); -static inline u8 *get_tx_buff(struct pld_link_device *pld, int id); -static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id); - -static inline u32 get_rx_head(struct pld_link_device *pld, int id); -static inline u32 get_rx_tail(struct pld_link_device *pld, int id); -static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in); -static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out); -static inline u8 *get_rx_buff(struct pld_link_device *pld, int id); -static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id); - -static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id); -static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id); -static inline u16 get_mask_send(struct pld_link_device *pld, int id); - -static void handle_cp_crash(struct pld_link_device *pld); -static int trigger_force_cp_crash(struct pld_link_device *pld); +static inline void clear_intr(struct dpram_link_device *dpld); +static inline u16 recv_intr(struct dpram_link_device *dpld); +static inline void send_intr(struct dpram_link_device *dpld, u16 mask); + +static inline u16 get_magic(struct dpram_link_device *dpld); +static inline void set_magic(struct dpram_link_device *dpld, u16 val); +static inline u16 get_access(struct dpram_link_device *dpld); +static inline void set_access(struct dpram_link_device *dpld, u16 val); + +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); + +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); + +static void handle_cp_crash(struct dpram_link_device *dpld); +static int trigger_force_cp_crash(struct dpram_link_device *dpld); /* ** Functions for debugging */ -static void set_dpram_map(struct pld_link_device *pld, +static inline void log_dpram_status(struct dpram_link_device *dpld) +{ + pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " + "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", + dpld->ld.mc->name, + get_magic(dpld), get_access(dpld), + get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), + get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), + get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), + get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), + recv_intr(dpld)); +} + +static void set_dpram_map(struct dpram_link_device *dpld, struct mif_irq_map *map) { - map->magic = get_magic(pld); - map->access = get_access(pld); + map->magic = get_magic(dpld); + map->access = get_access(dpld); + + map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); + map->raw_tx_in = get_tx_head(dpld, IPC_RAW); + map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rx_head(dpld, IPC_RAW); + map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); + + map->cp2ap = recv_intr(dpld); +} + +/* +** RXB (DPRAM RX buffer) functions +*/ +static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) +{ + struct dpram_rxb *rxb; + u8 *buff; + int i; + + rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); + if (!rxb) { + mif_info("ERR! kzalloc rxb fail\n"); + return NULL; + } + + buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); + if (!buff) { + mif_info("ERR! kzalloc buff fail\n"); + kfree(rxb); + return NULL; + } + + for (i = 0; i < count; i++) { + rxb[i].buff = buff; + rxb[i].size = size; + buff += size; + } + + return rxb; +} + +static inline unsigned rxbq_get_page_size(unsigned len) +{ + return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; +} + +static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) +{ + return (rxbq->in == rxbq->out) ? true : false; +} + +static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) +{ + struct dpram_rxb *rxb = NULL; + + if (likely(rxbq_free_size(rxbq) > 0)) { + rxb = &rxbq->rxb[rxbq->in]; + rxbq->in++; + if (rxbq->in >= rxbq->size) + rxbq->in -= rxbq->size; + rxb->data = rxb->buff; + } + + return rxb; +} + +static inline int rxbq_size(struct dpram_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in >= out) ? (in - out) : (qsize - out + in); +} - map->fmt_tx_in = get_tx_head(pld, IPC_FMT); - map->fmt_tx_out = get_tx_tail(pld, IPC_FMT); - map->fmt_rx_in = get_rx_head(pld, IPC_FMT); - map->fmt_rx_out = get_rx_tail(pld, IPC_FMT); - map->raw_tx_in = get_tx_head(pld, IPC_RAW); - map->raw_tx_out = get_tx_tail(pld, IPC_RAW); - map->raw_rx_in = get_rx_head(pld, IPC_RAW); - map->raw_rx_out = get_rx_tail(pld, IPC_RAW); +static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) +{ + struct dpram_rxb *rxb = NULL; - map->cp2ap = recv_intr(pld); + if (likely(!rxbq_empty(rxbq))) { + rxb = &rxbq->rxb[rxbq->out]; + rxbq->out++; + if (rxbq->out >= rxbq->size) + rxbq->out -= rxbq->size; + } + + return rxb; +} + +static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) +{ + rxb->len = len; + return rxb->data; +} + +static inline void rxb_clear(struct dpram_rxb *rxb) +{ + rxb->data = NULL; + rxb->len = 0; } /* ** DPRAM operations */ -static int pld_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), +static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), unsigned long flag, const char *name, - struct pld_link_device *pld) + struct dpram_link_device *dpld) { int ret = 0; - ret = request_irq(irq, isr, flag, name, pld); + ret = request_irq(irq, isr, flag, name, dpld); if (ret) { mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); return ret; @@ -109,31 +223,31 @@ static int pld_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), return 0; } -static inline void clear_intr(struct pld_link_device *pld) +static inline void clear_intr(struct dpram_link_device *dpld) { - if (pld->ext_op && pld->ext_op->clear_intr) - pld->ext_op->clear_intr(pld); + if (dpld->dpctl->clear_intr) + dpld->dpctl->clear_intr(); } -static inline u16 recv_intr(struct pld_link_device *pld) +static inline u16 recv_intr(struct dpram_link_device *dpld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -141,31 +255,31 @@ static inline u16 recv_intr(struct pld_link_device *pld) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline void send_intr(struct pld_link_device *pld, u16 mask) +static inline void send_intr(struct dpram_link_device *dpld, u16 mask) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), - pld->address_buffer); - iowrite16((u16)mask, pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), + dpld->address_buffer); + iowrite16((u16)mask, dpld->dp_base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), - pld->address_buffer); - val = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); if (likely(val == mask)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } @@ -173,35 +287,35 @@ static inline void send_intr(struct pld_link_device *pld, u16 mask) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), - pld->address_buffer); - iowrite16((u16)mask, pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), + dpld->address_buffer); + iowrite16((u16)mask, dpld->dp_base); } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } -static inline u16 get_magic(struct pld_link_device *pld) +static inline u16 get_magic(struct dpram_link_device *dpld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -210,31 +324,31 @@ static inline u16 get_magic(struct pld_link_device *pld) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline void set_magic(struct pld_link_device *pld, u16 in) +static inline void set_magic(struct dpram_link_device *dpld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), - pld->address_buffer); - iowrite16((u16)in, pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), - pld->address_buffer); - val = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); if (likely(val == in)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } @@ -242,34 +356,34 @@ static inline void set_magic(struct pld_link_device *pld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), - pld->address_buffer); - iowrite16((u16)in, pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } -static inline u16 get_access(struct pld_link_device *pld) +static inline u16 get_access(struct dpram_link_device *dpld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -278,31 +392,31 @@ static inline u16 get_access(struct pld_link_device *pld) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline void set_access(struct pld_link_device *pld, u16 in) +static inline void set_access(struct dpram_link_device *dpld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), - pld->address_buffer); - iowrite16((u16)in, pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), - pld->address_buffer); - val = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); if (likely(val == in)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } @@ -310,34 +424,34 @@ static inline void set_access(struct pld_link_device *pld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), - pld->address_buffer); - iowrite16((u16)in, pld->base); + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } -static inline u32 get_tx_head(struct pld_link_device *pld, int id) +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -347,29 +461,29 @@ static inline u32 get_tx_head(struct pld_link_device *pld, int id) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline u32 get_tx_tail(struct pld_link_device *pld, int id) +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -379,30 +493,30 @@ static inline u32 get_tx_tail(struct pld_link_device *pld, int id) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in) +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), - pld->address_buffer); - iowrite16((u16)in, pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), - pld->address_buffer); - val = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); if (likely(val == in)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } @@ -411,49 +525,49 @@ static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), - pld->address_buffer); - iowrite16((u16)in, pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } -static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out) +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) { return; } -static inline u8 *get_tx_buff(struct pld_link_device *pld, int id) +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) { - return pld->dev[id]->txq.buff; + return dpld->dev[id]->txq.buff; } -static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id) +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) { - return pld->dev[id]->txq.size; + return dpld->dev[id]->txq.size; } -static inline u32 get_rx_head(struct pld_link_device *pld, int id) +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -463,29 +577,29 @@ static inline u32 get_rx_head(struct pld_link_device *pld, int id) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline u32 get_rx_tail(struct pld_link_device *pld, int id) +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), - pld->address_buffer); - val1 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), - pld->address_buffer); - val2 = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } @@ -495,35 +609,35 @@ static inline u32 get_rx_tail(struct pld_link_device *pld, int id) } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return val1; } -static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in) +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) { return; } -static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out) +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&pld->pld_lock, flags); + spin_lock_irqsave(&dpld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), - pld->address_buffer); - iowrite16((u16)out, pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + iowrite16((u16)out, dpld->dp_base); do { /* Check tail value written */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), - pld->address_buffer); - val = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); if (val == out) { - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } @@ -532,60 +646,71 @@ static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out) udelay(100); /* Write tail value again */ - iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), - pld->address_buffer); - iowrite16((u16)out, pld->base); + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + iowrite16((u16)out, dpld->dp_base); } while (cnt--); - spin_unlock_irqrestore(&pld->pld_lock, flags); + spin_unlock_irqrestore(&dpld->pld_lock, flags); return; } -static inline u8 *get_rx_buff(struct pld_link_device *pld, int id) +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->rxq.buff; +} + +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) { - return pld->dev[id]->rxq.buff; + return dpld->dev[id]->rxq.size; } -static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id) +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) { - return pld->dev[id]->rxq.size; + return dpld->dev[id]->mask_req_ack; } -static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id) +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) { - return pld->dev[id]->mask_req_ack; + return dpld->dev[id]->mask_res_ack; } -static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id) +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) { - return pld->dev[id]->mask_res_ack; + return dpld->dev[id]->mask_send; } -static inline u16 get_mask_send(struct pld_link_device *pld, int id) +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) { - return pld->dev[id]->mask_send; + if (in >= size) + return false; + + if (out >= size) + return false; + + return true; } /* Get free space in the TXQ as well as in & out pointers */ -static inline int get_txq_space(struct pld_link_device *pld, int dev, u32 qsize, - u32 *in, u32 *out) +static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; int cnt = 3; u32 head; u32 tail; int space; do { - head = get_tx_head(pld, dev); - tail = get_tx_tail(pld, dev); + head = get_tx_head(dpld, dev); + tail = get_tx_tail(dpld, dev); space = (head < tail) ? (tail - head - 1) : (qsize + tail - head - 1); mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", ld->name, get_dev_name(dev), qsize, head, tail, space); - if (circ_valid(qsize, head, tail)) { + if (dpram_circ_valid(qsize, head, tail)) { *in = head; *out = tail; return space; @@ -604,27 +729,27 @@ static inline int get_txq_space(struct pld_link_device *pld, int dev, u32 qsize, return -EINVAL; } -static void reset_tx_circ(struct pld_link_device *pld, int dev) +static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) { - set_tx_head(pld, dev, 0); - set_tx_tail(pld, dev, 0); + set_tx_head(dpld, dev, 0); + set_tx_tail(dpld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(pld); + trigger_force_cp_crash(dpld); } /* Get data size in the RXQ as well as in & out pointers */ -static inline int get_rxq_rcvd(struct pld_link_device *pld, int dev, u32 qsize, - u32 *in, u32 *out) +static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; int cnt = 3; u32 head; u32 tail; u32 rcvd; do { - head = get_rx_head(pld, dev); - tail = get_rx_tail(pld, dev); + head = get_rx_head(dpld, dev); + tail = get_rx_tail(dpld, dev); if (head == tail) { *in = head; *out = tail; @@ -635,7 +760,7 @@ static inline int get_rxq_rcvd(struct pld_link_device *pld, int dev, u32 qsize, mif_info("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", ld->name, get_dev_name(dev), qsize, head, tail, rcvd); - if (circ_valid(qsize, head, tail)) { + if (dpram_circ_valid(qsize, head, tail)) { *in = head; *out = tail; return rcvd; @@ -654,20 +779,54 @@ static inline int get_rxq_rcvd(struct pld_link_device *pld, int dev, u32 qsize, return -EINVAL; } -static void reset_rx_circ(struct pld_link_device *pld, int dev) +static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) { - set_rx_head(pld, dev, 0); - set_rx_tail(pld, dev, 0); + set_rx_head(dpld, dev, 0); + set_rx_tail(dpld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(pld); + trigger_force_cp_crash(dpld); } -static int check_access(struct pld_link_device *pld) +/* +** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success +*/ +static int dpram_wake_up(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; + + if (!dpld->dpctl->wakeup) + return 0; + + if (dpld->dpctl->wakeup() < 0) { + mif_err("%s: ERR! <%pf> DPRAM wakeup fail\n", + ld->name, __builtin_return_address(0)); + return -EACCES; + } + + atomic_inc(&dpld->accessing); + return 0; +} + +static void dpram_allow_sleep(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (!dpld->dpctl->sleep) + return; + + if (atomic_dec_return(&dpld->accessing) <= 0) { + dpld->dpctl->sleep(); + atomic_set(&dpld->accessing, 0); + mif_debug("%s: DPRAM sleep possible\n", ld->name); + } +} + +static int dpram_check_access(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; int i; - u16 magic = get_magic(pld); - u16 access = get_access(pld); + u16 magic = get_magic(dpld); + u16 access = get_access(dpld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; @@ -677,8 +836,8 @@ static int check_access(struct pld_link_device *pld) ld->name, magic, access, i); udelay(100); - magic = get_magic(pld); - access = get_access(pld); + magic = get_magic(dpld); + access = get_access(dpld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; } @@ -687,9 +846,9 @@ static int check_access(struct pld_link_device *pld) return -EACCES; } -static bool ipc_active(struct pld_link_device *pld) +static bool dpram_ipc_active(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { @@ -698,8 +857,8 @@ static bool ipc_active(struct pld_link_device *pld) return false; } - if (check_access(pld) < 0) { - mif_info("%s: ERR! <%pf> check_access fail\n", + if (dpram_check_access(dpld) < 0) { + mif_info("%s: ERR! <%pf> dpram_check_access fail\n", ld->name, __builtin_return_address(0)); return false; } @@ -707,72 +866,67 @@ static bool ipc_active(struct pld_link_device *pld) return true; } -static void pld_ipc_write(struct pld_link_device *pld, int dev, +static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, u32 qsize, u32 in, u32 out, struct sk_buff *skb) { - struct link_device *ld = &pld->ld; - u8 __iomem *buff = get_tx_buff(pld, dev); + struct link_device *ld = &dpld->ld; + u8 __iomem *buff = get_tx_buff(dpld, dev); u8 *src = skb->data; u32 len = skb->len; u32 inp; struct mif_irq_map map; - unsigned long int flags; - - spin_lock_irqsave(&pld->pld_lock, flags); if (in < out) { /* +++++++++ in ---------- out ++++++++++ */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); - memcpy(pld->base, src, len); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); + memcpy(dpld->dp_base, src, len); } else { /* ------ out +++++++++++ in ------------ */ u32 space = qsize - in; /* 1) in -> buffer end */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); - memcpy(pld->base, src, ((len > space) ? space : len)); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); + memcpy(dpld->dp_base, src, ((len > space) ? space : len)); if (len > space) { iowrite16(PLD_ADDR_MASK(&buff[0]), - pld->address_buffer); - memcpy(pld->base, (src+space), (len-space)); + dpld->address_buffer); + memcpy(dpld->dp_base, (src+space), (len-space)); } } - spin_unlock_irqrestore(&pld->pld_lock, flags); - /* update new in pointer */ inp = in + len; if (inp >= qsize) inp -= qsize; - set_tx_head(pld, dev, inp); + set_tx_head(dpld, dev, inp); if (dev == IPC_FMT) { - set_dpram_map(pld, &map); + set_dpram_map(dpld, &map); mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); } } -static int pld_try_ipc_tx(struct pld_link_device *pld, int dev) +static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; struct sk_buff_head *txq = ld->skb_txq[dev]; struct sk_buff *skb; unsigned long int flags; - u32 qsize = get_tx_buff_size(pld, dev); + u32 qsize = get_tx_buff_size(dpld, dev); u32 in; u32 out; int space; int copied = 0; - spin_lock_irqsave(&pld->tx_rx_lock, flags); + spin_lock_irqsave(&dpld->tx_rx_lock, flags); while (1) { - space = get_txq_space(pld, dev, qsize, &in, &out); + space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); if (unlikely(space < 0)) { - spin_unlock_irqrestore(&pld->tx_rx_lock, flags); - reset_tx_circ(pld, dev); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + dpram_reset_tx_circ(dpld, dev); return space; } @@ -781,9 +935,9 @@ static int pld_try_ipc_tx(struct pld_link_device *pld, int dev) break; if (unlikely(space < skb->len)) { - atomic_set(&pld->res_required[dev], 1); + atomic_set(&dpld->res_required[dev], 1); skb_queue_head(txq, skb); - spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); mif_info("%s: %s " "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", ld->name, get_dev_name(dev), @@ -792,30 +946,30 @@ static int pld_try_ipc_tx(struct pld_link_device *pld, int dev) } /* TX if there is enough room in the queue */ - pld_ipc_write(pld, dev, qsize, in, out, skb); + dpram_ipc_write(dpld, dev, qsize, in, out, skb); copied += skb->len; dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); return copied; } -static void pld_ipc_rx_task(unsigned long data) +static void dpram_ipc_rx_task(unsigned long data) { - struct pld_link_device *pld = (struct pld_link_device *)data; - struct link_device *ld = &pld->ld; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = &dpld->ld; struct io_device *iod; - struct mif_rxb *rxb; + struct dpram_rxb *rxb; unsigned qlen; int i; - for (i = 0; i < ld->max_ipc_dev; i++) { - iod = pld->iod[i]; - qlen = rxbq_size(&pld->rxbq[i]); + for (i = 0; i < dpld->max_ipc_dev; i++) { + iod = dpld->iod[i]; + qlen = rxbq_size(&dpld->rxbq[i]); while (qlen > 0) { - rxb = rxbq_get_data_rxb(&pld->rxbq[i]); + rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); iod->recv(iod, ld, rxb->data, rxb->len); rxb_clear(rxb); qlen--; @@ -823,39 +977,36 @@ static void pld_ipc_rx_task(unsigned long data) } } -static void pld_ipc_read(struct pld_link_device *pld, int dev, u8 *dst, +static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, u8 __iomem *src, u32 out, u32 len, u32 qsize) { u8 *ori_det = dst; unsigned long flags; - spin_lock_irqsave(&pld->pld_lock, flags); - if ((out + len) <= qsize) { /* ----- (out) (in) ----- */ /* ----- 7f 00 00 7e ----- */ - iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); - memcpy(dst, pld->base, len); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); + memcpy(dst, dpld->dp_base, len); } else { /* (in) ----------- (out) */ /* 00 7e ----------- 7f 00 */ unsigned len1 = qsize - out; /* 1) out -> buffer end */ - iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); - memcpy(dst, pld->base, len1); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); + memcpy(dst, dpld->dp_base, len1); /* 2) buffer start -> in */ dst += len1; - iowrite16(PLD_ADDR_MASK(&src[0]), pld->address_buffer); - memcpy(dst, pld->base, (len - len1)); + iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer); + memcpy(dst, dpld->dp_base, (len - len1)); } - spin_unlock_irqrestore(&pld->pld_lock, flags); - if (pld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { + if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { mif_info("ipc read error!! in[%d], out[%d]\n", - get_rx_head(pld, dev), - get_rx_tail(pld, dev)); + get_rx_head(dpld, dev), + get_rx_tail(dpld, dev)); } } @@ -865,106 +1016,190 @@ static void pld_ipc_read(struct pld_link_device *pld, int dev, u8 *dst, ret == 0 : no data ret > 0 : valid data */ -static int pld_ipc_recv_data_with_rxb(struct pld_link_device *pld, int dev) +static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) { - struct link_device *ld = &pld->ld; - struct mif_rxb *rxb; - u8 __iomem *src = get_rx_buff(pld, dev); - u32 qsize = get_rx_buff_size(pld, dev); + struct link_device *ld = &dpld->ld; + struct dpram_rxb *rxb; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); u32 in; u32 out; u32 rcvd; struct mif_irq_map map; unsigned long int flags; - spin_lock_irqsave(&pld->tx_rx_lock, flags); + spin_lock_irqsave(&dpld->tx_rx_lock, flags); - rcvd = get_rxq_rcvd(pld, dev, qsize, &in, &out); + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); if (rcvd <= 0) { - spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); return rcvd; } if (dev == IPC_FMT) { - set_dpram_map(pld, &map); + set_dpram_map(dpld, &map); mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } /* Allocate an rxb */ - rxb = rxbq_get_free_rxb(&pld->rxbq[dev]); + rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); if (!rxb) { mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", ld->name, get_dev_name(dev)); - spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); return -ENOMEM; } /* Read data from each DPRAM buffer */ - pld_ipc_read(pld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); + dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); /* Calculate and set new out */ out += rcvd; if (out >= qsize) out -= qsize; - set_rx_tail(pld, dev, out); + set_rx_tail(dpld, dev, out); + + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + return rcvd; +} + +/* + ret < 0 : error + ret == 0 : no data + ret > 0 : valid data +*/ +static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod = dpld->iod[dev]; + struct sk_buff *skb; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + int rest; + u8 *frm; + u8 *dst; + unsigned int len; + unsigned int pad; + unsigned int tot; + struct mif_irq_map map; + + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) + return rcvd; + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + + rest = rcvd; + while (rest > 0) { + frm = src + out; + if (unlikely(!sipc5_start_valid(frm[0]))) { + mif_err("%s: ERR! %s invalid start 0x%02X\n", + ld->name, get_dev_name(dev), frm[0]); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + len = sipc5_get_frame_sz16(frm); + if (unlikely(len > rest)) { + mif_err("%s: ERR! %s len %d > rest %d\n", + ld->name, get_dev_name(dev), len, rest); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + pad = sipc5_calc_padding_size(len); + tot = len + pad; + + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + return -ENOMEM; + } + + /* Read data from each DPRAM buffer */ + dst = skb_put(skb, tot); + dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); + skb_trim(skb, len); + iod->recv_skb(iod, ld, skb); + + /* Calculate and set new out */ + rest -= tot; + out += tot; + if (out >= qsize) + out -= qsize; + } + + set_rx_tail(dpld, dev, out); - spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } -static void non_command_handler(struct pld_link_device *pld, u16 non_cmd) +static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; int i = 0; int ret = 0; - u16 mask = 0; + u16 tx_mask = 0; - if (!ipc_active(pld)) + if (!dpram_ipc_active(dpld)) return; /* Read data from DPRAM */ - for (i = 0; i < ld->max_ipc_dev; i++) { - ret = pld_ipc_recv_data_with_rxb(pld, i); + for (i = 0; i < dpld->max_ipc_dev; i++) { + if (dpld->use_skb) + ret = dpram_ipc_recv_data_with_skb(dpld, i); + else + ret = dpram_ipc_recv_data_with_rxb(dpld, i); if (ret < 0) - reset_rx_circ(pld, i); + dpram_reset_rx_circ(dpld, i); /* Check and process REQ_ACK (at this time, in == out) */ - if (non_cmd & get_mask_req_ack(pld, i)) { + if (non_cmd & get_mask_req_ack(dpld, i)) { mif_debug("%s: send %s_RES_ACK\n", ld->name, get_dev_name(i)); - mask |= get_mask_res_ack(pld, i); + tx_mask |= get_mask_res_ack(dpld, i); } } - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&pld->rx_tsk); + if (!dpld->use_skb) { + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&dpld->rx_tsk); + } /* Try TX via DPRAM */ - for (i = 0; i < ld->max_ipc_dev; i++) { - if (atomic_read(&pld->res_required[i]) > 0) { - ret = pld_try_ipc_tx(pld, i); + for (i = 0; i < dpld->max_ipc_dev; i++) { + if (atomic_read(&dpld->res_required[i]) > 0) { + ret = dpram_try_ipc_tx(dpld, i); if (ret > 0) { - atomic_set(&pld->res_required[i], 0); - mask |= get_mask_send(pld, i); + atomic_set(&dpld->res_required[i], 0); + tx_mask |= get_mask_send(dpld, i); } else if (ret == -ENOSPC) { - mask |= get_mask_req_ack(pld, i); + tx_mask |= get_mask_req_ack(dpld, i); } } } - if (mask) { - send_intr(pld, INT_NON_CMD(mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, mask); + if (tx_mask) { + send_intr(dpld, INT_NON_CMD(tx_mask)); + mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); } } -static void handle_cp_crash(struct pld_link_device *pld) +static void handle_cp_crash(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; struct io_device *iod; int i; - for (i = 0; i < ld->max_ipc_dev; i++) { + for (i = 0; i < dpld->max_ipc_dev; i++) { mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); skb_queue_purge(ld->skb_txq[i]); } @@ -975,27 +1210,27 @@ static void handle_cp_crash(struct pld_link_device *pld) iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_EXIT); - iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); if (iod) iodevs_for_each(iod->msd, iodev_netif_stop, 0); } static void handle_no_crash_ack(unsigned long arg) { - struct pld_link_device *pld = (struct pld_link_device *)arg; - struct link_device *ld = &pld->ld; + struct dpram_link_device *dpld = (struct dpram_link_device *)arg; + struct link_device *ld = &dpld->ld; mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name); - if (!wake_lock_active(&pld->wlock)) - wake_lock(&pld->wlock); + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); - handle_cp_crash(pld); + handle_cp_crash(dpld); } -static int trigger_force_cp_crash(struct pld_link_device *pld) +static int trigger_force_cp_crash(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; if (ld->mode == LINK_MODE_ULOAD) { mif_err("%s: CP crash is already in progress\n", ld->mc->name); @@ -1005,72 +1240,79 @@ static int trigger_force_cp_crash(struct pld_link_device *pld) ld->mode = LINK_MODE_ULOAD; mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); - send_intr(pld, INT_CMD(INT_CMD_CRASH_EXIT)); + dpram_wake_up(dpld); - mif_add_timer(&pld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)pld); + send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + + mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_crash_ack, (unsigned long)dpld); return 0; } -static int pld_init_ipc(struct pld_link_device *pld) +static int dpram_init_ipc(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; int i; if (ld->mode == LINK_MODE_IPC && - get_magic(pld) == DPRAM_MAGIC_CODE && - get_access(pld) == 1) + get_magic(dpld) == DPRAM_MAGIC_CODE && + get_access(dpld) == 1) mif_info("%s: IPC already initialized\n", ld->name); /* Clear pointers in every circular queue */ - for (i = 0; i < ld->max_ipc_dev; i++) { - set_tx_head(pld, i, 0); - set_tx_tail(pld, i, 0); - set_rx_head(pld, i, 0); - set_rx_tail(pld, i, 0); + for (i = 0; i < dpld->max_ipc_dev; i++) { + set_tx_head(dpld, i, 0); + set_tx_tail(dpld, i, 0); + set_rx_head(dpld, i, 0); + set_rx_tail(dpld, i, 0); } /* Initialize variables for efficient TX/RX processing */ - for (i = 0; i < ld->max_ipc_dev; i++) - pld->iod[i] = link_get_iod_with_format(ld, i); - pld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->iod[i] = link_get_iod_with_format(ld, i); + dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + + if (dpld->iod[IPC_RAW]->recv_skb) + dpld->use_skb = true; - for (i = 0; i < ld->max_ipc_dev; i++) - atomic_set(&pld->res_required[i], 0); + for (i = 0; i < dpld->max_ipc_dev; i++) { + atomic_set(&dpld->res_required[i], 0); + skb_queue_purge(&dpld->skb_rxq[i]); + } - spin_lock_init(&pld->tx_rx_lock); + spin_lock_init(&dpld->tx_rx_lock); /* Enable IPC */ - atomic_set(&pld->accessing, 0); + atomic_set(&dpld->accessing, 0); - set_magic(pld, DPRAM_MAGIC_CODE); - set_access(pld, 1); - if (get_magic(pld) != DPRAM_MAGIC_CODE || get_access(pld) != 1) + set_magic(dpld, DPRAM_MAGIC_CODE); + set_access(dpld, 1); + if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) return -EACCES; ld->mode = LINK_MODE_IPC; - if (wake_lock_active(&pld->wlock)) - wake_unlock(&pld->wlock); + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); return 0; } -static void cmd_req_active_handler(struct pld_link_device *pld) +static void cmd_req_active_handler(struct dpram_link_device *dpld) { - send_intr(pld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); } -static void cmd_crash_reset_handler(struct pld_link_device *pld) +static void cmd_crash_reset_handler(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&pld->wlock)) - wake_lock(&pld->wlock); + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); @@ -1081,33 +1323,35 @@ static void cmd_crash_reset_handler(struct pld_link_device *pld) iod->modem_state_changed(iod, STATE_CRASH_RESET); } -static void cmd_crash_exit_handler(struct pld_link_device *pld) +static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&pld->wlock)) - wake_lock(&pld->wlock); + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - del_timer(&pld->crash_ack_timer); + dpram_wake_up(dpld); - if (pld->ext_op && pld->ext_op->crash_log) - pld->ext_op->crash_log(pld); + del_timer(&dpld->crash_ack_timer); - handle_cp_crash(pld); + if (dpld->ext_op && dpld->ext_op->crash_log) + dpld->ext_op->crash_log(dpld); + + handle_cp_crash(dpld); } -static void cmd_phone_start_handler(struct pld_link_device *pld) +static void cmd_phone_start_handler(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); - pld_init_ipc(pld); + dpram_init_ipc(dpld); iod = link_get_iod_with_format(ld, IPC_FMT); if (!iod) { @@ -1115,8 +1359,8 @@ static void cmd_phone_start_handler(struct pld_link_device *pld) return; } - if (pld->ext_op && pld->ext_op->cp_start_handler) - pld->ext_op->cp_start_handler(pld); + if (dpld->ext_op && dpld->ext_op->cp_start_handler) + dpld->ext_op->cp_start_handler(dpld); if (ld->mc->phone_state != STATE_ONLINE) { mif_info("%s: phone_state: %d -> ONLINE\n", @@ -1125,32 +1369,32 @@ static void cmd_phone_start_handler(struct pld_link_device *pld) } mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_intr(pld, INT_CMD(INT_CMD_INIT_END)); + send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); } -static void command_handler(struct pld_link_device *pld, u16 cmd) +static void command_handler(struct dpram_link_device *dpld, u16 cmd) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; switch (INT_CMD_MASK(cmd)) { case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(pld); + cmd_req_active_handler(dpld); break; case INT_CMD_CRASH_RESET: - pld->init_status = PLD_INIT_STATE_NONE; - cmd_crash_reset_handler(pld); + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + cmd_crash_reset_handler(dpld); break; case INT_CMD_CRASH_EXIT: - pld->init_status = PLD_INIT_STATE_NONE; - cmd_crash_exit_handler(pld); + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + cmd_crash_exit_handler(dpld); break; case INT_CMD_PHONE_START: - pld->init_status = PLD_INIT_STATE_READY; - cmd_phone_start_handler(pld); - complete_all(&pld->dpram_init_cmd); + dpld->dpram_init_status = DPRAM_INIT_STATE_READY; + cmd_phone_start_handler(dpld); + complete_all(&dpld->dpram_init_cmd); break; case INT_CMD_NV_REBUILDING: @@ -1158,14 +1402,14 @@ static void command_handler(struct pld_link_device *pld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&pld->modem_pif_init_done); + complete_all(&dpld->modem_pif_init_done); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_POWER_OFF: + case INT_CMD_NORMAL_PWR_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1180,46 +1424,123 @@ static void command_handler(struct pld_link_device *pld, u16 cmd) } } -static irqreturn_t pld_irq_handler(int irq, void *data) +static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) { - struct pld_link_device *pld = (struct pld_link_device *)data; - struct link_device *ld = (struct link_device *)&pld->ld; + struct link_device *ld = &dpld->ld; + u16 resp; + + switch (EXT_CMD_MASK(cmd)) { + case EXT_CMD_SET_SPEED_LOW: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); + send_intr(dpld, resp); + } + break; + + case EXT_CMD_SET_SPEED_MID: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_MID); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); + send_intr(dpld, resp); + } + break; + + case EXT_CMD_SET_SPEED_HIGH: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); + send_intr(dpld, resp); + } + break; + + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + break; + } +} + +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; + + if (cmd & UDL_RESULT_FAIL) { + mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); + return; + } + + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); + send_intr(dpld, CMD_IMG_START_REQ); + break; + default: + complete_all(&dpld->udl_cmd_complete); + } +} + +static irqreturn_t dpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; u16 int2ap = 0; if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - int2ap = recv_intr(pld); + if (dpram_wake_up(dpld) < 0) { + log_dpram_status(dpld); + trigger_force_cp_crash(dpld); + return IRQ_HANDLED; + } + + int2ap = recv_intr(dpld); if (unlikely(int2ap == INT_POWERSAFE_FAIL)) { mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); goto exit; } else if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { - if (pld->ext_op && pld->ext_op->dload_cmd_handler) { - pld->ext_op->dload_cmd_handler(pld, int2ap); + if (dpld->ext_op && dpld->ext_op->dload_cmd_handler) { + dpld->ext_op->dload_cmd_handler(dpld, int2ap); goto exit; } } - if (likely(INT_VALID(int2ap))) { - if (unlikely(INT_CMD_VALID(int2ap))) - command_handler(pld, int2ap); - else - non_command_handler(pld, int2ap); + if (unlikely(EXT_UDL_CMD(int2ap))) { + if (likely(EXT_INT_VALID(int2ap))) { + if (UDL_CMD_VALID(int2ap)) + udl_command_handler(dpld, int2ap); + else if (EXT_CMD_VALID(int2ap)) + ext_command_handler(dpld, int2ap); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); + } else { + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); + } } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); + if (likely(INT_VALID(int2ap))) { + if (unlikely(INT_CMD_VALID(int2ap))) + command_handler(dpld, int2ap); + else + non_command_handler(dpld, int2ap); + } else { + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); + } } exit: - clear_intr(pld); + clear_intr(dpld); + dpram_allow_sleep(dpld); return IRQ_HANDLED; } -static void pld_send_ipc(struct link_device *ld, int dev, +static void dpram_send_ipc(struct link_device *ld, int dev, struct io_device *iod, struct sk_buff *skb) { - struct pld_link_device *pld = to_pld_link_device(ld); + struct dpram_link_device *dpld = to_dpram_link_device(ld); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; u16 mask; @@ -1230,31 +1551,46 @@ static void pld_send_ipc(struct link_device *ld, int dev, ld->name, get_dev_name(dev), txq->qlen); } - if (!ipc_active(pld)) + if (dpram_wake_up(dpld) < 0) { + trigger_force_cp_crash(dpld); + return; + } + + if (!dpram_ipc_active(dpld)) goto exit; - if (atomic_read(&pld->res_required[dev]) > 0) { + if (atomic_read(&dpld->res_required[dev]) > 0) { mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); goto exit; } - ret = pld_try_ipc_tx(pld, dev); + ret = dpram_try_ipc_tx(dpld, dev); if (ret > 0) { - mask = get_mask_send(pld, dev); - send_intr(pld, INT_NON_CMD(mask)); + mask = get_mask_send(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); } else if (ret == -ENOSPC) { - mask = get_mask_req_ack(pld, dev); - send_intr(pld, INT_NON_CMD(mask)); + mask = get_mask_req_ack(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); } else { - mif_info("%s: pld_try_ipc_tx fail (err %d)\n", ld->name, ret); + mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); } exit: - return; + dpram_allow_sleep(dpld); } -static int pld_send(struct link_device *ld, struct io_device *iod, +static int dpram_download_bin(struct link_device *ld, struct sk_buff *skb) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dload_bin) + return dpld->ext_op->dload_bin(dpld, skb); + else + return -ENODEV; +} + +static int dpram_send(struct link_device *ld, struct io_device *iod, struct sk_buff *skb) { enum dev_format dev = iod->format; @@ -1265,13 +1601,16 @@ static int pld_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - pld_send_ipc(ld, dev, iod, skb); + dpram_send_ipc(ld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); } return len; + case IPC_BOOT: + return dpram_download_bin(ld, skb); + default: mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name); dev_kfree_skb_any(skb); @@ -1279,38 +1618,48 @@ static int pld_send(struct link_device *ld, struct io_device *iod, } } -static int pld_force_dump(struct link_device *ld, struct io_device *iod) +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) { - struct pld_link_device *pld = to_pld_link_device(ld); - trigger_force_cp_crash(pld); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + trigger_force_cp_crash(dpld); return 0; } -static int pld_dump_start(struct link_device *ld, struct io_device *iod) +static void dpram_dump_memory(struct link_device *ld, char *buff) { - struct pld_link_device *pld = to_pld_link_device(ld); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u8 __iomem *base = dpld->dpctl->dp_base; + u32 size = dpld->dpctl->dp_size; - if (pld->ext_op && pld->ext_op->dump_start) - return pld->ext_op->dump_start(pld); + dpram_wake_up(dpld); + memcpy(buff, base, size); +} + +static int dpram_dump_start(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dump_start) + return dpld->ext_op->dump_start(dpld); else return -ENODEV; } -static int pld_dump_update(struct link_device *ld, struct io_device *iod, +static int dpram_dump_update(struct link_device *ld, struct io_device *iod, unsigned long arg) { - struct pld_link_device *pld = to_pld_link_device(ld); + struct dpram_link_device *dpld = to_dpram_link_device(ld); - if (pld->ext_op && pld->ext_op->dump_update) - return pld->ext_op->dump_update(pld, (void *)arg); + if (dpld->ext_op && dpld->ext_op->dump_update) + return dpld->ext_op->dump_update(dpld, (void *)arg); else return -ENODEV; } -static int pld_ioctl(struct link_device *ld, struct io_device *iod, +static int dpram_ioctl(struct link_device *ld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct pld_link_device *pld = to_pld_link_device(ld); + struct dpram_link_device *dpld = to_dpram_link_device(ld); int err = 0; /* @@ -1320,11 +1669,11 @@ static int pld_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return pld->init_status; + return dpld->dpram_init_status; default: - if (pld->ext_ioctl) { - err = pld->ext_ioctl(pld, iod, cmd, arg); + if (dpld->ext_ioctl) { + err = dpld->ext_ioctl(dpld, iod, cmd, arg); } else { mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); err = -EINVAL; @@ -1336,97 +1685,97 @@ static int pld_ioctl(struct link_device *ld, struct io_device *iod, return err; } -static int pld_table_init(struct pld_link_device *pld) +static int dpram_table_init(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; u8 __iomem *dp_base; int i; - if (!pld->base) { - mif_info("%s: ERR! pld->base == NULL\n", ld->name); + if (!dpld->dp_base) { + mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); return -EINVAL; } - dp_base = pld->base; + dp_base = dpld->dp_base; /* Map for IPC */ - if (pld->dpram->ipc_map) { - memcpy(&pld->ipc_map, pld->dpram->ipc_map, - sizeof(struct pld_ipc_map)); + if (dpld->dpctl->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, + sizeof(struct dpram_ipc_map)); } - pld->magic_ap2cp = pld->ipc_map.magic_ap2cp; - pld->access_ap2cp = pld->ipc_map.access_ap2cp; + dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp; + dpld->access_ap2cp = dpld->ipc_map.access_ap2cp; - pld->magic_cp2ap = pld->ipc_map.magic_cp2ap; - pld->access_cp2ap = pld->ipc_map.access_cp2ap; + dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap; + dpld->access_cp2ap = dpld->ipc_map.access_cp2ap; - pld->address_buffer = pld->ipc_map.address_buffer; + dpld->address_buffer = dpld->ipc_map.address_buffer; - for (i = 0; i < ld->max_ipc_dev; i++) - pld->dev[i] = &pld->ipc_map.dev[i]; - pld->mbx2ap = pld->ipc_map.mbx_cp2ap; - pld->mbx2cp = pld->ipc_map.mbx_ap2cp; + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->dev[i] = &dpld->ipc_map.dev[i]; + dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; + dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; /* Map for booting */ - if (pld->ext_op && pld->ext_op->init_boot_map) { - pld->ext_op->init_boot_map(pld); + if (dpld->ext_op && dpld->ext_op->init_boot_map) { + dpld->ext_op->init_boot_map(dpld); } else { - pld->bt_map.magic = (u32 *)(dp_base); - pld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); - pld->bt_map.space = pld->size - 8; + dpld->bt_map.magic = (u32 *)(dp_base); + dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); + dpld->bt_map.size = dpld->dp_size - 8; } /* Map for download (FOTA, UDL, etc.) */ - if (pld->ext_op && pld->ext_op->init_dl_map) { - pld->ext_op->init_dl_map(pld); + if (dpld->ext_op && dpld->ext_op->init_dl_map) { + dpld->ext_op->init_dl_map(dpld); } else { - pld->dl_map.magic = (u32 *)(dp_base); - pld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.magic = (u32 *)(dp_base); + dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); } /* Map for upload mode */ - if (pld->ext_op && pld->ext_op->init_ul_map) { - pld->ext_op->init_ul_map(pld); + if (dpld->ext_op && dpld->ext_op->init_ul_map) { + dpld->ext_op->init_ul_map(dpld); } else { - pld->ul_map.magic = (u32 *)(dp_base); - pld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + dpld->ul_map.magic = (u32 *)(dp_base); + dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); } return 0; } -static void pld_setup_common_op(struct pld_link_device *pld) -{ - pld->clear_intr = clear_intr; - pld->recv_intr = recv_intr; - pld->send_intr = send_intr; - pld->get_magic = get_magic; - pld->set_magic = set_magic; - pld->get_access = get_access; - pld->set_access = set_access; - pld->get_tx_head = get_tx_head; - pld->get_tx_tail = get_tx_tail; - pld->set_tx_head = set_tx_head; - pld->set_tx_tail = set_tx_tail; - pld->get_tx_buff = get_tx_buff; - pld->get_tx_buff_size = get_tx_buff_size; - pld->get_rx_head = get_rx_head; - pld->get_rx_tail = get_rx_tail; - pld->set_rx_head = set_rx_head; - pld->set_rx_tail = set_rx_tail; - pld->get_rx_buff = get_rx_buff; - pld->get_rx_buff_size = get_rx_buff_size; - pld->get_mask_req_ack = get_mask_req_ack; - pld->get_mask_res_ack = get_mask_res_ack; - pld->get_mask_send = get_mask_send; -} - -static int pld_link_init(struct link_device *ld, struct io_device *iod) +static void dpram_setup_common_op(struct dpram_link_device *dpld) +{ + dpld->clear_intr = clear_intr; + dpld->recv_intr = recv_intr; + dpld->send_intr = send_intr; + dpld->get_magic = get_magic; + dpld->set_magic = set_magic; + dpld->get_access = get_access; + dpld->set_access = set_access; + dpld->get_tx_head = get_tx_head; + dpld->get_tx_tail = get_tx_tail; + dpld->set_tx_head = set_tx_head; + dpld->set_tx_tail = set_tx_tail; + dpld->get_tx_buff = get_tx_buff; + dpld->get_tx_buff_size = get_tx_buff_size; + dpld->get_rx_head = get_rx_head; + dpld->get_rx_tail = get_rx_tail; + dpld->set_rx_head = set_rx_head; + dpld->set_rx_tail = set_rx_tail; + dpld->get_rx_buff = get_rx_buff; + dpld->get_rx_buff_size = get_rx_buff_size; + dpld->get_mask_req_ack = get_mask_req_ack; + dpld->get_mask_res_ack = get_mask_res_ack; + dpld->get_mask_send = get_mask_send; +} + +static int dpram_link_init(struct link_device *ld, struct io_device *iod) { return 0; } -static void pld_link_terminate(struct link_device *ld, struct io_device *iod) +static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) { return; } @@ -1434,11 +1783,11 @@ static void pld_link_terminate(struct link_device *ld, struct io_device *iod) struct link_device *pld_create_link_device(struct platform_device *pdev) { struct modem_data *mdm_data = NULL; - struct pld_link_device *pld = NULL; + struct dpram_link_device *dpld = NULL; struct link_device *ld = NULL; struct resource *res = NULL; resource_size_t res_size; - struct modemlink_dpram_data *dpram = NULL; + struct modemlink_dpram_control *dpctl = NULL; unsigned long task_data; int ret = 0; int i = 0; @@ -1454,19 +1803,19 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) mif_info("modem = %s\n", mdm_data->name); mif_info("link device = %s\n", mdm_data->link_name); - if (!mdm_data->dpram) { - mif_info("ERR! no mdm_data->dpram\n"); + if (!mdm_data->dpram_ctl) { + mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); goto err; } - dpram = mdm_data->dpram; + dpctl = mdm_data->dpram_ctl; /* Alloc DPRAM link device structure */ - pld = kzalloc(sizeof(struct pld_link_device), GFP_KERNEL); - if (!pld) { - mif_info("ERR! kzalloc pld fail\n"); + dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); + if (!dpld) { + mif_info("ERR! kzalloc dpld fail\n"); goto err; } - ld = &pld->ld; + ld = &dpld->ld; /* Retrieve modem data and DPRAM control data from the modem data */ ld->mdm_data = mdm_data; @@ -1474,30 +1823,30 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) ld->ipc_version = mdm_data->ipc_version; /* Retrieve the most basic data for IPC from the modem data */ - pld->dpram = dpram; - pld->type = dpram->type; + dpld->dpctl = dpctl; + dpld->dp_type = dpctl->dp_type; if (mdm_data->ipc_version < SIPC_VER_50) { - if (!mdm_data->max_ipc_dev) { + if (!dpctl->max_ipc_dev) { mif_info("ERR! no max_ipc_dev\n"); goto err; } - ld->aligned = dpram->aligned; - ld->max_ipc_dev = mdm_data->max_ipc_dev; + ld->aligned = dpctl->aligned; + dpld->max_ipc_dev = dpctl->max_ipc_dev; } else { ld->aligned = 1; - ld->max_ipc_dev = MAX_SIPC5_DEV; + dpld->max_ipc_dev = MAX_SIPC5_DEV; } /* Set attributes as a link device */ - ld->init_comm = pld_link_init; - ld->terminate_comm = pld_link_terminate; - ld->send = pld_send; - ld->force_dump = pld_force_dump; - ld->dump_start = pld_dump_start; - ld->dump_update = pld_dump_update; - ld->ioctl = pld_ioctl; + ld->init_comm = dpram_link_init; + ld->terminate_comm = dpram_link_terminate; + ld->send = dpram_send; + ld->force_dump = dpram_force_dump; + ld->dump_start = dpram_dump_start; + ld->dump_update = dpram_dump_update; + ld->ioctl = dpram_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1509,13 +1858,14 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; /* Set up function pointers */ - pld_setup_common_op(pld); - pld->ext_op = pld_get_ext_op(mdm_data->modem_type); - if (pld->ext_op && pld->ext_op->ioctl) - pld->ext_ioctl = pld->ext_op->ioctl; + dpram_setup_common_op(dpld); + dpld->dpram_dump = dpram_dump_memory; + dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); + if (dpld->ext_op && dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; /* Retrieve DPRAM resource */ - if (!dpram->base) { + if (!dpctl->dp_base) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { mif_info("%s: ERR! platform_get_resource fail\n", @@ -1524,54 +1874,59 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } res_size = resource_size(res); - dpram->base = ioremap_nocache(res->start, res_size); - dpram->size = res_size; + dpctl->dp_base = ioremap_nocache(res->start, res_size); + dpctl->dp_size = res_size; } - pld->base = dpram->base; - pld->size = dpram->size; + dpld->dp_base = dpctl->dp_base; + dpld->dp_size = dpctl->dp_size; - mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", - ld->name, pld->type, ld->aligned, (int)pld->base, pld->size); + mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", + ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, + dpld->dp_size); /* Initialize DPRAM map (physical map -> logical map) */ - ret = pld_table_init(pld); + ret = dpram_table_init(dpld); if (ret < 0) { - mif_info("%s: ERR! pld_table_init fail (err %d)\n", + mif_info("%s: ERR! dpram_table_init fail (err %d)\n", ld->name, ret); goto err; } - spin_lock_init(&pld->pld_lock); + spin_lock_init(&dpld->pld_lock); /* Disable IPC */ - set_magic(pld, 0); - set_access(pld, 0); + set_magic(dpld, 0); + set_access(dpld, 0); - pld->init_status = PLD_INIT_STATE_NONE; + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; /* Initialize locks, completions, and bottom halves */ - snprintf(pld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); - wake_lock_init(&pld->wlock, WAKE_LOCK_SUSPEND, pld->wlock_name); + snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); + wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); + + init_completion(&dpld->dpram_init_cmd); + init_completion(&dpld->modem_pif_init_done); + init_completion(&dpld->udl_start_complete); + init_completion(&dpld->udl_cmd_complete); + init_completion(&dpld->dump_start_complete); + init_completion(&dpld->dump_recv_done); - init_completion(&pld->dpram_init_cmd); - init_completion(&pld->modem_pif_init_done); - init_completion(&pld->udl_start_complete); - init_completion(&pld->udl_cmd_complete); - init_completion(&pld->crash_start_complete); - init_completion(&pld->crash_recv_done); + task_data = (unsigned long)dpld; + tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); - task_data = (unsigned long)pld; - tasklet_init(&pld->rx_tsk, pld_ipc_rx_task, task_data); + /* Prepare SKB queue head for RX processing */ + for (i = 0; i < dpld->max_ipc_dev; i++) + skb_queue_head_init(&dpld->skb_rxq[i]); /* Prepare RXB queue */ - qsize = MAX_RXBQ_SIZE; - for (i = 0; i < ld->max_ipc_dev; i++) { - bsize = rxbq_get_page_size(get_rx_buff_size(pld, i)); - pld->rxbq[i].size = qsize; - pld->rxbq[i].in = 0; - pld->rxbq[i].out = 0; - pld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); - if (!pld->rxbq[i].rxb) { + qsize = DPRAM_MAX_RXBQ_SIZE; + for (i = 0; i < dpld->max_ipc_dev; i++) { + bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); + dpld->rxbq[i].size = qsize; + dpld->rxbq[i].in = 0; + dpld->rxbq[i].out = 0; + dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); + if (!dpld->rxbq[i].rxb) { mif_info("%s: ERR! %s rxbq_create_pool fail\n", ld->name, get_dev_name(i)); goto err; @@ -1581,37 +1936,44 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } /* Prepare a multi-purpose miscellaneous buffer */ - pld->buff = kzalloc(pld->size, GFP_KERNEL); - if (!pld->buff) { - mif_info("%s: ERR! kzalloc pld->buff fail\n", ld->name); + dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); + if (!dpld->buff) { + mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); goto err; } /* Retrieve DPRAM IRQ GPIO# */ - pld->gpio_ipc_int2ap = mdm_data->gpio_ipc_int2ap; + dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; /* Retrieve DPRAM IRQ# */ - pld->irq = mdm_data->irq_ipc_int2ap; + if (!dpctl->dpram_irq) { + dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); + if (dpctl->dpram_irq < 0) { + mif_info("%s: ERR! platform_get_irq_byname fail\n", + ld->name); + goto err; + } + } + dpld->irq = dpctl->dpram_irq; /* Retrieve DPRAM IRQ flags */ - if (mdm_data->irqf_ipc_int2ap) - pld->irq_flags = mdm_data->irqf_ipc_int2ap; - else - pld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + if (!dpctl->dpram_irq_flags) + dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + dpld->irq_flags = dpctl->dpram_irq_flags; /* Register DPRAM interrupt handler */ - snprintf(pld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); - ret = pld_register_isr(pld->irq, pld_irq_handler, pld->irq_flags, - pld->irq_name, pld); + snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); + ret = dpram_register_isr(dpld->irq, dpram_irq_handler, dpld->irq_flags, + dpld->irq_name, dpld); if (ret) goto err; return ld; err: - if (pld) { - kfree(pld->buff); - kfree(pld); + if (dpld) { + kfree(dpld->buff); + kfree(dpld); } return NULL; diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h index 2690faa..2656110 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.h +++ b/drivers/misc/modem_if/modem_link_device_pld.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -11,134 +12,181 @@ * GNU General Public License for more details. * */ -#ifndef __MODEM_LINK_DEVICE_PLD_H__ -#define __MODEM_LINK_DEVICE_PLD_H__ - -#include "modem_link_device_memory.h" +#ifndef __MODEM_LINK_DEVICE_DPRAM_H__ +#define __MODEM_LINK_DEVICE_DPRAM_H__ + +#include +#include +#include +#include +#include + +#include "modem_prj.h" + +#define DPRAM_MAGIC_CODE 0xAA + +/* interrupt masks.*/ +#define INT_MASK_VALID 0x0080 +#define INT_MASK_CMD 0x0040 +#define INT_VALID(x) ((x) & INT_MASK_VALID) +#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) +#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) +#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) + +#define EXT_UDL_MASK 0xF000 +#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) +#define EXT_INT_VALID_MASK 0x8000 +#define EXT_CMD_VALID_MASK 0x4000 +#define UDL_CMD_VALID_MASK 0x2000 +#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) +#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) +#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) +#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) + +#define EXT_CMD_MASK(x) ((x) & 0x0FFF) +#define EXT_CMD_SET_SPEED_LOW 0x0011 +#define EXT_CMD_SET_SPEED_MID 0x0012 +#define EXT_CMD_SET_SPEED_HIGH 0x0013 + +#define UDL_RESULT_SUCCESS 0x1 +#define UDL_RESULT_FAIL 0x2 + +#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) +#define UDL_CMD_RECV_READY 0x1 +#define UDL_CMD_DL_START_REQ 0x2 +#define UDL_CMD_DL_START_RESP 0x3 +#define UDL_CMD_IMAGE_SEND_REQ 0x4 +#define UDL_CMD_SEND_DONE_RESP 0x5 +#define UDL_CMD_SEND_DONE_REQ 0x6 +#define UDL_CMD_UPDATE_DONE 0x7 +#define UDL_CMD_STATUS_UPDATE 0x8 +#define UDL_CMD_IMAGE_SEND_RESP 0x9 +#define UDL_CMD_EFS_CLEAR_RESP 0xB +#define UDL_CMD_ALARM_BOOT_OK 0xC +#define UDL_CMD_ALARM_BOOT_FAIL 0xD + +#define CMD_IMG_START_REQ 0x9200 +#define CMD_IMG_SEND_REQ 0x9400 +#define CMD_DL_SEND_DONE_REQ 0x9600 +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_REQ_ACK_F 0x0020 +#define INT_MASK_REQ_ACK_R 0x0010 +#define INT_MASK_RES_ACK_F 0x0008 +#define INT_MASK_RES_ACK_R 0x0004 +#define INT_MASK_SEND_F 0x0002 +#define INT_MASK_SEND_R 0x0001 + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_RES_ACK_SET \ + (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) + +#define INT_MASK_SEND_SET \ + (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) + +#define INT_CMD_MASK(x) ((x) & 0xF) +#define INT_CMD_INIT_START 0x1 +#define INT_CMD_INIT_END 0x2 +#define INT_CMD_REQ_ACTIVE 0x3 +#define INT_CMD_RES_ACTIVE 0x4 +#define INT_CMD_REQ_TIME_SYNC 0x5 +#define INT_CMD_CRASH_RESET 0x7 +#define INT_CMD_PHONE_START 0x8 +#define INT_CMD_ERR_DISPLAY 0x9 +#define INT_CMD_CRASH_EXIT 0x9 +#define INT_CMD_CP_DEEP_SLEEP 0xA +#define INT_CMD_NV_REBUILDING 0xB +#define INT_CMD_EMER_DOWN 0xC +#define INT_CMD_PIF_INIT_DONE 0xD +#define INT_CMD_SILENT_NV_REBUILDING 0xE +#define INT_CMD_NORMAL_PWR_OFF 0xF + +#define START_FLAG 0x7F +#define END_FLAG 0x7E + +#define DP_MAGIC_DMDL 0x4445444C +#define DP_MAGIC_UMDL 0x4445444D +#define DP_DPRAM_SIZE 0x4000 +#define DP_DEFAULT_WRITE_LEN 8168 +#define DP_DEFAULT_DUMP_LEN 16128 +#define DP_DUMP_HEADER_SIZE 7 + +#define UDL_TIMEOUT (50 * HZ) +#define UDL_SEND_TIMEOUT (200 * HZ) +#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) +#define DUMP_TIMEOUT (30 * HZ) +#define DUMP_START_TIMEOUT (100 * HZ) +#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ #define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) -enum pld_init_status { - PLD_INIT_STATE_NONE, - PLD_INIT_STATE_READY, +enum host_boot_mode { + HOST_BOOT_MODE_NORMAL, + HOST_BOOT_MODE_DUMP, }; -#if 1 -#define MAX_RXBQ_SIZE 256 +enum dpram_init_status { + DPRAM_INIT_STATE_NONE, + DPRAM_INIT_STATE_READY, +}; -struct mif_rxb { - u8 *buff; - unsigned size; +struct dpram_boot_img { + char *addr; + int size; + enum host_boot_mode mode; + unsigned req; + unsigned resp; +}; - u8 *data; - unsigned len; +#define MAX_PAYLOAD_SIZE 0x2000 +struct dpram_boot_frame { + unsigned req; /* AP->CP request */ + unsigned resp; /* response expected by AP */ + ssize_t len; /* data size in the buffer */ + unsigned offset; /* offset to write into DPRAM */ + char data[MAX_PAYLOAD_SIZE]; }; -struct mif_rxb_queue { +/* buffer type for modem image */ +struct dpram_dump_arg { + char *buff; /* pointer to the buffer */ + int buff_size; /* buffer size */ + unsigned req; /* AP->CP request */ + unsigned resp; /* CP->AP response */ + bool cmd; /* AP->CP command */ +}; + +struct dpram_firmware { + char *firmware; int size; - int in; - int out; - struct mif_rxb *rxb; + int is_delta; +}; +enum dpram_link_mode { + DPRAM_LINK_MODE_INVALID = 0, + DPRAM_LINK_MODE_IPC, + DPRAM_LINK_MODE_BOOT, + DPRAM_LINK_MODE_DLOAD, + DPRAM_LINK_MODE_ULOAD, }; -/* -** RXB (DPRAM RX buffer) functions -*/ -static inline struct mif_rxb *rxbq_create_pool(unsigned size, int count) -{ - struct mif_rxb *rxb; - u8 *buff; - int i; - - rxb = kzalloc(sizeof(struct mif_rxb) * count, GFP_KERNEL); - if (!rxb) { - mif_info("ERR! kzalloc rxb fail\n"); - return NULL; - } - - buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); - if (!buff) { - mif_info("ERR! kzalloc buff fail\n"); - kfree(rxb); - return NULL; - } - - for (i = 0; i < count; i++) { - rxb[i].buff = buff; - rxb[i].size = size; - buff += size; - } - - return rxb; -} - -static inline unsigned rxbq_get_page_size(unsigned len) -{ - return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; -} - -static inline bool rxbq_empty(struct mif_rxb_queue *rxbq) -{ - return (rxbq->in == rxbq->out) ? true : false; -} - -static inline int rxbq_free_size(struct mif_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in < out) ? (out - in - 1) : (qsize + out - in - 1); -} - -static inline struct mif_rxb *rxbq_get_free_rxb(struct mif_rxb_queue *rxbq) -{ - struct mif_rxb *rxb = NULL; - - if (likely(rxbq_free_size(rxbq) > 0)) { - rxb = &rxbq->rxb[rxbq->in]; - rxbq->in++; - if (rxbq->in >= rxbq->size) - rxbq->in -= rxbq->size; - rxb->data = rxb->buff; - } - - return rxb; -} - -static inline int rxbq_size(struct mif_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in >= out) ? (in - out) : (qsize - out + in); -} - -static inline struct mif_rxb *rxbq_get_data_rxb(struct mif_rxb_queue *rxbq) -{ - struct mif_rxb *rxb = NULL; - - if (likely(!rxbq_empty(rxbq))) { - rxb = &rxbq->rxb[rxbq->out]; - rxbq->out++; - if (rxbq->out >= rxbq->size) - rxbq->out -= rxbq->size; - } - - return rxb; -} - -static inline u8 *rxb_put(struct mif_rxb *rxb, unsigned len) -{ - rxb->len = len; - return rxb->data; -} - -static inline void rxb_clear(struct mif_rxb *rxb) -{ - rxb->data = NULL; - rxb->len = 0; -} -#endif +struct dpram_boot_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 __iomem *req; + u32 __iomem *resp; + u32 size; +}; struct qc_dpram_boot_map { u8 __iomem *buff; @@ -147,14 +195,39 @@ struct qc_dpram_boot_map { u16 __iomem *count; }; -struct qc_dpram_udl_param { +struct dpram_dload_map { + u32 __iomem *magic; + u8 __iomem *buff; +}; + +struct dpram_uload_map { + u32 __iomem *magic; + u8 __iomem *buff; +}; + +struct dpram_ota_header { + u8 start_index; + u16 nframes; + u16 curframe; + u16 len; + +} __packed; + +struct ul_header { + u8 bop; + u16 total_frame; + u16 curr_frame; + u16 len; +} __packed; + +struct dpram_udl_param { unsigned char *addr; unsigned int size; unsigned int count; unsigned int tag; }; -struct qc_dpram_udl_check { +struct dpram_udl_check { unsigned int total_size; unsigned int rest_size; unsigned int send_size; @@ -163,38 +236,180 @@ struct qc_dpram_udl_check { unsigned int boot_complete; }; -struct pld_ext_op; +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 + +#define MAX_WQ_NAME_LENGTH 64 + +#define DPRAM_MAX_RXBQ_SIZE 256 + +struct dpram_rxb { + u8 *buff; + unsigned size; -struct pld_link_device { + u8 *data; + unsigned len; +}; + +struct dpram_rxb_queue { + int size; + int in; + int out; + struct dpram_rxb *rxb; +}; + +/* + mbx_ap2cp + 0x0 + magic_code + + access_enable + + padding + + mbx_cp2ap + 0x1000 + magic_code + + access_enable + + padding + + fmt_tx_head + fmt_tx_tail + fmt_tx_buff + 0x2000 + raw_tx_head + raw_tx_tail + raw_tx_buff + + fmt_rx_head + fmt_rx_tail + fmt_rx_buff + 0x3000 + raw_rx_head + raw_rx_tail + raw_rx_buff + + = 2 + + 4094 + + 2 + + 4094 + + 2 + + 2 + + 2 + 2 + 1020 + + 2 + 2 + 3064 + + 2 + 2 + 1020 + + 2 + 2 + 3064 + */ + +#define DP_PLD_FMT_TX_BUFF_SZ 1024 +#define DP_PLD_RAW_TX_BUFF_SZ 3072 +#define DP_PLD_FMT_RX_BUFF_SZ 1024 +#define DP_PLD_RAW_RX_BUFF_SZ 3072 + +#define MAX_MSM_EDPRAM_IPC_DEV 2 /* FMT, RAW */ + +struct dpram_ipc_pld_map { + u16 mbx_ap2cp; + u16 magic_ap2cp; + u16 access_ap2cp; + u16 fmt_tx_head; + u16 raw_tx_head; + u16 fmt_rx_tail; + u16 raw_rx_tail; + u16 temp1; + u8 padding1[4080]; + + u16 mbx_cp2ap; + u16 magic_cp2ap; + u16 access_cp2ap; + u16 fmt_tx_tail; + u16 raw_tx_tail; + u16 fmt_rx_head; + u16 raw_rx_head; + u16 temp2; + u8 padding2[4080]; + + u8 fmt_tx_buff[DP_PLD_FMT_TX_BUFF_SZ]; + u8 raw_tx_buff[DP_PLD_RAW_TX_BUFF_SZ]; + u8 fmt_rx_buff[DP_PLD_RAW_TX_BUFF_SZ]; + u8 raw_rx_buff[DP_PLD_RAW_RX_BUFF_SZ]; + + u8 padding3[16384]; + + u16 address_buffer; +}; + +/* + magic_code + + access_enable + + fmt_tx_head + fmt_tx_tail + fmt_tx_buff + + raw_tx_head + raw_tx_tail + raw_tx_buff + + fmt_rx_head + fmt_rx_tail + fmt_rx_buff + + raw_rx_head + raw_rx_tail + raw_rx_buff + + mbx_cp2ap + + mbx_ap2cp + = 2 + + 2 + + 2 + 2 + 1336 + + 2 + 2 + 4564 + + 2 + 2 + 1336 + + 2 + 2 + 9124 + + 2 + + 2 + = 16384 +*/ +#define DP_16K_FMT_TX_BUFF_SZ 1336 +#define DP_16K_RAW_TX_BUFF_SZ 4564 +#define DP_16K_FMT_RX_BUFF_SZ 1336 +#define DP_16K_RAW_RX_BUFF_SZ 9124 + +struct dpram_ipc_16k_map { + u16 magic; + u16 access; + + u16 fmt_tx_head; + u16 fmt_tx_tail; + u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ]; + + u16 raw_tx_head; + u16 raw_tx_tail; + u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ]; + + u16 fmt_rx_head; + u16 fmt_rx_tail; + u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ]; + + u16 raw_rx_head; + u16 raw_rx_tail; + u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ]; + + u16 mbx_cp2ap; + u16 mbx_ap2cp; +}; + +#define DP_MAX_NAME_LEN 32 + +struct dpram_ext_op; + +struct dpram_link_device { struct link_device ld; + /* The mode of this DPRAM link device */ + enum dpram_link_mode mode; + /* DPRAM address and size */ - enum dpram_type type; /* DPRAM type */ - u8 __iomem *base; /* DPRAM base virtual address */ - u32 size; /* DPRAM size */ + u8 __iomem *dp_base; /* DPRAM base virtual address */ + u32 dp_size; /* DPRAM size */ + enum dpram_type dp_type; /* DPRAM type */ /* DPRAM IRQ GPIO# */ - unsigned gpio_ipc_int2ap; + unsigned gpio_dpram_int; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[MIF_MAX_NAME_LEN]; + char irq_name[DP_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - struct modemlink_dpram_data *dpram; + int max_ipc_dev; + struct modemlink_dpram_control *dpctl; /* Physical configuration -> logical configuration */ union { - struct memif_boot_map bt_map; + struct dpram_boot_map bt_map; struct qc_dpram_boot_map qc_bt_map; }; - struct memif_dload_map dl_map; - struct memif_uload_map ul_map; + struct dpram_dload_map dl_map; + struct dpram_uload_map ul_map; /* IPC device map */ - struct pld_ipc_map ipc_map; + struct dpram_ipc_map ipc_map; /* Pointers (aliases) to IPC device map */ u16 __iomem *magic_ap2cp; @@ -209,7 +424,7 @@ struct pld_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[MIF_MAX_NAME_LEN]; + char wlock_name[DP_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; @@ -217,16 +432,19 @@ struct pld_link_device { struct completion modem_pif_init_done; /* For UDL */ + struct tasklet_struct ul_tsk; struct tasklet_struct dl_tsk; struct completion udl_start_complete; struct completion udl_cmd_complete; - struct qc_dpram_udl_check qc_udl_check; - struct qc_dpram_udl_param qc_udl_param; + struct dpram_udl_check udl_check; + struct dpram_udl_param udl_param; - /* For CP crash dump */ + /* For CP RAM dump */ struct timer_list crash_ack_timer; - struct completion crash_start_complete; - struct completion crash_recv_done; + struct completion dump_start_complete; + struct completion dump_recv_done; + struct timer_list dump_timer; + int dump_rcvd; /* Count of dump packets received */ /* For locking TX process */ spinlock_t tx_rx_lock; @@ -234,8 +452,10 @@ struct pld_link_device { /* For efficient RX process */ struct tasklet_struct rx_tsk; - struct mif_rxb_queue rxbq[MAX_IPC_DEV]; + struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; struct io_device *iod[MAX_IPC_DEV]; + bool use_skb; + struct sk_buff_head skb_rxq[MAX_IPC_DEV]; /* For retransmission after buffer full state */ atomic_t res_required[MAX_IPC_DEV]; @@ -246,65 +466,67 @@ struct pld_link_device { /* Multi-purpose miscellaneous buffer */ u8 *buff; - /* PLD IPC initialization status */ - int init_status; + /* DPRAM IPC initialization status */ + int dpram_init_status; /* Alias to device-specific IOCTL function */ - int (*ext_ioctl)(struct pld_link_device *pld, struct io_device *iod, + int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg); + /* For DPRAM dump */ + void (*dpram_dump)(struct link_device *ld, char *buff); + /* Common operations for each DPRAM */ - void (*clear_intr)(struct pld_link_device *pld); - u16 (*recv_intr)(struct pld_link_device *pld); - void (*send_intr)(struct pld_link_device *pld, u16 mask); - u16 (*get_magic)(struct pld_link_device *pld); - void (*set_magic)(struct pld_link_device *pld, u16 value); - u16 (*get_access)(struct pld_link_device *pld); - void (*set_access)(struct pld_link_device *pld, u16 value); - u32 (*get_tx_head)(struct pld_link_device *pld, int id); - u32 (*get_tx_tail)(struct pld_link_device *pld, int id); - void (*set_tx_head)(struct pld_link_device *pld, int id, u32 head); - void (*set_tx_tail)(struct pld_link_device *pld, int id, u32 tail); - u8 *(*get_tx_buff)(struct pld_link_device *pld, int id); - u32 (*get_tx_buff_size)(struct pld_link_device *pld, int id); - u32 (*get_rx_head)(struct pld_link_device *pld, int id); - u32 (*get_rx_tail)(struct pld_link_device *pld, int id); - void (*set_rx_head)(struct pld_link_device *pld, int id, u32 head); - void (*set_rx_tail)(struct pld_link_device *pld, int id, u32 tail); - u8 *(*get_rx_buff)(struct pld_link_device *pld, int id); - u32 (*get_rx_buff_size)(struct pld_link_device *pld, int id); - u16 (*get_mask_req_ack)(struct pld_link_device *pld, int id); - u16 (*get_mask_res_ack)(struct pld_link_device *pld, int id); - u16 (*get_mask_send)(struct pld_link_device *pld, int id); + void (*clear_intr)(struct dpram_link_device *dpld); + u16 (*recv_intr)(struct dpram_link_device *dpld); + void (*send_intr)(struct dpram_link_device *dpld, u16 mask); + u16 (*get_magic)(struct dpram_link_device *dpld); + void (*set_magic)(struct dpram_link_device *dpld, u16 value); + u16 (*get_access)(struct dpram_link_device *dpld); + void (*set_access)(struct dpram_link_device *dpld, u16 value); + u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); + void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); + void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); /* Extended operations for various modems */ - struct pld_ext_op *ext_op; + struct dpram_ext_op *ext_op; }; /* converts from struct link_device* to struct xxx_link_device* */ -#define to_pld_link_device(linkdev) \ - container_of(linkdev, struct pld_link_device, ld) +#define to_dpram_link_device(linkdev) \ + container_of(linkdev, struct dpram_link_device, ld) -struct pld_ext_op { +struct dpram_ext_op { int exist; - void (*init_boot_map)(struct pld_link_device *pld); - void (*init_dl_map)(struct pld_link_device *pld); - void (*init_ul_map)(struct pld_link_device *pld); + void (*init_boot_map)(struct dpram_link_device *dpld); + void (*init_dl_map)(struct dpram_link_device *dpld); + void (*init_ul_map)(struct dpram_link_device *dpld); - void (*dload_cmd_handler)(struct pld_link_device *pld, u16 cmd); + int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb); + void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd); - void (*cp_start_handler)(struct pld_link_device *pld); + void (*cp_start_handler)(struct dpram_link_device *dpld); - void (*crash_log)(struct pld_link_device *pld); - int (*dump_start)(struct pld_link_device *pld); - int (*dump_update)(struct pld_link_device *pld, void *arg); + void (*crash_log)(struct dpram_link_device *dpld); + int (*dump_start)(struct dpram_link_device *dpld); + int (*dump_update)(struct dpram_link_device *dpld, void *arg); - int (*ioctl)(struct pld_link_device *pld, struct io_device *iod, + int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg); - - void (*clear_intr)(struct pld_link_device *pld); }; -struct pld_ext_op *pld_get_ext_op(enum modem_t modem); +struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); #endif diff --git a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c index 6c259f4..ae6578c 100644 --- a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c @@ -25,8 +25,8 @@ #include #include #include - #include + #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" @@ -41,55 +41,56 @@ enum qc_dload_tag { static void qc_dload_task(unsigned long data); -static void qc_init_boot_map(struct pld_link_device *pld) +static void qc_init_boot_map(struct dpram_link_device *dpld) { - struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; - struct modemlink_dpram_data *dpram = pld->dpram; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct modemlink_dpram_control *dpctl = dpld->dpctl; - qbt_map->buff = pld->dev[0]->txq.buff; - qbt_map->frame_size = (u16 *)(pld->base + dpram->boot_size_offset); - qbt_map->tag = (u16 *)(pld->base + dpram->boot_tag_offset); - qbt_map->count = (u16 *)(pld->base + dpram->boot_count_offset); + bt_map->buff = dpld->dev[0]->txq.buff; + bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); + bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); + bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); - tasklet_init(&pld->dl_tsk, qc_dload_task, (unsigned long)pld); + tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); } -static void qc_dload_map(struct pld_link_device *pld, u8 is_upload) +static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload) { - struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; - struct modemlink_dpram_data *dpram = pld->dpram; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct modemlink_dpram_control *dpctl = dpld->dpctl; unsigned int upload_offset = 0; if (is_upload == 1) { upload_offset = 0x1000; - qbt_map->buff = pld->dev[0]->rxq.buff; + bt_map->buff = dpld->dev[0]->rxq.buff; } else { upload_offset = 0; - qbt_map->buff = pld->dev[0]->txq.buff; + bt_map->buff = dpld->dev[0]->txq.buff; } - qbt_map->frame_size = (u16 *)(pld->base + - dpram->boot_size_offset + upload_offset); - qbt_map->tag = (u16 *)(pld->base + - dpram->boot_tag_offset + upload_offset); - qbt_map->count = (u16 *)(pld->base + - dpram->boot_count_offset + upload_offset); + bt_map->frame_size = (u16 *)(dpld->dp_base + + dpctl->boot_size_offset + upload_offset); + bt_map->tag = (u16 *)(dpld->dp_base + + dpctl->boot_tag_offset + upload_offset); + bt_map->count = (u16 *)(dpld->dp_base + + dpctl->boot_count_offset + upload_offset); + } -static int qc_prepare_download(struct pld_link_device *pld) +static int qc_prepare_download(struct dpram_link_device *dpld) { int retval = 0; int count = 0; - qc_dload_map(pld, 0); + qc_dload_map(dpld, 0); while (1) { - if (pld->qc_udl_check.copy_start) { - pld->qc_udl_check.copy_start = 0; + if (dpld->udl_check.copy_start) { + dpld->udl_check.copy_start = 0; break; } - usleep_range(10000, 11000); + msleep(20); count++; if (count > 1000) { @@ -101,42 +102,42 @@ static int qc_prepare_download(struct pld_link_device *pld) return retval; } -static void _qc_do_download(struct pld_link_device *pld, - struct qc_dpram_udl_param *param) +static void _qc_do_download(struct dpram_link_device *dpld, + struct dpram_udl_param *param) { - struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - if (param->size <= pld->dpram->max_boot_frame_size) { - iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), - pld->address_buffer); - memcpy(pld->base, param->addr, param->size); + if (param->size <= dpld->dpctl->max_boot_frame_size) { + iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), + dpld->address_buffer); + memcpy(dpld->dp_base, param->addr, param->size); - iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), - pld->address_buffer); - iowrite16(param->size, pld->base); + iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), + dpld->address_buffer); + iowrite16(param->size, dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), - pld->address_buffer); - iowrite16(param->tag, pld->base); + iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), + dpld->address_buffer); + iowrite16(param->tag, dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), - pld->address_buffer); - iowrite16(param->count, pld->base); + iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), + dpld->address_buffer); + iowrite16(param->count, dpld->dp_base); - pld->send_intr(pld, 0xDB12); + dpld->send_intr(dpld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int _qc_download(struct pld_link_device *pld, void *arg, +static int _qc_download(struct dpram_link_device *dpld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct qc_dpram_udl_param param; + struct dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -152,24 +153,24 @@ static int _qc_download(struct pld_link_device *pld, void *arg, memset(img, 0, param.size); memcpy(img, param.addr, param.size); - pld->qc_udl_check.total_size = param.size; - pld->qc_udl_check.rest_size = param.size; - pld->qc_udl_check.send_size = 0; - pld->qc_udl_check.copy_complete = 0; + dpld->udl_check.total_size = param.size; + dpld->udl_check.rest_size = param.size; + dpld->udl_check.send_size = 0; + dpld->udl_check.copy_complete = 0; - pld->qc_udl_param.addr = img; - pld->qc_udl_param.size = pld->dpram->max_boot_frame_size; + dpld->udl_param.addr = img; + dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - pld->qc_udl_param.count = 1; + dpld->udl_param.count = 1; else - pld->qc_udl_param.count = param.count; - pld->qc_udl_param.tag = tag; + dpld->udl_param.count = param.count; + dpld->udl_param.tag = tag; - if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) - pld->qc_udl_param.size = pld->qc_udl_check.rest_size; + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(pld, &pld->qc_udl_param); + _qc_do_download(dpld, &dpld->udl_param); /* Wait for completion */ @@ -179,13 +180,13 @@ static int _qc_download(struct pld_link_device *pld, void *arg, cnt_limit = 1000; while (1) { - if (pld->qc_udl_check.copy_complete) { - pld->qc_udl_check.copy_complete = 0; + if (dpld->udl_check.copy_complete) { + dpld->udl_check.copy_complete = 0; retval = 0; break; } - usleep_range(10000, 11000); + msleep(20); count++; if (count > cnt_limit) { @@ -200,53 +201,53 @@ static int _qc_download(struct pld_link_device *pld, void *arg, return retval; } -static int qc_download_bin(struct pld_link_device *pld, void *arg) +static int qc_download_bin(struct dpram_link_device *dpld, void *arg) { - return _qc_download(pld, arg, QC_DLOAD_TAG_BIN); + return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); } -static int qc_download_nv(struct pld_link_device *pld, void *arg) +static int qc_download_nv(struct dpram_link_device *dpld, void *arg) { - return _qc_download(pld, arg, QC_DLOAD_TAG_NV); + return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { - struct pld_link_device *pld = (struct pld_link_device *)data; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; - pld->qc_udl_check.send_size += pld->qc_udl_param.size; - pld->qc_udl_check.rest_size -= pld->qc_udl_param.size; + dpld->udl_check.send_size += dpld->udl_param.size; + dpld->udl_check.rest_size -= dpld->udl_param.size; - pld->qc_udl_param.addr += pld->qc_udl_param.size; + dpld->udl_param.addr += dpld->udl_param.size; - if (pld->qc_udl_check.send_size >= pld->qc_udl_check.total_size) { - pld->qc_udl_check.copy_complete = 1; - pld->qc_udl_param.tag = 0; + if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { + dpld->udl_check.copy_complete = 1; + dpld->udl_param.tag = 0; return; } - if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) - pld->qc_udl_param.size = pld->qc_udl_check.rest_size; + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; - pld->qc_udl_param.count += 1; + dpld->udl_param.count += 1; - _qc_do_download(pld, &pld->qc_udl_param); + _qc_do_download(dpld, &dpld->udl_param); } -static void qc_dload_cmd_handler(struct pld_link_device *pld, u16 cmd) +static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) { switch (cmd) { case 0x1234: - pld->qc_udl_check.copy_start = 1; + dpld->udl_check.copy_start = 1; break; case 0xDBAB: - tasklet_schedule(&pld->dl_tsk); + tasklet_schedule(&dpld->dl_tsk); break; case 0xABCD: - mif_info("[%s] booting Start\n", pld->ld.name); - pld->qc_udl_check.boot_complete = 1; + mif_info("[%s] booting Start\n", dpld->ld.name); + dpld->udl_check.boot_complete = 1; break; default: @@ -254,22 +255,22 @@ static void qc_dload_cmd_handler(struct pld_link_device *pld, u16 cmd) } } -static int qc_boot_start(struct pld_link_device *pld) +static int qc_boot_start(struct dpram_link_device *dpld) { u16 mask = 0; int count = 0; /* Send interrupt -> '0x4567' */ mask = 0x4567; - pld->send_intr(pld, mask); + dpld->send_intr(dpld, mask); while (1) { - if (pld->qc_udl_check.boot_complete) { - pld->qc_udl_check.boot_complete = 0; + if (dpld->udl_check.boot_complete) { + dpld->udl_check.boot_complete = 0; break; } - usleep_range(10000, 11000); + msleep(20); count++; if (count > 200) { @@ -281,17 +282,17 @@ static int qc_boot_start(struct pld_link_device *pld) return 0; } -static int qc_boot_post_process(struct pld_link_device *pld) +static int qc_boot_post_process(struct dpram_link_device *dpld) { int count = 0; while (1) { - if (pld->boot_start_complete) { - pld->boot_start_complete = 0; + if (dpld->boot_start_complete) { + dpld->boot_start_complete = 0; break; } - usleep_range(10000, 11000); + msleep(20); count++; if (count > 200) { @@ -303,7 +304,7 @@ static int qc_boot_post_process(struct pld_link_device *pld) return 0; } -static void qc_start_handler(struct pld_link_device *pld) +static void qc_start_handler(struct dpram_link_device *dpld) { /* * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT | @@ -311,38 +312,38 @@ static void qc_start_handler(struct pld_link_device *pld) */ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002); - pld->boot_start_complete = 1; + dpld->boot_start_complete = 1; /* Send INIT_END code to CP */ mif_info("send 0x%04X (INIT_END)\n", mask); - pld->send_intr(pld, mask); + dpld->send_intr(dpld, mask); } -static void qc_crash_log(struct pld_link_device *pld) +static void qc_crash_log(struct dpram_link_device *dpld) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = pld->get_rx_buff(pld, IPC_FMT); + data = dpld->get_rx_buff(dpld, IPC_FMT); memcpy(buf, data, (sizeof(buf) - 1)); mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); mif_info("PHONE ERR MSG\t| %s\n", buf); } -static int _qc_data_upload(struct pld_link_device *pld, - struct qc_dpram_udl_param *param) +static int _qc_data_upload(struct dpram_link_device *dpld, + struct dpram_udl_param *param) { - struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(pld->gpio_ipc_int2ap)) { - intval = pld->recv_intr(pld); + if (!gpio_get_value(dpld->gpio_dpram_int)) { + intval = dpld->recv_intr(dpld); if (intval == 0xDBAB) { break; } else { @@ -351,7 +352,7 @@ static int _qc_data_upload(struct pld_link_device *pld, } } - usleep_range(1000, 2000); + msleep(20); count++; if (count > 200) { @@ -360,43 +361,43 @@ static int _qc_data_upload(struct pld_link_device *pld, } } - iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), - pld->address_buffer); - param->size = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), + dpld->address_buffer); + param->size = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), - pld->address_buffer); - param->tag = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), + dpld->address_buffer); + param->tag = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), - pld->address_buffer); - param->count = ioread16(pld->base); + iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), + dpld->address_buffer); + param->count = ioread16(dpld->dp_base); - iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), - pld->address_buffer); - memcpy(param->addr, pld->base, param->size); + iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), + dpld->address_buffer); + memcpy(param->addr, dpld->dp_base, param->size); - pld->send_intr(pld, 0xDB12); + dpld->send_intr(dpld, 0xDB12); return retval; } -static int qc_uload_step1(struct pld_link_device *pld) +static int qc_uload_step1(struct dpram_link_device *dpld) { int retval = 0; int count = 0; u16 intval = 0; u16 mask = 0; - qc_dload_map(pld, 1); + qc_dload_map(dpld, 1); mif_info("+---------------------------------------------+\n"); mif_info("| UPLOAD PHONE SDRAM |\n"); mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(pld->gpio_ipc_int2ap)) { - intval = pld->recv_intr(pld); + if (!gpio_get_value(dpld->gpio_dpram_int)) { + intval = dpld->recv_intr(dpld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { break; @@ -406,11 +407,11 @@ static int qc_uload_step1(struct pld_link_device *pld) } } - usleep_range(1000, 2000); + msleep(20); count++; if (count > 200) { - intval = pld->recv_intr(pld); + intval = dpld->recv_intr(dpld); mif_info("count %d, intr 0x%04x\n", count, intval); if (intval == 0x1234) break; @@ -419,15 +420,15 @@ static int qc_uload_step1(struct pld_link_device *pld) } mask = 0xDEAD; - pld->send_intr(pld, mask); + dpld->send_intr(dpld, mask); return retval; } -static int qc_uload_step2(struct pld_link_device *pld, void *arg) +static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) { int retval = 0; - struct qc_dpram_udl_param param; + struct dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -435,7 +436,7 @@ static int qc_uload_step2(struct pld_link_device *pld, void *arg) return -1; } - retval = _qc_data_upload(pld, ¶m); + retval = _qc_data_upload(dpld, ¶m); if (retval < 0) { mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); return -1; @@ -445,7 +446,7 @@ static int qc_uload_step2(struct pld_link_device *pld, void *arg) mif_info("param->count = %d\n", param.count); if (param.tag == 4) { - enable_irq(pld->irq); + enable_irq(dpld->irq); mif_info("param->tag = %d\n", param.tag); } @@ -458,57 +459,57 @@ static int qc_uload_step2(struct pld_link_device *pld, void *arg) return retval; } -static int qc_ioctl(struct pld_link_device *pld, struct io_device *iod, +static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct link_device *ld = &pld->ld; + struct link_device *ld = &dpld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: - err = qc_prepare_download(pld); + err = qc_prepare_download(dpld); if (err < 0) mif_info("%s: ERR! prepare_download fail\n", ld->name); break; case IOCTL_DPRAM_PHONEIMG_LOAD: - err = qc_download_bin(pld, (void *)arg); + err = qc_download_bin(dpld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_bin fail\n", ld->name); break; case IOCTL_DPRAM_NVDATA_LOAD: - err = qc_download_nv(pld, (void *)arg); + err = qc_download_nv(dpld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_nv fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_BOOTSTART: - err = qc_boot_start(pld); + err = qc_boot_start(dpld); if (err < 0) { mif_info("%s: ERR! boot_start fail\n", ld->name); break; } - err = qc_boot_post_process(pld); + err = qc_boot_post_process(dpld); if (err < 0) mif_info("%s: ERR! boot_post_process fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_UPLOAD_STEP1: - disable_irq_nosync(pld->irq); - err = qc_uload_step1(pld); + disable_irq_nosync(dpld->irq); + err = qc_uload_step1(dpld); if (err < 0) { - enable_irq(pld->irq); + enable_irq(dpld->irq); mif_info("%s: ERR! upload_step1 fail\n", ld->name); } break; case IOCTL_DPRAM_PHONE_UPLOAD_STEP2: - err = qc_uload_step2(pld, (void *)arg); + err = qc_uload_step2(dpld, (void *)arg); if (err < 0) { - enable_irq(pld->irq); + enable_irq(dpld->irq); mif_info("%s: ERR! upload_step2 fail\n", ld->name); } break; @@ -523,7 +524,7 @@ static int qc_ioctl(struct pld_link_device *pld, struct io_device *iod, } #endif -static struct pld_ext_op ext_op_set[] = { +static struct dpram_ext_op ext_op_set[] = { #if defined(CONFIG_CDMA_MODEM_MDM6600) [QC_MDM6600] = { .exist = 1, @@ -546,7 +547,7 @@ static struct pld_ext_op ext_op_set[] = { #endif }; -struct pld_ext_op *pld_get_ext_op(enum modem_t modem) +struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) { if (ext_op_set[modem].exist) return &ext_op_set[modem]; diff --git a/drivers/misc/modem_if/modem_link_device_shmem.h b/drivers/misc/modem_if/modem_link_device_shmem.h deleted file mode 100644 index 1f33c2a..0000000 --- a/drivers/misc/modem_if/modem_link_device_shmem.h +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright (C) 2010 Samsung Electronics. - * - * 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 __MODEM_LINK_DEVICE_SHMEM_H__ -#define __MODEM_LINK_DEVICE_SHMEM_H__ - -#include "modem_utils.h" -#include "modem_link_device_memory.h" - -#define SHM_BOOT_MAGIC 0x424F4F54 -#define SHM_DUMP_MAGIC 0x44554D50 -#define SHM_IPC_MAGIC 0xAA -#define SHM_PM_MAGIC 0x5F - -#define SHM_4M_RESERVED_SZ 4056 -#define SHM_4M_FMT_TX_BUFF_SZ 4096 -#define SHM_4M_FMT_RX_BUFF_SZ 4096 -#define SHM_4M_RAW_TX_BUFF_SZ 2084864 -#define SHM_4M_RAW_RX_BUFF_SZ 2097152 - -struct shmem_4mb_phys_map { - u32 magic; - u32 access; - - u32 fmt_tx_head; - u32 fmt_tx_tail; - - u32 fmt_rx_head; - u32 fmt_rx_tail; - - u32 raw_tx_head; - u32 raw_tx_tail; - - u32 raw_rx_head; - u32 raw_rx_tail; - - u8 reserved[SHM_4M_RESERVED_SZ]; - - u8 fmt_tx_buff[SHM_4M_FMT_TX_BUFF_SZ]; - u8 fmt_rx_buff[SHM_4M_FMT_RX_BUFF_SZ]; - - u8 raw_tx_buff[SHM_4M_RAW_TX_BUFF_SZ]; - u8 raw_rx_buff[SHM_4M_RAW_RX_BUFF_SZ]; -} __packed; - -struct shmem_circ { - u32 __iomem *head; - u32 __iomem *tail; - u8 __iomem *buff; - u32 size; -}; - -struct shmem_ipc_device { - char name[16]; - int id; - - struct shmem_circ txq; - struct shmem_circ rxq; - - u16 mask_req_ack; - u16 mask_res_ack; - u16 mask_send; - - int req_ack_rcvd; -}; - -struct shmem_ipc_map { - u32 __iomem *magic; - u32 __iomem *access; - - struct shmem_ipc_device dev[MAX_SIPC5_DEV]; - - u32 __iomem *mbx2ap; - u32 __iomem *mbx2cp; -}; - -struct shmem_link_device { - struct link_device ld; - - enum shmem_type type; - - /* SHMEM (SHARED MEMORY) address and size */ - u32 start; /* physical "start" address of SHMEM */ - u32 size; /* size of SHMEM */ - u8 __iomem *base; /* virtual address of the "start" */ - - /* SHMEM GPIO & IRQ */ - unsigned gpio_pda_active; - - unsigned gpio_ap_wakeup; - int irq_ap_wakeup; - unsigned gpio_ap_status; - - unsigned gpio_cp_wakeup; - unsigned gpio_cp_status; - int irq_cp_status; - - /* IPC device map */ - struct shmem_ipc_map ipc_map; - - /* Pointers (aliases) to IPC device map */ - u32 __iomem *magic; - u32 __iomem *access; - struct shmem_ipc_device *dev[MAX_SIPC5_DEV]; - u32 __iomem *mbx2ap; - u32 __iomem *mbx2cp; - - /* Wakelock for SHMEM device */ - struct wake_lock wlock; - char wlock_name[MIF_MAX_NAME_LEN]; - struct wake_lock ap_wlock; - char ap_wlock_name[MIF_MAX_NAME_LEN]; - struct wake_lock cp_wlock; - char cp_wlock_name[MIF_MAX_NAME_LEN]; - - /* for UDL */ - struct completion udl_cmpl; - struct std_dload_info dl_info; - - /* for CP crash dump */ - bool forced_cp_crash; - struct timer_list crash_ack_timer; - - /* for locking TX process */ - spinlock_t tx_lock[MAX_SIPC5_DEV]; - - /* for retransmission under SHMEM flow control after TXQ full state */ - atomic_t res_required[MAX_SIPC5_DEV]; - struct completion req_ack_cmpl[MAX_SIPC5_DEV]; - - /* for efficient RX process */ - struct tasklet_struct rx_tsk; - struct delayed_work ipc_rx_dwork; - struct delayed_work udl_rx_dwork; - struct io_device *iod[MAX_SIPC5_DEV]; - - /* for logging SHMEM status */ - struct mem_status_queue stat_list; - - /* for logging SHMEM dump */ - struct trace_data_queue trace_list; -#ifdef DEBUG_MODEM_IF - struct delayed_work dump_dwork; - char dump_path[MIF_MAX_PATH_LEN]; -#endif - - /* to hold/release "cp_wakeup" for PM (power-management) */ - struct delayed_work cp_sleep_dwork; - struct delayed_work link_off_dwork; - atomic_t ref_cnt; - spinlock_t pm_lock; -}; - -/* converts from struct link_device* to struct xxx_link_device* */ -#define to_shmem_link_device(linkdev) \ - container_of(linkdev, struct shmem_link_device, ld) - -#if 1 -#endif - -/** - * get_magic - * @shmd: pointer to an instance of shmem_link_device structure - * - * Returns the value of the "magic code" field. - */ -static inline u32 get_magic(struct shmem_link_device *shmd) -{ - return ioread32(shmd->magic); -} - -/** - * get_access - * @shmd: pointer to an instance of shmem_link_device structure - * - * Returns the value of the "access enable" field. - */ -static inline u32 get_access(struct shmem_link_device *shmd) -{ - return ioread32(shmd->access); -} - -/** - * set_magic - * @shmd: pointer to an instance of shmem_link_device structure - * @val: value to be written to the "magic code" field - */ -static inline void set_magic(struct shmem_link_device *shmd, u32 val) -{ - iowrite32(val, shmd->magic); -} - -/** - * set_access - * @shmd: pointer to an instance of shmem_link_device structure - * @val: value to be written to the "access enable" field - */ -static inline void set_access(struct shmem_link_device *shmd, u32 val) -{ - iowrite32(val, shmd->access); -} - -/** - * get_txq_head - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a head (in) pointer in a TX queue. - */ -static inline u32 get_txq_head(struct shmem_link_device *shmd, int id) -{ - return ioread32(shmd->dev[id]->txq.head); -} - -/** - * get_txq_tail - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a tail (out) pointer in a TX queue. - * - * It is useless for an AP to read a tail pointer in a TX queue twice to verify - * whether or not the value in the pointer is valid, because it can already have - * been updated by a CP after the first access from the AP. - */ -static inline u32 get_txq_tail(struct shmem_link_device *shmd, int id) -{ - return ioread32(shmd->dev[id]->txq.tail); -} - -/** - * get_txq_buff - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the start address of the buffer in a TXQ. - */ -static inline u8 *get_txq_buff(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->txq.buff; -} - -/** - * get_txq_buff_size - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the size of the buffer in a TXQ. - */ -static inline u32 get_txq_buff_size(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->txq.size; -} - -/** - * get_rxq_head - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a head (in) pointer in an RX queue. - * - * It is useless for an AP to read a head pointer in an RX queue twice to verify - * whether or not the value in the pointer is valid, because it can already have - * been updated by a CP after the first access from the AP. - */ -static inline u32 get_rxq_head(struct shmem_link_device *shmd, int id) -{ - return ioread32(shmd->dev[id]->rxq.head); -} - -/** - * get_rxq_tail - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the value of a tail (in) pointer in an RX queue. - */ -static inline u32 get_rxq_tail(struct shmem_link_device *shmd, int id) -{ - return ioread32(shmd->dev[id]->rxq.tail); -} - -/** - * get_rxq_buff - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the start address of the buffer in an RXQ. - */ -static inline u8 *get_rxq_buff(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->rxq.buff; -} - -/** - * get_rxq_buff_size - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the size of the buffer in an RXQ. - */ -static inline u32 get_rxq_buff_size(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->rxq.size; -} - -/** - * set_txq_head - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @in: value to be written to the head pointer in a TXQ - */ -static inline void set_txq_head(struct shmem_link_device *shmd, int id, u32 in) -{ - iowrite32(in, shmd->dev[id]->txq.head); -} - -/** - * set_txq_tail - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @out: value to be written to the tail pointer in a TXQ - */ -static inline void set_txq_tail(struct shmem_link_device *shmd, int id, u32 out) -{ - iowrite32(out, shmd->dev[id]->txq.tail); -} - -/** - * set_rxq_head - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @in: value to be written to the head pointer in an RXQ - */ -static inline void set_rxq_head(struct shmem_link_device *shmd, int id, u32 in) -{ - iowrite32(in, shmd->dev[id]->rxq.head); -} - -/** - * set_rxq_tail - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * @out: value to be written to the tail pointer in an RXQ - */ -static inline void set_rxq_tail(struct shmem_link_device *shmd, int id, u32 out) -{ - iowrite32(out, shmd->dev[id]->rxq.tail); -} - -/** - * get_mask_req_ack - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the REQ_ACK mask value for the IPC device. - */ -static inline u16 get_mask_req_ack(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->mask_req_ack; -} - -/** - * get_mask_res_ack - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the RES_ACK mask value for the IPC device. - */ -static inline u16 get_mask_res_ack(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->mask_res_ack; -} - -/** - * get_mask_send - * @shmd: pointer to an instance of shmem_link_device structure - * @id: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Returns the SEND mask value for the IPC device. - */ -static inline u16 get_mask_send(struct shmem_link_device *shmd, int id) -{ - return shmd->dev[id]->mask_send; -} - -#ifndef CONFIG_LINK_DEVICE_C2C -/** - * read_int2cp - * @shmd: pointer to an instance of shmem_link_device structure - * - * Returns the value of the AP-to-CP interrupt register. - */ -static inline u16 read_int2cp(struct shmem_link_device *shmd) -{ - if (shmd->mbx2cp) - return ioread16(shmd->mbx2cp); - else - return 0; -} -#endif - -/** - * reset_txq_circ - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Empties a TXQ by resetting the head (in) pointer with the value in the tail - * (out) pointer. - */ -static inline void reset_txq_circ(struct shmem_link_device *shmd, int dev) -{ - struct link_device *ld = &shmd->ld; - u32 head = get_txq_head(shmd, dev); - u32 tail = get_txq_tail(shmd, dev); - - mif_err("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", - ld->name, get_dev_name(dev), head, tail); - - set_txq_head(shmd, dev, tail); -} - -/** - * reset_rxq_circ - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * - * Empties an RXQ by resetting the tail (out) pointer with the value in the head - * (in) pointer. - */ -static inline void reset_rxq_circ(struct shmem_link_device *shmd, int dev) -{ - struct link_device *ld = &shmd->ld; - u32 head = get_rxq_head(shmd, dev); - u32 tail = get_rxq_tail(shmd, dev); - - mif_err("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", - ld->name, get_dev_name(dev), tail, head); - - set_rxq_tail(shmd, dev, head); -} - -/** - * ipc_active - * @shmd: pointer to an instance of shmem_link_device structure - * - * Returns whether or not IPC via the shmem_link_device instance is possible. - */ -static bool ipc_active(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - u32 magic = get_magic(shmd); - u32 access = get_access(shmd); - - /* Check link mode */ - if (unlikely(ld->mode != LINK_MODE_IPC)) { - mif_err("%s: ERR! ld->mode != LINK_MODE_IPC\n", - ld->name, CALLER); - return false; - } - - /* Check "magic code" and "access enable" values */ - if (unlikely(magic != SHM_IPC_MAGIC || access != 1)) { - mif_err("%s: ERR! magic:0x%X access:%d\n", - ld->name, CALLER, magic, access); - return false; - } - - return true; -} - -/** - * get_rxq_rcvd - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * OUT @circ: pointer to an instance of circ_status structure - * - * Stores {start address of the buffer in a RXQ, size of the buffer, in & out - * pointer values, size of received data} into the 'circ' instance. - * - * Returns an error code. - */ -static int get_rxq_rcvd(struct shmem_link_device *shmd, int dev, - struct mem_status *mst, struct circ_status *circ) -{ - struct link_device *ld = &shmd->ld; - - circ->buff = get_rxq_buff(shmd, dev); - circ->qsize = get_rxq_buff_size(shmd, dev); - circ->in = mst->head[dev][RX]; - circ->out = mst->tail[dev][RX]; - circ->size = circ_get_usage(circ->qsize, circ->in, circ->out); - - if (circ_valid(circ->qsize, circ->in, circ->out)) { - mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", - ld->name, get_dev_name(dev), circ->qsize, circ->in, - circ->out, circ->size); - return 0; - } else { - mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", - ld->name, get_dev_name(dev), circ->qsize, circ->in, - circ->out); - return -EIO; - } -} - -/** - * get_txq_space - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * OUT @circ: pointer to an instance of circ_status structure - * - * Stores {start address of the buffer in a TXQ, size of the buffer, in & out - * pointer values, size of free space} into the 'circ' instance. - * - * Returns the size of free space in the buffer or an error code. - */ -static int get_txq_space(struct shmem_link_device *shmd, int dev, - struct circ_status *circ) -{ - struct link_device *ld = &shmd->ld; - int cnt = 0; - u32 qsize; - u32 head; - u32 tail; - int space; - - while (1) { - qsize = get_txq_buff_size(shmd, dev); - head = get_txq_head(shmd, dev); - tail = get_txq_tail(shmd, dev); - space = circ_get_space(qsize, head, tail); - - mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", - ld->name, get_dev_name(dev), qsize, head, tail, space); - - if (circ_valid(qsize, head, tail)) - break; - - cnt++; - mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " - "space:%d}, count %d\n", - ld->name, get_dev_name(dev), qsize, head, tail, - space, cnt); - if (cnt >= MAX_RETRY_CNT) { - space = -EIO; - break; - } - - udelay(100); - } - - circ->buff = get_txq_buff(shmd, dev); - circ->qsize = qsize; - circ->in = head; - circ->out = tail; - circ->size = space; - - return space; -} - -/** - * get_txq_saved - * @shmd: pointer to an instance of shmem_link_device structure - * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) - * @mst: pointer to an instance of mem_status structure - * OUT @circ: pointer to an instance of circ_status structure - * - * Stores {start address of the buffer in a TXQ, size of the buffer, in & out - * pointer values, size of stored data} into the 'circ' instance. - * - * Returns an error code. - */ -static int get_txq_saved(struct shmem_link_device *shmd, int dev, - struct circ_status *circ) -{ - struct link_device *ld = &shmd->ld; - int cnt = 0; - u32 qsize; - u32 head; - u32 tail; - int saved; - - while (1) { - qsize = get_txq_buff_size(shmd, dev); - head = get_txq_head(shmd, dev); - tail = get_txq_tail(shmd, dev); - saved = circ_get_usage(qsize, head, tail); - - mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u saved:%u}\n", - ld->name, get_dev_name(dev), qsize, head, tail, saved); - - if (circ_valid(qsize, head, tail)) - break; - - cnt++; - mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " - "saved:%d}, count %d\n", - ld->name, get_dev_name(dev), qsize, head, tail, - saved, cnt); - if (cnt >= MAX_RETRY_CNT) { - saved = -EIO; - break; - } - - udelay(100); - } - - circ->buff = get_txq_buff(shmd, dev); - circ->qsize = qsize; - circ->in = head; - circ->out = tail; - circ->size = saved; - - return saved; -} - -/** - * clear_shmem_map - * @shmd: pointer to an instance of shmem_link_device structure - * - * Clears all pointers in every circular queue. - */ -static void clear_shmem_map(struct shmem_link_device *shmd) -{ - set_txq_head(shmd, IPC_FMT, 0); - set_txq_tail(shmd, IPC_FMT, 0); - set_rxq_head(shmd, IPC_FMT, 0); - set_rxq_tail(shmd, IPC_FMT, 0); - - set_txq_head(shmd, IPC_RAW, 0); - set_txq_tail(shmd, IPC_RAW, 0); - set_rxq_head(shmd, IPC_RAW, 0); - set_rxq_tail(shmd, IPC_RAW, 0); -} - -/** - * reset_shmem_ipc - * @shmd: pointer to an instance of shmem_link_device structure - * - * Reset SHMEM with IPC map. - */ -static void reset_shmem_ipc(struct shmem_link_device *shmd) -{ - set_access(shmd, 0); - - clear_shmem_map(shmd); - - atomic_set(&shmd->res_required[IPC_FMT], 0); - atomic_set(&shmd->res_required[IPC_RAW], 0); - - atomic_set(&shmd->ref_cnt, 0); - - set_magic(shmd, SHM_IPC_MAGIC); - set_access(shmd, 1); -} - -/** - * init_shmem_ipc - * @shmd: pointer to an instance of shmem_link_device structure - * - * Initializes IPC via SHMEM. - */ -static int init_shmem_ipc(struct shmem_link_device *shmd) -{ - struct link_device *ld = &shmd->ld; - - if (ld->mode == LINK_MODE_IPC && - get_magic(shmd) == SHM_IPC_MAGIC && - get_access(shmd) == 1) { - mif_err("%s: IPC already initialized\n", ld->name); - return 0; - } - - /* Initialize variables for efficient TX/RX processing */ - shmd->iod[IPC_FMT] = link_get_iod_with_format(ld, IPC_FMT); - shmd->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - - reset_shmem_ipc(shmd); - - if (get_magic(shmd) != SHM_IPC_MAGIC || get_access(shmd) != 1) - return -EACCES; - - return 0; -} - -#endif - diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c index 5a2d4b0..c4715e0 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.c +++ b/drivers/misc/modem_if/modem_link_device_spi.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -67,7 +66,8 @@ static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld) if (!spild->boot_done) return result; - if (!wake_lock_active(&spild->spi_wake_lock)) { + if (!wake_lock_active(&spild->spi_wake_lock) + && spild->send_modem_spi != 1) { wake_lock(&spild->spi_wake_lock); pr_debug("[SPI] [%s](%d) spi_wakelock locked . spild->spi_state[%d]\n", __func__, __LINE__, (int)spild->spi_state); @@ -311,7 +311,7 @@ static void spi_prepare_tx_packet(void) ld = &p_spild->ld; - for (i = 0; i < ld->max_ipc_dev; i++) { + for (i = 0; i < p_spild->max_ipc_dev; i++) { while ((skb = skb_dequeue(ld->skb_txq[i]))) { ret = spi_buff_write(p_spild, i, skb->data, skb->len); if (!ret) { @@ -331,7 +331,7 @@ static void spi_start_data_send(void) ld = &p_spild->ld; - for (i = 0; i < ld->max_ipc_dev; i++) { + for (i = 0; i < p_spild->max_ipc_dev; i++) { if (skb_queue_len(ld->skb_txq[i]) > 0) spi_send_work(SPI_WORK_SEND, SPI_WORK); } @@ -345,14 +345,6 @@ static void spi_tx_work(void) char *spi_sync_buf; spild = p_spild; - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (!iod) { - mif_err("no iodevice for modem control\n"); - return; - } - - if (iod->mc->phone_state == STATE_CRASH_EXIT) - return; /* check SUB SRDY, SRDY state */ if (gpio_get_value(spild->gpio_ipc_sub_srdy) == @@ -428,6 +420,8 @@ static void spi_tx_work(void) pr_err("[SPI] spi_dev_send fail\n"); /* add cp reset when spi sync fail */ + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (iod) iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -559,15 +553,6 @@ static void spi_rx_work(void) if (!spild) pr_err("[LNK/E] <%s> dpld == NULL\n", __func__); - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (!iod) { - mif_err("no iodevice for modem control\n"); - return; - } - - if (iod->mc->phone_state == STATE_CRASH_EXIT) - return; - if (!wake_lock_active(&spild->spi_wake_lock) || gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW || get_console_suspended() || @@ -621,7 +606,7 @@ static void spi_rx_work(void) /* parsing SPI packet */ if (spi_buff_read(spild) > 0) { /* call function for send data to IPC, RAW, RFS */ - for (i = 0; i < ld->max_ipc_dev; i++) { + for (i = 0; i < spild->max_ipc_dev; i++) { iod = spild->iod[i]; while ((skb = skb_dequeue(&spild->skb_rxq[i])) != NULL) { @@ -638,6 +623,8 @@ static void spi_rx_work(void) "spi sync failed"); /* add cp reset when spi sync fail */ + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (iod) iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -652,34 +639,6 @@ static void spi_rx_work(void) spi_start_data_send(); } - -static int spi_init_ipc(struct spi_link_device *spild) -{ - struct link_device *ld = &spild->ld; - - int i; - - /* Make aliases to each IO device */ - for (i = 0; i < MAX_DEV_FORMAT; i++) - spild->iod[i] = link_get_iod_with_format(ld, i); - - spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; - - /* List up the IO devices connected to each IPC channel */ - for (i = 0; i < MAX_DEV_FORMAT; i++) { - if (spild->iod[i]) - pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", - __func__, ld->name, i, spild->iod[i]->name); - else - pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", - __func__, ld->name, i); - } - - ld->mode = LINK_MODE_IPC; - - return 0; -} - unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len) { unsigned int i; @@ -1280,19 +1239,10 @@ err3: static void spi_send_modem_bin(struct work_struct *send_modem_w) { - struct spi_link_device *spild; - struct io_device *iod; int retval; struct image_buf img; unsigned long tick1, tick2 = 0; - spild = p_spild; - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (!iod) { - mif_err("no iodevice for modem control\n"); - return; - } - tick1 = jiffies_to_msecs(jiffies); retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img); @@ -1335,25 +1285,16 @@ static void spi_send_modem_bin(struct work_struct *send_modem_w) tick2 = jiffies_to_msecs(jiffies); pr_info("Downloading takes %lu msec\n", (tick2-tick1)); - spi_init_ipc(p_spild); - + complete_all(&p_spild->ril_init); sprd_boot_done = 1; p_spild->ril_send_cnt = 0; - p_spild->spi_state = SPI_STATE_IDLE; - if (iod) - iod->modem_state_changed(iod, - STATE_ONLINE); - return; err: - if (iod) - iod->modem_state_changed(iod, - STATE_OFFLINE); return; } -static inline int _request_mem(struct spi_v_buff *od) +static inline int _request_mem(struct ipc_spi *od) { if (!p_spild->p_virtual_buff) { od->mmio = vmalloc(od->size); @@ -1379,7 +1320,7 @@ void spi_tx_timer_callback(unsigned long param) { if (p_spild->spi_state == SPI_STATE_TX_WAIT) { p_spild->spi_timer_tx_state = SPI_STATE_TIME_OVER; - pr_debug("[SPI] spi_tx_timer_callback -timer expires\n"); + pr_err("[SPI] spi_tx_timer_callback -timer expires\n"); } } @@ -1387,7 +1328,7 @@ void spi_rx_timer_callback(unsigned long param) { if (p_spild->spi_state == SPI_STATE_RX_WAIT) { p_spild->spi_timer_rx_state = SPI_STATE_TIME_OVER; - pr_debug("[SPI] spi_rx_timer_callback -timer expires\n"); + pr_err("[SPI] spi_rx_timer_callback -timer expires\n"); } } @@ -1440,37 +1381,96 @@ static void spi_work(struct work_struct *work) } } -static int link_pm_notifier_event(struct notifier_block *this, - unsigned long event, void *ptr) +static int spi_init_ipc(struct spi_link_device *spild) { - struct io_device *iod; - struct link_pm_data *pm_data = - container_of(this, struct link_pm_data, pm_notifier); + struct link_device *ld = &spild->ld; + + int i; + + /* Make aliases to each IO device */ + for (i = 0; i < MAX_DEV_FORMAT; i++) + spild->iod[i] = link_get_iod_with_format(ld, i); + + spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; - iod = link_get_iod_with_format(&pm_data->spild->ld, IPC_FMT); - if (!iod) { - pr_err("no iodevice for modem control\n"); - return NOTIFY_BAD; + /* List up the IO devices connected to each IPC channel */ + for (i = 0; i < MAX_DEV_FORMAT; i++) { + if (spild->iod[i]) + pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", + __func__, ld->name, i, spild->iod[i]->name); + else + pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", + __func__, ld->name, i); } - if (!gpio_get_value(iod->mc->gpio_phone_active)) - return NOTIFY_DONE; + ld->mode = LINK_MODE_IPC; - switch (event) { - case PM_SUSPEND_PREPARE: - /* set TD PDA Active High if previous state was LPA */ - mif_info("TD PDA active low to LPA suspend spot\n"); - gpio_set_value(iod->mc->gpio_pda_active, 0); + return 0; +} - return NOTIFY_OK; - case PM_POST_SUSPEND: - /* LPA to Kernel suspend and User Freezing task fail resume, - restore to LPA GPIO states. */ - mif_info("TD PDA active High to LPA GPIO state\n"); - gpio_set_value(iod->mc->gpio_pda_active, 1); - return NOTIFY_OK; +static int spi_thread(void *data) +{ + struct spi_link_device *spild = (struct spi_link_device *)data; + + if (lpcharge == 1) { + pr_err("[LPM MODE] spi_thread_exit!\n"); + return 0; } - return NOTIFY_DONE; + + daemonize("spi_thread"); + + pr_info("[%s] spi_thread start.\n", __func__); + p_spild->boot_done = 1; + + wait_for_completion(&p_spild->ril_init); + + pr_info("[%s] ril_init completed.\n", __func__); + + pr_info("<%s> wait 2 sec... srdy : %d\n", + __func__, gpio_get_value(spild->gpio_ipc_srdy)); + msleep_interruptible(1700); + + while (gpio_get_value(spild->gpio_ipc_srdy)) + ; + pr_info("(%s) cp booting... Done.\n", __func__); + + spi_init_ipc(spild); + + pr_info("[spi_thread] Start IPC Communication. SRDY : %d\n", + gpio_get_value(spild->gpio_ipc_srdy)); + + /* CP booting is already completed, just set submrdy to high */ + if (gpio_get_value(spild->gpio_ipc_sub_srdy) == SPI_GPIOLEVEL_HIGH) { + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); + pr_err("[spi_thread] CP booting is already completed\n"); + } + /* CP booting is not completed. + set submrdy to high and wait until subsrdy is high */ + else { + pr_err("[spi_thread] CP booting is not completed. wait...\n"); + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); + do { + msleep_interruptible(5); + } while (gpio_get_value(spild->gpio_ipc_sub_srdy) == + SPI_GPIOLEVEL_LOW); + + pr_err("[spi_thread] CP booting is done...\n"); + } + + if (p_spild->spi_is_restart) + msleep_interruptible(100); + else + msleep_interruptible(30); + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); + + pr_info("(%s) spi sync done.\n", __func__); + + spild->spi_state = SPI_STATE_IDLE; + p_spild->spi_is_restart = 0; + + return 0; } static int spi_probe(struct spi_device *spi) @@ -1512,7 +1512,8 @@ static struct spi_driver spi_driver = { static int spi_link_init(void) { int ret; - struct spi_v_buff *od; + struct ipc_spi *od; + struct task_struct *th; struct link_device *ld = &p_spild->ld; p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy; @@ -1521,7 +1522,7 @@ static int spi_link_init(void) p_spild->gpio_ipc_mrdy, p_spild->gpio_modem_bin_srdy, gpio_get_value(p_spild->gpio_ipc_srdy)); - od = kzalloc(sizeof(struct spi_v_buff), GFP_KERNEL); + od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL); if (!od) { pr_err("(%d) failed to allocate device\n", __LINE__); ret = -ENOMEM; @@ -1536,6 +1537,7 @@ static int spi_link_init(void) if (ret) goto err; + init_completion(&p_spild->ril_init); sema_init(&p_spild->srdy_sem, 0); INIT_WORK(&p_spild->send_modem_w, @@ -1561,7 +1563,12 @@ static int spi_link_init(void) if (ret) goto err; - p_spild->boot_done = 1; + th = kthread_create(spi_thread, (void *)p_spild, "spi_thread"); + if (IS_ERR(th)) { + pr_err("kernel_thread() failed\n"); + goto err; + } + wake_up_process(th); pr_info("[%s] Done\n", __func__); return 0; @@ -1584,6 +1591,7 @@ void spi_set_restart(void) gpio_set_value(p_spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); p_spild->spi_state = SPI_STATE_END; + p_spild->spi_is_restart = 1; /* Flush SPI work queue */ flush_workqueue(p_spild->spi_wq); @@ -1627,35 +1635,6 @@ exit: } EXPORT_SYMBOL(spi_thread_restart); -static int spi_link_pm_init(struct spi_link_device *spild, - struct platform_device *pdev) -{ - struct modem_data *pdata = - (struct modem_data *)pdev->dev.platform_data; - struct modemlink_pm_data *pm_pdata; - struct link_pm_data *pm_data = - kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); - - if (!pdata || !pdata->link_pm_data) { - mif_err("platform data is NULL\n"); - return -EINVAL; - } - pm_pdata = pdata->link_pm_data; - - if (!pm_data) { - mif_err("link_pm_data is NULL\n"); - return -ENOMEM; - } - - pm_data->spild = spild; - spild->link_pm_data = pm_data; - - pm_data->pm_notifier.notifier_call = link_pm_notifier_event; - register_pm_notifier(&pm_data->pm_notifier); - - return 0; -} - struct link_device *spi_create_link_device(struct platform_device *pdev) { struct spi_link_device *spild = NULL; @@ -1710,9 +1689,9 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) } spild->spi_state = SPI_STATE_END; - ld->max_ipc_dev = (IPC_RFS + 1); /* FMT, RAW, RFS */ + spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */ - for (i = 0; i < ld->max_ipc_dev; i++) + for (i = 0; i < spild->max_ipc_dev; i++) skb_queue_head_init(&spild->skb_rxq[i]); /* Prepare a clean buffer for SPI access */ @@ -1759,11 +1738,6 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) goto err; } - /* create link pm device */ - ret = spi_link_pm_init(spild, pdev); - if (ret) - goto err; - /* Create SPI device */ ret = spi_link_init(); if (ret) diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h index a8f9cd5..210d815 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.h +++ b/drivers/misc/modem_if/modem_link_device_spi.h @@ -135,24 +135,20 @@ struct spi_data_packet_header { unsigned long more:1; }; -struct link_pm_data { - struct miscdevice miscdev; - struct spi_link_device *spild; - - struct notifier_block pm_notifier; -}; - struct spi_link_device { struct link_device ld; + /* Link to SPI control functions dependent on each platform */ + int max_ipc_dev; + /* Wakelock for SPI device */ struct wake_lock spi_wake_lock; - /* Workqueue for modem bin transfers */ struct workqueue_struct *ipc_spi_wq; /* SPI state */ int spi_state; + int spi_is_restart; /* SPI Timer state */ int spi_timer_tx_state; @@ -186,13 +182,11 @@ struct spi_link_device { struct io_device *iod[MAX_DEV_FORMAT]; struct sk_buff_head skb_rxq[MAX_DEV_FORMAT]; - /* LINK PM DEVICE DATA */ - struct link_pm_data *link_pm_data; - /* Multi-purpose miscellaneous buffer */ u8 *buff; u8 *sync_buff; + struct completion ril_init; struct semaphore srdy_sem; int send_modem_spi; @@ -208,12 +202,6 @@ struct spi_link_device { #define to_spi_link_device(linkdev) \ container_of(linkdev, struct spi_link_device, ld) -struct spi_v_buff { - unsigned long base; - unsigned long size; - void __iomem *mmio; -}; - extern unsigned int lpcharge; extern int get_console_suspended(void); static void spi_work(struct work_struct *work); diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c index 0fd779c..14aee9f 100644 --- a/drivers/misc/modem_if/modem_link_device_usb.c +++ b/drivers/misc/modem_if/modem_link_device_usb.c @@ -13,7 +13,7 @@ * */ -#define DEBUG +/* #define DEBUG */ #include #include @@ -297,15 +297,10 @@ static void usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state) { struct io_device *iod; - struct io_device *bootd; iod = link_get_iod_with_format(&usb_ld->ld, IPC_FMT); if (iod) iod->modem_state_changed(iod, state); - - bootd = usb_ld->ld.mc->bootd; - if (bootd) - bootd->modem_state_changed(bootd, state); } static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld, @@ -629,12 +624,7 @@ static void if_usb_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work); usb_put_dev(usbdev); usb_ld->usbdev = NULL; - if (!has_hub(usb_ld)) { - if (pm_data->root_hub) - pm_runtime_forbid(pm_data->root_hub); - schedule_delayed_work(&usb_ld->wait_enumeration, - WAIT_ENUMURATION_TIMEOUT_JIFFIES); - } + pm_runtime_forbid(pm_data->root_hub); } } diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c index c63f08e..75ad970 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.c +++ b/drivers/misc/modem_if/modem_link_pm_usb.c @@ -12,7 +12,7 @@ * */ -#define DEBUG +/* #define DEBUG */ #include #include @@ -27,15 +27,12 @@ #include "modem_link_pm_usb.h" -int during_hub_resume; - static inline void start_hub_work(struct link_pm_data *pm_data, int delay) { if (pm_data->hub_work_running == false) { pm_data->hub_work_running = true; wake_lock(&pm_data->hub_lock); mif_debug("link_pm_hub_work is started\n"); - during_hub_resume = 1; } schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay)); @@ -84,13 +81,12 @@ void link_pm_preactive(struct link_pm_data *pm_data) static void link_pm_hub_work(struct work_struct *work) { - int err, cnt; + int err; struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_hub.work); if (pm_data->hub_status == HUB_STATE_ACTIVE) { end_hub_work(pm_data); - during_hub_resume = 0; return; } @@ -115,16 +111,7 @@ static void link_pm_hub_work(struct work_struct *work) /* skip 1st time before first probe */ if (pm_data->root_hub) pm_runtime_get_sync(pm_data->root_hub); - - for (cnt=0;cnt<5;cnt++) { - err = pm_data->port_enable(2, 1); - if (err >= 0) { - mif_err("hub on success\n"); - break; - } - mif_err("hub on fail %d th\n", cnt); - msleep(100); - } + err = pm_data->port_enable(2, 1); if (err < 0) { mif_err("hub on fail err=%d\n", err); err = pm_data->port_enable(2, 0); @@ -145,8 +132,6 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); - - mif_err("USB Hub resume fail !!!\n"); end_hub_work(pm_data); } else { mif_info("hub resumming: %d\n", @@ -179,6 +164,9 @@ static int link_pm_hub_standby(void *args) /* this function is atomic. * make force disconnect in workqueue.. */ + if (pm_data->usb_ld->if_usb_connected) + schedule_work(&usb_ld->disconnect_work); + return err; } @@ -222,7 +210,6 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, sizeof(int))) return -EFAULT; gpio_set_value(pm_data->gpio_link_active, value); - mif_info("> H-ACT %d\n", value); break; case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); @@ -246,7 +233,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, case IOCTL_LINK_PORT_OFF: err = link_pm_hub_standby(pm_data); if (err < 0) { - mif_err("usb3503 standby fail\n"); + mif_err("usb3503 active fail\n"); goto exit; } pm_data->hub_init_lock = 1; @@ -376,8 +363,6 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->miscdev.name = "link_pm"; pm_data->miscdev.fops = &link_pm_fops; - during_hub_resume = 0; - err = misc_register(&pm_data->miscdev); if (err < 0) { mif_err("fail to register pm device(%d)\n", err); diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c index 1686043..2617be8 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c @@ -1,4 +1,6 @@ -/* +/* /linux/drivers/misc/modem_if/modem_modemctl_device_cbp7.1.c + * + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -29,6 +31,7 @@ #define PIF_TIMEOUT (180 * HZ) #define DPRAM_INIT_TIMEOUT (30 * HZ) + static irqreturn_t phone_active_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; @@ -160,13 +163,13 @@ static int cbp72_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - + struct dpram_link_device *dpld = to_dpram_link_device(ld); mif_debug("\n"); /* Wait here until the PHONE is up. * Waiting as the this called from IOCTL->UM thread */ mif_info("Waiting for INT_CMD_PHONE_START\n"); - ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, - DPRAM_INIT_TIMEOUT); + ret = wait_for_completion_interruptible_timeout( + &dpld->dpram_init_cmd, DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ mif_err("Timeout!!! (PHONE_START was not arrived.)\n"); @@ -174,8 +177,8 @@ static int cbp72_boot_off(struct modem_ctl *mc) } mif_info("Waiting for INT_CMD_PIF_INIT_DONE\n"); - ret = wait_for_completion_interruptible_timeout(&ld->pif_cmpl, - PIF_TIMEOUT); + ret = wait_for_completion_interruptible_timeout( + &dpld->modem_pif_init_done, PIF_TIMEOUT); if (!ret) { mif_err("Timeout!!! (PIF_INIT_DONE was not arrived.)\n"); return -ENXIO; @@ -196,6 +199,10 @@ static int cbp72_force_crash_exit(struct modem_ctl *mc) /* Make DUMP start */ ld->force_dump(ld, mc->bootd); + msleep_interruptible(1000); + + mc->bootd->modem_state_changed(mc->bootd, STATE_CRASH_EXIT); + return 0; } @@ -214,15 +221,16 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_off = pdata->gpio_cp_off; - mc->gpio_reset_req_n = pdata->gpio_reset_req_n; - mc->gpio_cp_reset = pdata->gpio_cp_reset; - mc->gpio_pda_active = pdata->gpio_pda_active; - mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; - mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; + struct platform_device *pdev = NULL; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_reset_req_n = pdata->gpio_reset_req_n; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset; if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { @@ -237,9 +245,10 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cbp72_get_ops(mc); - mc->irq_phone_active = pdata->irq_phone_active; + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); if (!mc->irq_phone_active) { - mif_err("get irq_phone_active fail\n"); + mif_err("get irq fail\n"); return -1; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp82.c b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c deleted file mode 100644 index ee90232..0000000 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp82.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2010 Samsung Electronics. - * - * 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 "modem_prj.h" -#include "modem_utils.h" - -#define DPRAM_INIT_TIMEOUT (30 * HZ) -#define PIF_TIMEOUT (180 * HZ) - -static irqreturn_t phone_active_handler(int irq, void *arg) -{ - struct modem_ctl *mc = (struct modem_ctl *)arg; - int cp_on = gpio_get_value(mc->gpio_cp_on); - int cp_reset = gpio_get_value(mc->gpio_cp_reset); - int cp_active = gpio_get_value(mc->gpio_phone_active); - int old_state = mc->phone_state; - int new_state = mc->phone_state; - - mif_info("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", - get_cp_state_str(old_state), cp_on, cp_reset, cp_active); - - if (cp_reset && cp_active) { - if (mc->phone_state == STATE_BOOTING) { - new_state = STATE_ONLINE; - mc->bootd->modem_state_changed(mc->bootd, new_state); - } - } else if (cp_reset && !cp_active) { - if (mc->phone_state == STATE_ONLINE) { - new_state = STATE_CRASH_EXIT; - mc->bootd->modem_state_changed(mc->bootd, new_state); - } - } else { - new_state = STATE_OFFLINE; - if (mc->bootd && mc->bootd->modem_state_changed) - mc->bootd->modem_state_changed(mc->bootd, new_state); - } - - if (new_state != old_state) { - mif_err("%s: phone_state changed (%s -> %s\n)", - mc->name, get_cp_state_str(old_state), - get_cp_state_str(new_state)); - } - - return IRQ_HANDLED; -} - -static int cbp82_on(struct modem_ctl *mc) -{ - int cp_on = gpio_get_value(mc->gpio_cp_on); - int cp_off = gpio_get_value(mc->gpio_cp_off); - int cp_reset = gpio_get_value(mc->gpio_cp_reset); - int cp_active = gpio_get_value(mc->gpio_phone_active); - mif_err("+++\n"); - - mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", - get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, - cp_active); - - /* prevent sleep during bootloader downloading */ - if (!wake_lock_active(&mc->mc_wake_lock)) - wake_lock(&mc->mc_wake_lock); - - gpio_set_value(mc->gpio_cp_on, 0); - gpio_set_value(mc->gpio_cp_off, 1); - gpio_set_value(mc->gpio_cp_reset, 0); - - msleep(500); - - cp_on = gpio_get_value(mc->gpio_cp_on); - cp_off = gpio_get_value(mc->gpio_cp_off); - cp_reset = gpio_get_value(mc->gpio_cp_reset); - cp_active = gpio_get_value(mc->gpio_phone_active); - mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", - get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, - cp_active); - - gpio_set_value(mc->gpio_cp_off, 0); - gpio_set_value(mc->gpio_cp_on, 1); - - msleep(100); - - gpio_set_value(mc->gpio_cp_reset, 1); - - msleep(300); - - cp_on = gpio_get_value(mc->gpio_cp_on); - cp_off = gpio_get_value(mc->gpio_cp_off); - cp_reset = gpio_get_value(mc->gpio_cp_reset); - cp_active = gpio_get_value(mc->gpio_phone_active); - mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", - get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, - cp_active); - - if (mc->gpio_pda_active) - gpio_set_value(mc->gpio_pda_active, 1); - - if (mc->bootd) - mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); - else - mif_err("no mc->bootd\n"); - - mif_err("---\n"); - return 0; -} - -static int cbp82_off(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - mif_err("+++\n"); - - gpio_set_value(mc->gpio_cp_reset, 0); - gpio_set_value(mc->gpio_cp_on, 0); - gpio_set_value(mc->gpio_cp_off, 1); - - mc->bootd->modem_state_changed(mc->bootd, STATE_OFFLINE); - ld->mode = LINK_MODE_OFFLINE; - - mif_err("---\n"); - return 0; -} - -static int cbp82_reset(struct modem_ctl *mc) -{ - int ret = 0; - - mif_debug("cbp82_reset()\n"); - - ret = cbp82_off(mc); - if (ret) - return -ENXIO; - - msleep(100); - - ret = cbp82_on(mc); - if (ret) - return -ENXIO; - - return 0; -} - -static int cbp82_boot_on(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - mif_info("+++\n"); - - ld->mode = LINK_MODE_BOOT; - - mif_info("---\n"); - return 0; -} - -static int cbp82_boot_off(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - int ret; - mif_err("+++\n"); - - /* Wait here until the PHONE is up. - * Waiting as the this called from IOCTL->UM thread */ - mif_err("Waiting for PHONE_START\n"); - ret = wait_for_completion_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); - if (!ret) { - /* ret == 0 on timeout */ - mif_err("T-I-M-E-O-U-T (PHONE_START)\n"); - cbp82_off(mc); - ret = -EIO; - goto exit; - } - mif_err("recv PHONE_START\n"); - - mif_err("Waiting for PIF_INIT_DONE\n"); - ret = wait_for_completion_timeout(&ld->pif_cmpl, PIF_TIMEOUT); - if (!ret) { - /* ret == 0 on timeout */ - mif_err("T-I-M-E-O-U-T (PIF_INIT_DONE)!!!\n"); - cbp82_off(mc); - ret = -EIO; - goto exit; - } - mif_err("recv PIF_INIT_DONE\n"); - - mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE); - ret = 0; - -exit: - wake_unlock(&mc->mc_wake_lock); - mif_err("---\n"); - return ret; -} - -static int cbp82_force_crash_exit(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - - mif_err("device = %s\n", mc->bootd->name); - - /* Make DUMP start */ - ld->force_dump(ld, mc->bootd); - - return 0; -} - -static void cbp82_get_ops(struct modem_ctl *mc) -{ - mc->ops.modem_on = cbp82_on; - mc->ops.modem_off = cbp82_off; - mc->ops.modem_reset = cbp82_reset; - mc->ops.modem_boot_on = cbp82_boot_on; - mc->ops.modem_boot_off = cbp82_boot_off; - mc->ops.modem_force_crash_exit = cbp82_force_crash_exit; -} - -int cbp82_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) -{ - int ret = 0; - int irq = 0; - unsigned long flag = 0; - mif_err("+++\n"); - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_off = pdata->gpio_cp_off; - mc->gpio_cp_reset = pdata->gpio_cp_reset; - mc->gpio_phone_active = pdata->gpio_phone_active; - - if (!mc->gpio_cp_on || !mc->gpio_cp_off || !mc->gpio_cp_reset - || !mc->gpio_phone_active) { - mif_err("no GPIO data\n"); - mif_err("---\n"); - return -ENXIO; - } - - mc->gpio_pda_active = pdata->gpio_pda_active; - - gpio_set_value(mc->gpio_cp_reset, 0); - gpio_set_value(mc->gpio_cp_off, 1); - gpio_set_value(mc->gpio_cp_on, 0); - - cbp82_get_ops(mc); - - wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp82_wake_lock"); - - mc->irq_phone_active = pdata->irq_phone_active; - if (!mc->irq_phone_active) { - mif_err("get irq fail\n"); - mif_err("---\n"); - return -1; - } - mif_info("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); - - irq = mc->irq_phone_active; - flag = IRQF_NO_SUSPEND | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; - ret = request_irq(irq, phone_active_handler, flag, "cdma_active", mc); - if (ret) { - mif_err("request_irq fail (%d)\n", ret); - mif_err("---\n"); - return ret; - } - - ret = enable_irq_wake(irq); - if (ret) - mif_err("enable_irq_wake fail (%d)\n", ret); - - mif_err("---\n"); - return 0; -} - diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c index df18c83..fe8ce69 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c @@ -1,4 +1,6 @@ -/* +/* /linux/drivers/misc/modem_if/modem_modemctl_device_cmc221.c + * + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -11,15 +13,15 @@ * GNU General Public License for more details. * */ - #include + #include #include #include #include #include -#include +#include #include "modem_prj.h" #include "modem_link_device_usb.h" #include "modem_link_device_dpram.h" @@ -75,7 +77,7 @@ static irqreturn_t phone_active_handler(int irq, void *arg) static irqreturn_t dynamic_switching_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; - int txpath = gpio_get_value(mc->gpio_link_switch); + int txpath = gpio_get_value(mc->gpio_dynamic_switching); bool enumerated = usb_is_enumerated(mc->msd); mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated); @@ -92,52 +94,6 @@ static irqreturn_t dynamic_switching_handler(int irq, void *arg) return IRQ_HANDLED; } -#ifdef CONFIG_EXYNOS4_CPUFREQ /* Set cpu clock to 800MHz for high TP */ -static void cmc221_cpufreq_lock(struct work_struct *work) -{ - struct modem_ctl *mc; - - mc = container_of(work, struct modem_ctl, work_cpu_lock.work); - if (mc->mdm_data->link_pm_data->freq_lock) { - mif_debug("Call freq lock func.\n"); - mc->mdm_data->link_pm_data->freq_lock(mc->dev); - - cancel_delayed_work(&mc->work_cpu_unlock); - schedule_delayed_work(&mc->work_cpu_unlock, - msecs_to_jiffies(5000)); - } -} - -static void cmc221_cpufreq_unlock(struct work_struct *work) -{ - struct modem_ctl *mc; - int tp_level; - - mc = container_of(work, struct modem_ctl, work_cpu_unlock.work); - tp_level = gpio_get_value(mc->gpio_cpufreq_lock); - - mif_debug("TP Level is (%d)\n", tp_level); - if (tp_level) { - mif_debug("maintain cpufreq lock !!!\n"); - schedule_delayed_work(&mc->work_cpu_unlock, - msecs_to_jiffies(5000)); - } else { - if (mc->mdm_data->link_pm_data->freq_unlock) { - mif_debug("Call freq unlock func.\n"); - mc->mdm_data->link_pm_data->freq_unlock(mc->dev); - } - } -} - -static irqreturn_t cpufreq_lock_handler(int irq, void *arg) -{ - struct modem_ctl *mc = (struct modem_ctl *)arg; - - schedule_delayed_work(&mc->work_cpu_lock, 0); - return IRQ_HANDLED; -} -#endif - static int cmc221_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); @@ -248,10 +204,11 @@ static int cmc221_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); + struct dpram_link_device *dpld = to_dpram_link_device(ld); mif_err("%s\n", mc->name); - ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, + ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd, DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ @@ -292,23 +249,22 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; + struct platform_device *pdev = NULL; - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_reset = pdata->gpio_cp_reset; mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_pda_active = pdata->gpio_pda_active; #if 0 /*TODO: check the GPIO map*/ - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup; - mc->gpio_host_active = pdata->gpio_host_active; - mc->gpio_host_wakeup = pdata->gpio_host_wakeup; + mc->gpio_host_active = pdata->gpio_host_active; + mc->gpio_host_wakeup = pdata->gpio_host_wakeup; #endif - mc->gpio_link_switch = pdata->gpio_link_switch; + mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching; mc->need_switch_to_usb = false; -#ifdef CONFIG_EXYNOS4_CPUFREQ - mc->gpio_cpufreq_lock = pdata->gpio_cpufreq_lock; -#endif + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { mif_err("%s: ERR! no GPIO data\n", mc->name); return -ENXIO; @@ -320,9 +276,10 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cmc221_get_ops(mc); dev_set_drvdata(mc->dev, mc); - mc->irq_phone_active = pdata->irq_phone_active; + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); if (!mc->irq_phone_active) { - mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + mif_err("%s: ERR! get cp_active_irq fail\n", mc->name); return -1; } mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active); @@ -344,8 +301,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } flag = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND; - if (mc->gpio_link_switch) { - irq = gpio_to_irq(mc->gpio_link_switch); + if (mc->gpio_dynamic_switching) { + irq = gpio_to_irq(mc->gpio_dynamic_switching); mif_err("%s: DYNAMIC_SWITCH IRQ# = %d\n", mc->name, irq); ret = request_irq(irq, dynamic_switching_handler, flag, "dynamic_switching", mc); @@ -356,23 +313,5 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } } -#ifdef CONFIG_EXYNOS4_CPUFREQ - INIT_DELAYED_WORK(&mc->work_cpu_lock, cmc221_cpufreq_lock); - INIT_DELAYED_WORK(&mc->work_cpu_unlock, cmc221_cpufreq_unlock); - - flag = IRQF_TRIGGER_RISING; - if (mc->gpio_cpufreq_lock) { - irq = gpio_to_irq(mc->gpio_cpufreq_lock); - mif_err("%s: CPUFREQ_LOCK_CNT IRQ# = %d\n", mc->name, irq); - ret = request_irq(irq, cpufreq_lock_handler, flag, - "cpufreq_lock", mc); - if (ret) { - mif_err("%s: ERR! request_irq(#%d) fail (err %d)\n", - mc->name, irq, ret); - return ret; - } - } -#endif - return 0; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c index c5835cb..5a42755 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c +++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c @@ -115,13 +115,6 @@ static int esc6270_reset(struct modem_ctl *mc) int esc6270_boot_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); -#if defined(CONFIG_LINK_DEVICE_DPRAM) - /* clear intr */ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u16 recv_msg = dpld->recv_intr(dpld); - - pr_info("[MODEM_IF:ESC] dpram intr: %x\n", recv_msg); -#endif pr_info("[MODEM_IF:ESC] <%s>\n", __func__); @@ -280,11 +273,8 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); - mc->irq_phone_active = pdata->irq_phone_active; - if (!mc->irq_phone_active) { - mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); - return -1; - } + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -312,7 +302,7 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = pdata->irq_sim_detect; + mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c index 41ead40..ad44579 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c +++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c @@ -30,8 +30,6 @@ #include -#include "modem_link_device_pld.h" - #if defined(CONFIG_MACH_M0_CTC) #include #endif @@ -41,8 +39,6 @@ static int mdm6600_on(struct modem_ctl *mc) { - struct link_device *ld = get_current_link(mc->iod); - pr_info("[MODEM_IF] mdm6600_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_reset_msm || !mc->gpio_cp_on) { @@ -62,7 +58,6 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_pda_active, 1); mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); - ld->mode = LINK_MODE_BOOT; return 0; } @@ -87,8 +82,7 @@ static int mdm6600_off(struct modem_ctl *mc) static int mdm6600_reset(struct modem_ctl *mc) { - struct link_device *ld = get_current_link(mc->iod); - /* int ret; */ + int ret; pr_info("[MODEM_IF] mdm6600_reset()\n"); @@ -115,9 +109,6 @@ static int mdm6600_reset(struct modem_ctl *mc) msleep(40); /* > 37.2 + 2 msec */ } - mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); - ld->mode = LINK_MODE_BOOT; - return 0; } @@ -168,7 +159,6 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int cp_dump_value = 0; int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; - struct link_device *ld; if (!mc->gpio_cp_reset || !mc->gpio_phone_active /*|| !mc->gpio_cp_dump_int */) { @@ -189,6 +179,11 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) } else if (phone_reset && !phone_active_value) { if (count == 1) { phone_state = STATE_CRASH_EXIT; + if (mc->iod) { + ld = get_current_link(mc->iod); + if (ld->terminate_comm) + ld->terminate_comm(ld, mc->iod); + } if (mc->iod && mc->iod->modem_state_changed) mc->iod->modem_state_changed (mc->iod, phone_state); @@ -231,11 +226,8 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->vbus_on = pdata->vbus_on; mc->vbus_off = pdata->vbus_off; - mc->irq_phone_active = pdata->irq_phone_active; - if (!mc->irq_phone_active) { - mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); - return -1; - } + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); pr_info("[MODEM_IF] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -340,7 +332,7 @@ static int mdm6600_on(struct modem_ctl *mc) return -ENXIO; } - gpio_set_value(mc->gpio_pda_active, 1); + gpio_set_value(mc->gpio_pda_active, 0); gpio_set_value(mc->gpio_cp_on, 1); msleep(500); @@ -354,6 +346,8 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_on, 0); msleep(500); + gpio_set_value(mc->gpio_pda_active, 1); + #if defined(CONFIG_LINK_DEVICE_PLD) gpio_set_value(mc->gpio_fpga_cs_n, 1); #endif @@ -426,13 +420,9 @@ static int mdm6600_reset(struct modem_ctl *mc) static int mdm6600_boot_on(struct modem_ctl *mc) { struct regulator *regulator; - struct link_device *ld = get_current_link(mc->iod); - struct pld_link_device *dpld = to_pld_link_device(ld); pr_info("[MSM] <%s>\n", __func__); - dpld->recv_intr(dpld); - if (!mc->gpio_flm_uart_sel) { pr_err("[MSM] no gpio data\n"); return -ENXIO; @@ -739,11 +729,8 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); - mc->irq_phone_active = pdata->irq_phone_active; - if (!mc->irq_phone_active) { - mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); - return -1; - } + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); pr_info("[MSM] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -767,7 +754,7 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = pdata->irq_sim_detect; + mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c deleted file mode 100644 index 6078916..0000000 --- a/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c +++ /dev/null @@ -1,218 +0,0 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c - * - * Copyright (C) 2010 Samsung Electronics. - * - * 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 "modem_prj.h" -#include "modem_link_device_dpram.h" -#include "modem_utils.h" - -#define IDPRAM_NORMAL_BOOT_MAGIC 0x4D4E - -static irqreturn_t phone_active_irq_handler(int irq, void *arg) -{ - struct modem_ctl *mc = (struct modem_ctl *)arg; - int phone_reset = gpio_get_value(mc->gpio_cp_reset); - int phone_active = gpio_get_value(mc->gpio_phone_active); - int phone_state = mc->phone_state; - - pr_info("MIF: <%s> state = %d, phone_reset = %d, phone_active = %d\n", - __func__, phone_state, phone_reset, phone_active); - - if (phone_reset && phone_active) { - phone_state = STATE_ONLINE; - if (mc->phone_state != STATE_BOOTING) - mc->iod->modem_state_changed(mc->iod, phone_state); - } else if (phone_reset && !phone_active) { - if (mc->phone_state == STATE_ONLINE) { - phone_state = STATE_CRASH_EXIT; - mc->iod->modem_state_changed(mc->iod, phone_state); - } - } else { - phone_state = STATE_OFFLINE; - if (mc->iod && mc->iod->modem_state_changed) - mc->iod->modem_state_changed(mc->iod, phone_state); - } - - if (phone_active) - irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW); - else - irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH); - - pr_info("MIF: <%s> phone_state = %d\n", __func__, phone_state); - - return IRQ_HANDLED; -} - -static void set_idpram_boot_magic(struct dpram_link_device *dpld) -{ - dpld->set_access(dpld, 0); - dpld->set_magic(dpld, IDPRAM_NORMAL_BOOT_MAGIC); - dpld->set_access(dpld, 1); -} - -static int qsc6085_on(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->iod); - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - mif_err("+++\n"); - - if (!mc->gpio_cp_on || !mc->gpio_cp_reset) { - mif_err("no gpio_cp_on or no gpio_cp_reset\n"); - return -ENXIO; - } - - set_idpram_boot_magic(dpld); - - mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); - - gpio_set_value(mc->gpio_cp_reset, 1); - gpio_set_value(mc->gpio_cp_on, 0); - - msleep(100); - - gpio_set_value(mc->gpio_cp_on, 1); - - msleep(400); - msleep(400); - msleep(200); - - gpio_set_value(mc->gpio_cp_on, 0); - - mif_err("---\n"); - return 0; -} - -static int qsc6085_off(struct modem_ctl *mc) -{ - int phone_wait_cnt = 0; - - pr_info("MIF: <%s+>\n", __func__); - - if (!mc->gpio_cp_on || !mc->gpio_cp_reset || - !mc->gpio_phone_active) { - pr_err("MIF: <%s> no gpio data\n", __func__); - return -ENXIO; - } - - gpio_set_value(mc->gpio_cp_on, 0); - - /* confirm phone off */ - while (1) { - if (gpio_get_value(mc->gpio_phone_active)) { - pr_err("MIF: <%s> Try to Turn Phone Off by CP_RST\n", - __func__); - gpio_set_value(mc->gpio_cp_reset, 0); - if (phone_wait_cnt > 10) { - pr_emerg("MIF: <%s> OFF Failed\n", __func__); - break; - } - phone_wait_cnt++; - mdelay(100); - } else { - pr_emerg("MIF: <%s> OFF Success\n", __func__); - break; - } - } - - mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); - - pr_info("MIF: <%s->\n", __func__); - - return 0; -} - -static int qsc6085_reset(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->iod); - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - mif_err("+++\n"); - - set_idpram_boot_magic(dpld); - - gpio_set_value(mc->gpio_cp_reset, 0); - msleep(100); - gpio_set_value(mc->gpio_cp_reset, 1); - - mif_err("---\n"); - return 0; -} - -static int qsc6085_modem_dump_reset(struct modem_ctl *mc) -{ - pr_info("MIF: <%s>\n", __func__); - panic("CP Crashed"); -} - -static void qsc6085_get_ops(struct modem_ctl *mc) -{ - mc->ops.modem_on = qsc6085_on; - mc->ops.modem_off = qsc6085_off; - mc->ops.modem_reset = qsc6085_reset; - mc->ops.modem_dump_reset = qsc6085_modem_dump_reset; -} - -int qsc6085_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) -{ - int ret = 0; - unsigned long flag = 0; - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_reset = pdata->gpio_cp_reset; - mc->gpio_pda_active = pdata->gpio_pda_active; - mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; - - if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { - mif_err("no GPIO data\n"); - return -ENXIO; - } - - mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); - pr_err("MIF: <%s> PHONE_ACTIVE IRQ# = %d\n", - __func__, mc->irq_phone_active); - - qsc6085_get_ops(mc); - - /*register phone_active_handler*/ - flag = IRQF_TRIGGER_HIGH; - - ret = request_irq(mc->irq_phone_active, - phone_active_irq_handler, - flag, "phone_active", mc); - if (ret) { - pr_err("MIF: failed to irq_phone_active request_irq: %d\n" - , ret); - return ret; - } - - ret = enable_irq_wake(mc->irq_phone_active); - if (ret) { - pr_err("MIF: <%s> failed to enable_irq_wake:%d\n", - __func__, ret); - free_irq(mc->irq_phone_active, mc); - return ret; - } - return ret; -} diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c index 9f97dfd..cfa2896 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c +++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c @@ -26,9 +26,6 @@ #include "modem_prj.h" #include -spinlock_t irq_lock; -int irq_lock_flag; - int sprd_boot_done; extern int spi_thread_restart(void); @@ -48,20 +45,10 @@ static int sprd8803_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_ctrl2, 1); #endif msleep(100); - pr_info("[MODEM_IF] %s\n", __func__); +// pr_info("[MODEM_IF] %s\n", __func__); // Kill spam gpio_set_value(mc->gpio_cp_on, 1); gpio_set_value(mc->gpio_pda_active, 1); - spin_lock(&irq_lock); - if (!irq_lock_flag) { - enable_irq(mc->irq_phone_active); - enable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); - enable_irq_wake(mc->irq_phone_active); - enable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); - irq_lock_flag = 1; - } - spin_unlock(&irq_lock); - mc->phone_state = STATE_BOOTING; return 0; @@ -69,7 +56,7 @@ static int sprd8803_on(struct modem_ctl *mc) static int sprd8803_off(struct modem_ctl *mc) { - pr_info("[MODEM_IF] %s\n", __func__); + pr_debug("[MODEM_IF] %s\n", __func__); if (!mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -77,17 +64,6 @@ static int sprd8803_off(struct modem_ctl *mc) } gpio_set_value(mc->gpio_cp_on, 0); - gpio_set_value(mc->gpio_pda_active, 0); - - spin_lock(&irq_lock); - if (irq_lock_flag) { - disable_irq(mc->irq_phone_active); - disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); - disable_irq_wake(mc->irq_phone_active); - disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); - irq_lock_flag = 0; - } - spin_unlock(&irq_lock); mc->phone_state = STATE_OFFLINE; @@ -96,7 +72,7 @@ static int sprd8803_off(struct modem_ctl *mc) static int sprd8803_reset(struct modem_ctl *mc) { - pr_info("[MODEM_IF] %s\n", __func__); + pr_debug("[MODEM_IF] %s\n", __func__); spi_thread_restart(); @@ -105,20 +81,20 @@ static int sprd8803_reset(struct modem_ctl *mc) static int sprd8803_boot_on(struct modem_ctl *mc) { - pr_info("[MODEM_IF] %s %d\n", __func__, mc->phone_state); - return mc->phone_state; + pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done); + return sprd_boot_done; } static int sprd8803_boot_off(struct modem_ctl *mc) { - pr_info("[MODEM_IF] %s\n", __func__); + pr_debug("[MODEM_IF] %s\n", __func__); spi_sema_init(); return 0; } static int sprd8803_dump_reset(struct modem_ctl *mc) { - pr_info("[MODEM_IF] %s\n", __func__); + pr_debug("[MODEM_IF] %s\n", __func__); if (!mc->gpio_ap_cp_int2) return -ENXIO; @@ -139,8 +115,6 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; - disable_irq_nosync(mc->irq_phone_active); - if (!mc->gpio_phone_active || !mc->gpio_cp_dump_int) { pr_err("[MODEM_IF] no gpio data\n"); @@ -161,7 +135,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) else phone_state = STATE_OFFLINE; - if (phone_active_value && cp_dump_value) + if (cp_dump_value) phone_state = STATE_CRASH_EXIT; if (mc->iod && mc->iod->modem_state_changed) @@ -171,8 +145,6 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) mc->bootd->modem_state_changed(mc->bootd, phone_state); exit: - enable_irq(mc->irq_phone_active); - return IRQ_HANDLED; } @@ -245,19 +217,5 @@ int sprd8803_init_modemctl_device(struct modem_ctl *mc, __func__, ret); free_irq(irq_cp_dump_int, mc); } - - irq_lock_flag = 1; - spin_lock_init(&irq_lock); - - spin_lock(&irq_lock); - if (irq_lock_flag) { - disable_irq(mc->irq_phone_active); - disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); - disable_irq_wake(mc->irq_phone_active); - disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); - irq_lock_flag = 0; - } - spin_unlock(&irq_lock); - return ret; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_ss222.c b/drivers/misc/modem_if/modem_modemctl_device_ss222.c deleted file mode 100644 index 1132d7a..0000000 --- a/drivers/misc/modem_if/modem_modemctl_device_ss222.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2010 Samsung Electronics. - * - * 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 "modem_prj.h" -#include "modem_utils.h" - -#define MIF_INIT_TIMEOUT (30 * HZ) - -static void ss222_mc_state_fsm(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->iod); - int cp_on = gpio_get_value(mc->gpio_cp_on); - int cp_reset = gpio_get_value(mc->gpio_cp_reset); - int cp_active = gpio_get_value(mc->gpio_phone_active); - int old_state = mc->phone_state; - int new_state = mc->phone_state; - - mif_err("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", - get_cp_state_str(old_state), cp_on, cp_reset, cp_active); - - if (cp_active) { - if (!cp_on) { - new_state = STATE_OFFLINE; - ld->mode = LINK_MODE_OFFLINE; - } else if (old_state == STATE_ONLINE) { - new_state = STATE_CRASH_EXIT; - ld->mode = LINK_MODE_ULOAD; - } else { - mif_err("don't care!!!\n"); - } - } - - if (old_state != new_state) { - mif_err("new_state = %s\n", get_cp_state_str(new_state)); - mc->bootd->modem_state_changed(mc->bootd, new_state); - mc->iod->modem_state_changed(mc->iod, new_state); - } -} - -static irqreturn_t phone_active_handler(int irq, void *arg) -{ - struct modem_ctl *mc = (struct modem_ctl *)arg; - int cp_reset = gpio_get_value(mc->gpio_cp_reset); - - if (cp_reset) - ss222_mc_state_fsm(mc); - - return IRQ_HANDLED; -} - -static inline void make_gpio_floating(int gpio, bool floating) -{ - if (floating) - gpio_direction_input(gpio); - else - gpio_direction_output(gpio, 0); -} - -static int ss222_on(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->iod); - int cp_on = gpio_get_value(mc->gpio_cp_on); - int cp_off = gpio_get_value(mc->gpio_cp_off); - int cp_reset = gpio_get_value(mc->gpio_cp_reset); - int cp_active = gpio_get_value(mc->gpio_phone_active); - int cp_status = gpio_get_value(mc->gpio_cp_status); - mif_err("+++\n"); - mif_err("cp_on:%d cp_reset:%d ps_hold:%d cp_active:%d cp_status:%d\n", - cp_on, cp_reset, cp_off, cp_active, cp_status); - - gpio_set_value(mc->gpio_pda_active, 1); - - if (!wake_lock_active(&mc->mc_wake_lock)) - wake_lock(&mc->mc_wake_lock); - - mc->phone_state = STATE_OFFLINE; - ld->mode = LINK_MODE_OFFLINE; - - /* Make PS_HOLD floating (Hi-Z) for CP ON */ - make_gpio_floating(mc->gpio_cp_off, true); - - gpio_set_value(mc->gpio_cp_on, 0); - msleep(100); - - gpio_set_value(mc->gpio_cp_reset, 0); - msleep(500); - - gpio_set_value(mc->gpio_cp_on, 1); - msleep(100); - - c2c_reload(); - gpio_set_value(mc->gpio_cp_reset, 1); - msleep(300); - - mif_err("---\n"); - return 0; -} - -static int ss222_off(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->iod); - int cp_on = gpio_get_value(mc->gpio_cp_on); - mif_err("+++\n"); - - if (mc->phone_state == STATE_OFFLINE || cp_on == 0) - return 0; - - mc->phone_state = STATE_OFFLINE; - ld->mode = LINK_MODE_OFFLINE; - - gpio_set_value(mc->gpio_cp_reset, 0); - - /* Make PS_HOLD LOW for CP OFF */ - make_gpio_floating(mc->gpio_cp_off, false); - gpio_set_value(mc->gpio_cp_on, 0); - - mif_err("---\n"); - return 0; -} - -static int ss222_reset(struct modem_ctl *mc) -{ - mif_err("+++\n"); - - if (ss222_off(mc)) - return -EIO; - - msleep(100); - - if (ss222_on(mc)) - return -EIO; - - mif_err("---\n"); - return 0; -} - -static int ss222_force_crash_exit(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - mif_err("+++\n"); - - /* Make DUMP start */ - ld->force_dump(ld, mc->bootd); - - mif_err("---\n"); - return 0; -} - -static int ss222_dump_reset(struct modem_ctl *mc) -{ - unsigned int gpio_cp_reset = mc->gpio_cp_reset; - mif_err("+++\n"); - - if (!wake_lock_active(&mc->mc_wake_lock)) - wake_lock(&mc->mc_wake_lock); - - gpio_set_value(gpio_cp_reset, 0); - udelay(200); - - c2c_reload(); - gpio_set_value(gpio_cp_reset, 1); - msleep(300); - - gpio_set_value(mc->gpio_ap_status, 1); - - mif_err("---\n"); - return 0; -} - -static int ss222_boot_on(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - mif_debug("+++\n"); - - disable_irq_nosync(mc->irq_phone_active); - - gpio_set_value(mc->gpio_ap_status, 1); - - ld->mode = LINK_MODE_BOOT; - - mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); - mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); - - INIT_COMPLETION(ld->init_cmpl); - - mif_debug("---\n"); - return 0; -} - -static int ss222_boot_off(struct modem_ctl *mc) -{ - struct link_device *ld = get_current_link(mc->bootd); - unsigned long remain; - mif_debug("+++\n"); - - ld->mode = LINK_MODE_IPC; - - remain = wait_for_completion_timeout(&ld->init_cmpl, MIF_INIT_TIMEOUT); - if (remain == 0) { - mif_err("T-I-M-E-O-U-T\n"); - mif_err("xxx\n"); - return -EAGAIN; - } - - mif_debug("---\n"); - return 0; -} - -static int ss222_boot_done(struct modem_ctl *mc) -{ - mif_debug("+++\n"); - - if (wake_lock_active(&mc->mc_wake_lock)) - wake_unlock(&mc->mc_wake_lock); - - enable_irq(mc->irq_phone_active); - - mif_debug("---\n"); - return 0; -} - -static void ss222_get_ops(struct modem_ctl *mc) -{ - mc->ops.modem_on = ss222_on; - mc->ops.modem_off = ss222_off; - mc->ops.modem_reset = ss222_reset; - mc->ops.modem_boot_on = ss222_boot_on; - mc->ops.modem_boot_off = ss222_boot_off; - mc->ops.modem_boot_done = ss222_boot_done; - mc->ops.modem_force_crash_exit = ss222_force_crash_exit; - mc->ops.modem_dump_reset = ss222_dump_reset; -} - -int ss222_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) -{ - int ret = 0; - int irq = 0; - unsigned long flag = 0; - mif_debug("+++\n"); - - if (!pdata->gpio_cp_on || !pdata->gpio_cp_off || !pdata->gpio_cp_reset - || !pdata->gpio_pda_active || !pdata->gpio_phone_active - || !pdata->gpio_ap_wakeup || !pdata->gpio_ap_status - || !pdata->gpio_cp_wakeup || !pdata->gpio_cp_status) { - mif_err("ERR! no GPIO data\n"); - mif_err("xxx\n"); - return -ENXIO; - } - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_off = pdata->gpio_cp_off; - mc->gpio_cp_reset = pdata->gpio_cp_reset; - mc->gpio_pda_active = pdata->gpio_pda_active; - mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_ap_wakeup = pdata->gpio_ap_wakeup; - mc->gpio_ap_status = pdata->gpio_ap_status; - mc->gpio_cp_wakeup = pdata->gpio_cp_wakeup; - mc->gpio_cp_status = pdata->gpio_cp_status; - - gpio_set_value(mc->gpio_cp_reset, 0); - - gpio_set_value(mc->gpio_cp_on, 0); - - ss222_get_ops(mc); - dev_set_drvdata(mc->dev, mc); - - wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "umts_wake_lock"); - - mc->irq_phone_active = pdata->irq_phone_active; - if (!mc->irq_phone_active) { - mif_err("ERR! no irq_phone_active\n"); - mif_err("xxx\n"); - return -1; - } - mif_err("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); - - irq = mc->irq_phone_active; - flag = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND; - ret = request_irq(irq, phone_active_handler, flag, "umts_active", mc); - if (ret) { - mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, ret); - mif_err("xxx\n"); - return ret; - } - ret = enable_irq_wake(irq); - if (ret) - mif_err("enable_irq_wake(#%d) fail (err %d)\n", irq, ret); - - mif_debug("---\n"); - return 0; -} - diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c index c2d5067..d2fcf5b 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c @@ -27,7 +27,7 @@ static int xmm6260_on(struct modem_ctl *mc) { - mif_info("xmm6260_on()\n"); + mif_debug("xmm6260_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) { mif_err("no gpio data\n"); @@ -66,7 +66,7 @@ static int xmm6260_on(struct modem_ctl *mc) static int xmm6260_off(struct modem_ctl *mc) { - mif_info("xmm6260_off()\n"); + mif_debug("xmm6260_off()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -85,7 +85,7 @@ static int xmm6260_off(struct modem_ctl *mc) static int xmm6260_reset(struct modem_ctl *mc) { - mif_info("xmm6260_reset()\n"); + mif_debug("xmm6260_reset()\n"); if (!mc->gpio_cp_reset || !mc->gpio_reset_req_n) return -ENXIO; @@ -122,7 +122,7 @@ static int xmm6260_reset(struct modem_ctl *mc) static int xmm6260_boot_on(struct modem_ctl *mc) { - mif_info("xmm6260_boot_on()\n"); + mif_debug("xmm6260_boot_on()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); @@ -136,7 +136,7 @@ static int xmm6260_boot_on(struct modem_ctl *mc) static int xmm6260_boot_off(struct modem_ctl *mc) { - mif_info("xmm6260_boot_off()\n"); + mif_debug("xmm6260_boot_off()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c index fcc4042..4d0b69c 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c @@ -26,9 +26,7 @@ #include #include #include "modem_prj.h" -#ifdef CONFIG_FAST_BOOT -#include -#endif + static int xmm6262_on(struct modem_ctl *mc) { mif_info("\n"); @@ -61,13 +59,11 @@ static int xmm6262_on(struct modem_ctl *mc) udelay(60); gpio_set_value(mc->gpio_cp_on, 0); msleep(20); - - mc->phone_state = STATE_BOOTING; - if (mc->gpio_revers_bias_restore) mc->gpio_revers_bias_restore(); gpio_set_value(mc->gpio_pda_active, 1); + mc->phone_state = STATE_BOOTING; return 0; } @@ -124,20 +120,6 @@ static int xmm6262_reset(struct modem_ctl *mc) return 0; } -static int xmm6262_force_crash_exit(struct modem_ctl *mc) -{ - mif_info("\n"); - - if (!mc->gpio_ap_dump_int) - return -ENXIO; - - gpio_set_value(mc->gpio_ap_dump_int, 1); - mif_info("set ap_dump_int(%d) to high=%d\n", - mc->gpio_ap_dump_int, gpio_get_value(mc->gpio_ap_dump_int)); - return 0; -} - - static irqreturn_t phone_active_irq_handler(int irq, void *_mc) { int phone_reset = 0; @@ -190,72 +172,14 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) return IRQ_HANDLED; } -#ifdef CONFIG_FAST_BOOT -#include - -static void mif_sim_detect_complete(struct modem_ctl *mc) -{ - if (mc->sim_shutdown_req) { - mif_info("fake shutdown sim changed shutdown\n"); - kernel_power_off(); - /*kernel_restart(NULL);*/ - mc->sim_shutdown_req = false; - } -} - -static int mif_init_sim_shutdown(struct modem_ctl *mc) -{ - mc->sim_shutdown_req = false; - mc->modem_complete = mif_sim_detect_complete; - - return 0; -} - -static void mif_check_fake_shutdown(struct modem_ctl *mc, bool online) -{ - if (fake_shut_down && mc->sim_state.online != online) - mc->sim_shutdown_req = true; -} - -#else -static inline int mif_init_sim_shutdown(struct modem_ctl *mc) { return 0; } -#define mif_check_fake_shutdown(a, b) do {} while (0) -#endif - - -#define SIM_DETECT_DEBUG static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) { struct modem_ctl *mc = (struct modem_ctl *)_mc; -#ifdef SIM_DETECT_DEBUG - int val = gpio_get_value(mc->gpio_sim_detect); - static int unchange; - static int prev_val; - - if (mc->phone_state == STATE_BOOTING) { - mif_info("BOOTING, reset unchange\n"); - unchange = 0; - } - if (prev_val == val) { - if (unchange++ > 50) { - mif_err("Abnormal SIM detect GPIO irqs"); - disable_irq_nosync(mc->gpio_sim_detect); - panic("SIM detect IRQ Error"); - } - } else { - unchange = 0; - } - prev_val = val; -#endif - if (mc->iod && mc->iod->sim_state_changed) { - mif_check_fake_shutdown(mc, - gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity - ); + if (mc->iod && mc->iod->sim_state_changed) mc->iod->sim_state_changed(mc->iod, gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity ); - } return IRQ_HANDLED; } @@ -265,7 +189,6 @@ static void xmm6262_get_ops(struct modem_ctl *mc) mc->ops.modem_on = xmm6262_on; mc->ops.modem_off = xmm6262_off; mc->ops.modem_reset = xmm6262_reset; - mc->ops.modem_force_crash_exit = xmm6262_force_crash_exit; } int xmm6262_init_modemctl_device(struct modem_ctl *mc, @@ -295,7 +218,6 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2; #endif - pdev = to_platform_device(mc->dev); mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); @@ -339,12 +261,6 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, /* initialize sim_state => insert: gpio=0, remove: gpio=1 */ mc->sim_state.online = gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity; - - ret = mif_init_sim_shutdown(mc); - if (ret) { - mif_err("failed to sim fake shutdown init: %d\n", ret); - goto err_sim_detect_set_wake_irq; - } } return ret; diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h index 537220a..f85596f 100644 --- a/drivers/misc/modem_if/modem_prj.h +++ b/drivers/misc/modem_if/modem_prj.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -18,18 +19,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include - -#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP -#define DEBUG_MODEM_IF -#endif #define MAX_CPINFO_SIZE 512 @@ -38,10 +33,7 @@ #define MAX_FMT_DEVS 10 #define MAX_RAW_DEVS 32 #define MAX_RFS_DEVS 10 -#define MAX_BOOT_DEVS 10 -#define MAX_DUMP_DEVS 10 - -#define MAX_IOD_RXQ_LEN 2048 +#define MAX_NUM_IO_DEV (MAX_FMT_DEVS + MAX_RAW_DEVS + MAX_RFS_DEVS) #define IOCTL_MODEM_ON _IO('o', 0x19) #define IOCTL_MODEM_OFF _IO('o', 0x20) @@ -70,39 +62,21 @@ #define IOCTL_MODEM_SWITCH_MODEM _IO('o', 0x37) #endif -#define IOCTL_MODEM_RAMDUMP_START _IO('o', 0xCE) -#define IOCTL_MODEM_RAMDUMP_STOP _IO('o', 0xCF) - -#define IOCTL_MODEM_XMIT_BOOT _IO('o', 0x40) +#define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40) #define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) -/* ioctl command for IPC Logger */ -#define IOCTL_MIF_LOG_DUMP _IO('o', 0x51) -#define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52) - /* ioctl command definitions. */ -#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xD0) -#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xD1) -#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xD2) -#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xD3) - -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xDE) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xDF) +#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xd0) +#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xd1) +#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xd2) +#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xd3) -#define CPBOOT_DIR_MASK 0xF000 -#define CPBOOT_STAGE_MASK 0x0F00 -#define CPBOOT_CMD_MASK 0x000F -#define CPBOOT_REQ_RESP_MASK 0x0FFF +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde) +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf) -#define CPBOOT_DIR_AP2CP 0x9000 -#define CPBOOT_DIR_CP2AP 0xA000 - -#define CPBOOT_STAGE_SHIFT 8 - -#define CPBOOT_STAGE_START 0x0000 -#define CPBOOT_CRC_SEND 0x000C -#define CPBOOT_STAGE_DONE 0x000D -#define CPBOOT_STAGE_FAIL 0x000F +/* ioctl command for IPC Logger */ +#define IOCTL_MIF_LOG_DUMP _IO('o', 0x51) +#define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52) /* modem status */ #define MODEM_OFF 0 @@ -119,50 +93,83 @@ #define PSD_DATA_CHID_BEGIN 0x2A #define PSD_DATA_CHID_END 0x38 -#define PS_DATA_CH_0 10 -#define PS_DATA_CH_LAST 24 -#define RMNET0_CH_ID PS_DATA_CH_0 +#define PS_DATA_CH_0 10 +#define PS_DATA_CH_LAST 24 #define IP6VERSION 6 #define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC} -/* IP loopback */ -#define DATA_DRAIN_CHANNEL 30 /* Drain channel to drop RX packets */ +/* loopback: CP -> AP -> CP */ +#define CP2AP_LOOPBACK_CHANNEL 30 + +/* ip loopback */ +#define RMNET0_CH_ID 10 #define DATA_LOOPBACK_CHANNEL 31 /* Debugging features */ -#define MIF_LOG_DIR "/sdcard/log" -#define MIF_MAX_PATH_LEN 256 -#define MIF_MAX_NAME_LEN 64 -#define MIF_MAX_STR_LEN 32 - -#define CP_CRASH_TAG "CP Crash " - -static const char const *dev_format_str[] = { - [IPC_FMT] = "FMT", - [IPC_RAW] = "RAW", - [IPC_RFS] = "RFS", - [IPC_MULTI_RAW] = "MULTI_RAW", - [IPC_CMD] = "CMD", - [IPC_BOOT] = "BOOT", - [IPC_RAMDUMP] = "RAMDUMP", - [IPC_DEBUG] = "DEBUG", +#define MAX_MIF_LOG_PATH_LEN 128 +#define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */ + +#define MAX_MIF_EVT_BUFF_SIZE 256 +#define MAX_MIF_TIME_LEN 32 +#define MAX_MIF_NAME_LEN 16 +#define MAX_MIF_STR_LEN 127 +#define MAX_MIF_LOG_LEN 128 + +enum mif_event_id { + MIF_IRQ_EVT = 0, + MIF_LNK_RX_EVT, + MIF_MUX_RX_EVT, + MIF_IOD_RX_EVT, + MIF_IOD_TX_EVT, + MIF_MUX_TX_EVT, + MIF_LNK_TX_EVT, + MAX_MIF_EVT }; -/** - * get_dev_name - * @dev: IPC device (enum dev_format) - * - * Returns IPC device name as a string. - */ -static const inline char *get_dev_name(unsigned int dev) -{ - if (unlikely(dev >= MAX_DEV_FORMAT)) - return "INVALID"; - else - return dev_format_str[dev]; -} +struct dpram_queue_status { + unsigned in; + unsigned out; +}; + +struct dpram_queue_status_pair { + struct dpram_queue_status txq; + struct dpram_queue_status rxq; +}; + +struct dpram_irq_buff { + unsigned magic; + unsigned access; + struct dpram_queue_status_pair qsp[MAX_IPC_DEV]; + unsigned int2ap; + unsigned int2cp; +}; + +/* Not use */ +struct mif_event_buff { + char time[MAX_MIF_TIME_LEN]; + + struct timeval tv; + enum mif_event_id evt; + + char mc[MAX_MIF_NAME_LEN]; + + char iod[MAX_MIF_NAME_LEN]; + + char ld[MAX_MIF_NAME_LEN]; + enum modem_link link_type; + + unsigned rcvd; + unsigned len; + union { + u8 data[MAX_MIF_LOG_LEN]; + struct dpram_irq_buff dpram_irqb; + }; +}; + +#define MIF_LOG_DIR "/sdcard" +#define MIF_LOG_LV_FILE "/data/.mif_log_level" /* Does modem ctl structure will use state ? or status defined below ?*/ enum modem_state { @@ -180,26 +187,6 @@ enum modem_state { #endif }; -static const char const *cp_state_str[] = { - [STATE_OFFLINE] = "OFFLINE", - [STATE_CRASH_RESET] = "CRASH_RESET", - [STATE_CRASH_EXIT] = "CRASH_EXIT", - [STATE_BOOTING] = "BOOTING", - [STATE_ONLINE] = "ONLINE", - [STATE_NV_REBUILDING] = "NV_REBUILDING", - [STATE_LOADER_DONE] = "LOADER_DONE", - [STATE_SIM_ATTACH] = "SIM_ATTACH", - [STATE_SIM_DETACH] = "SIM_DETACH", -#if defined(CONFIG_SEC_DUAL_MODEM_MODE) - [STATE_MODEM_SWITCH] = "MODEM_SWITCH", -#endif -}; - -static const inline char *get_cp_state_str(int state) -{ - return cp_state_str[state]; -} - enum com_state { COM_NONE, COM_ONLINE, @@ -221,17 +208,6 @@ struct sim_state { bool changed; /* online is changed? */ }; -enum cp_boot_mode { - CP_BOOT_MODE_NORMAL, - CP_BOOT_MODE_DUMP, - MAX_CP_BOOT_MODE -}; - -struct modem_firmware { - char *binary; - u32 size; -}; - #define HDLC_START 0x7F #define HDLC_END 0x7E #define SIZE_OF_HDLC_START 1 @@ -240,8 +216,8 @@ struct modem_firmware { struct header_data { char hdr[HDLC_HEADER_MAX_SIZE]; - u32 len; - u32 frag_len; + unsigned len; + unsigned frag_len; char start; /*hdlc start header 0x7F*/ }; @@ -279,14 +255,10 @@ struct sipc_fmt_hdr { #define SIPC5_EXT_FIELD_EXIST 0b00000010 #define SIPC5_CTL_FIELD_EXIST 0b00000001 -#define SIPC5_EXT_LENGTH_MASK SIPC5_EXT_FIELD_EXIST -#define SIPC5_CTL_FIELD_MASK (SIPC5_EXT_FIELD_EXIST | SIPC5_CTL_FIELD_EXIST) - -#define SIPC5_MIN_HEADER_SIZE 4 -#define SIPC5_HEADER_SIZE_WITH_CTL_FLD 5 +#define SIPC5_MAX_HEADER_SIZE 6 #define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 -#define SIPC5_MAX_HEADER_SIZE SIPC5_HEADER_SIZE_WITH_EXT_LEN - +#define SIPC5_HEADER_SIZE_WITH_CTL_FLD 5 +#define SIPC5_MIN_HEADER_SIZE 4 #define SIPC5_CONFIG_SIZE 1 #define SIPC5_CH_ID_SIZE 1 @@ -295,18 +267,13 @@ struct sipc_fmt_hdr { #define SIPC5_LEN_OFFSET 2 #define SIPC5_CTL_OFFSET 4 +#define SIPC5_CH_ID_RAW_0 0 #define SIPC5_CH_ID_PDP_0 10 #define SIPC5_CH_ID_PDP_LAST 24 -#define SIPC5_CH_ID_BOOT0 215 -#define SIPC5_CH_ID_DUMP0 225 #define SIPC5_CH_ID_FMT_0 235 #define SIPC5_CH_ID_RFS_0 245 #define SIPC5_CH_ID_MAX 255 -#define SIPC5_CH_ID_FLOW_CTRL 255 -#define FLOW_CTRL_SUSPEND ((u8)(0xCA)) -#define FLOW_CTRL_RESUME ((u8)(0xCB)) - /* If iod->id is 0, do not need to store to `iodevs_tree_fmt' in SIPC4 */ #define sipc4_is_not_reserved_channel(ch) ((ch) != 0) @@ -328,6 +295,20 @@ struct sipc5_link_hdr { } __packed; struct sipc5_frame_data { + /* Config octet */ + u8 config; + + /* Channel ID */ + u8 ch_id; + + /* Control for multiple FMT frame */ + u8 control; + + /* Frame configuration set by header analysis */ + bool padding; + bool ctl_fld; + bool ext_len; + /* Frame length calculated from the length fields */ unsigned len; @@ -337,17 +318,11 @@ struct sipc5_frame_data { /* The length of received header */ unsigned hdr_rcvd; - /* The length of link layer payload */ - unsigned pay_len; + /* The length of data payload */ + unsigned data_len; /* The length of received data */ - unsigned pay_rcvd; - - /* The length of link layer padding */ - unsigned pad_len; - - /* The length of received padding */ - unsigned pad_rcvd; + unsigned data_rcvd; /* Header buffer */ u8 hdr[SIPC5_MAX_HEADER_SIZE]; @@ -374,9 +349,8 @@ struct skbuff_private { struct io_device *iod; struct link_device *ld; struct io_device *real_iod; /* for rx multipdp */ - - /* for indicating that thers is only one IPC frame in an skb */ - bool single_frame; + u8 ch_id; + u8 control; } __packed; static inline struct skbuff_private *skbpriv(struct sk_buff *skb) @@ -385,35 +359,6 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb) return (struct skbuff_private *)&skb->cb; } -enum iod_rx_state { - IOD_RX_ON_STANDBY = 0, - IOD_RX_HEADER, - IOD_RX_PAYLOAD, - IOD_RX_PADDING, - MAX_IOD_RX_STATE -}; - -static const char const *rx_state_str[] = { - [IOD_RX_ON_STANDBY] = "RX_ON_STANDBY", - [IOD_RX_HEADER] = "RX_HEADER", - [IOD_RX_PAYLOAD] = "RX_PAYLOAD", - [IOD_RX_PADDING] = "RX_PADDING", -}; - -/** - * get_dev_name - * @dev: IPC device (enum dev_format) - * - * Returns IPC device name as a string. - */ -static const inline char *get_rx_state_str(unsigned int state) -{ - if (unlikely(state >= MAX_IOD_RX_STATE)) - return "INVALID_STATE"; - else - return rx_state_str[state]; -} - struct io_device { /* rb_tree node for an io device */ struct rb_node node_chan; @@ -422,7 +367,6 @@ struct io_device { /* Name of the IO device */ char *name; - /* Reference count */ atomic_t opened; /* Wait queue for the IO device */ @@ -439,11 +383,7 @@ struct io_device { enum modem_io io_typ; enum modem_network net_typ; - /* The name of the application that will use this IO device */ - char *app; - - /* Whether or not handover among 2+ link devices */ - bool use_handover; + bool use_handover; /* handover 2+ link devices */ /* SIPC version */ enum sipc_ver ipc_version; @@ -451,10 +391,6 @@ struct io_device { /* Rx queue of sk_buff */ struct sk_buff_head sk_rx_q; - /* RX state used in RX FSM */ - enum iod_rx_state curr_rx_state; - enum iod_rx_state next_rx_state; - /* ** work for each io device, when delayed work needed ** use this for private io device rx action @@ -511,9 +447,6 @@ struct link_device { /* SIPC version */ enum sipc_ver ipc_version; - /* Maximum IPC device = the last IPC device (e.g. IPC_RFS) + 1 */ - int max_ipc_dev; - /* Modem data */ struct modem_data *mdm_data; @@ -526,12 +459,6 @@ struct link_device { /* Operation mode of the link device */ enum link_mode mode; - /* completion for waiting for link initialization */ - struct completion init_cmpl; - - /* completion for waiting for PIF initialization in a CP */ - struct completion pif_cmpl; - struct io_device *fmt_iods[4]; /* TX queue of socket buffers */ @@ -541,30 +468,13 @@ struct link_device { struct sk_buff_head *skb_txq[MAX_IPC_DEV]; - /* RX queue of socket buffers */ - struct sk_buff_head sk_fmt_rx_q; - struct sk_buff_head sk_raw_rx_q; - struct sk_buff_head sk_rfs_rx_q; - - struct sk_buff_head *skb_rxq[MAX_IPC_DEV]; - bool raw_tx_suspended; /* for misc dev */ struct completion raw_tx_resumed_by_cp; - /** - * This flag is for TX flow control on network interface. - * This must be set and clear only by a flow control command from CP. - */ - bool suspend_netif_tx; - struct workqueue_struct *tx_wq; struct work_struct tx_work; struct delayed_work tx_delayed_work; - - struct delayed_work *tx_dwork[MAX_IPC_DEV]; - struct delayed_work fmt_tx_dwork; - struct delayed_work raw_tx_dwork; - struct delayed_work rfs_tx_dwork; + struct delayed_work tx_dwork; struct workqueue_struct *rx_wq; struct work_struct rx_work; @@ -585,26 +495,20 @@ struct link_device { int (*send)(struct link_device *ld, struct io_device *iod, struct sk_buff *skb); - /* method for CP booting */ - int (*xmit_boot)(struct link_device *ld, struct io_device *iod, - unsigned long arg); + int (*udl_start)(struct link_device *ld, struct io_device *iod); - /* methods for CP firmware upgrade */ - int (*dload_start)(struct link_device *ld, struct io_device *iod); - int (*firm_update)(struct link_device *ld, struct io_device *iod, - unsigned long arg); - - /* methods for CP crash dump */ int (*force_dump)(struct link_device *ld, struct io_device *iod); + int (*dump_start)(struct link_device *ld, struct io_device *iod); - int (*dump_update)(struct link_device *ld, struct io_device *iod, + + int (*modem_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); - int (*dump_finish)(struct link_device *ld, struct io_device *iod, + + int (*dump_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); - /* IOCTL extension */ int (*ioctl)(struct link_device *ld, struct io_device *iod, - unsigned cmd, unsigned long arg); + unsigned cmd, unsigned long _arg); }; /** rx_alloc_skb - allocate an skbuff and set skb's iod, ld @@ -663,9 +567,6 @@ struct modem_shared { struct mif_storage storage; spinlock_t lock; - /* CP crash information */ - char cp_crash_info[530]; - /* loopbacked IP address * default is 0.0.0.0 (disabled) * after you setted this, you can use IP packet loopback using this IP. @@ -685,52 +586,35 @@ struct modem_ctl { struct sim_state sim_state; unsigned gpio_cp_on; - unsigned gpio_cp_off; unsigned gpio_reset_req_n; unsigned gpio_cp_reset; - - /* for broadcasting AP's PM state (active or sleep) */ unsigned gpio_pda_active; - - /* for checking aliveness of CP */ unsigned gpio_phone_active; - int irq_phone_active; - - /* for AP-CP power management (PM) handshaking */ - unsigned gpio_ap_wakeup; - int irq_ap_wakeup; - unsigned gpio_ap_status; - unsigned gpio_cp_wakeup; - unsigned gpio_cp_status; - int irq_cp_status; - - /* for USB/HSIC PM */ - unsigned gpio_host_wakeup; - int irq_host_wakeup; - unsigned gpio_host_active; - unsigned gpio_slave_wakeup; - -#ifdef CONFIG_EXYNOS4_CPUFREQ - /* cpu/bus frequency lock */ - unsigned gpio_cpufreq_lock; - struct delayed_work work_cpu_lock; - struct delayed_work work_cpu_unlock; -#endif - unsigned gpio_cp_dump_int; unsigned gpio_ap_dump_int; unsigned gpio_flm_uart_sel; - unsigned gpio_cp_warm_reset; #if defined(CONFIG_MACH_M0_CTC) unsigned gpio_flm_uart_sel_rev06; #endif - + unsigned gpio_cp_warm_reset; + unsigned gpio_cp_off; unsigned gpio_sim_detect; + unsigned gpio_dynamic_switching; + + int irq_phone_active; int irq_sim_detect; -#ifdef CONFIG_LINK_DEVICE_PLD - unsigned gpio_fpga_cs_n; -#endif +#ifdef CONFIG_LTE_MODEM_CMC221 + const struct attribute_group *group; + unsigned gpio_slave_wakeup; + unsigned gpio_host_wakeup; + unsigned gpio_host_active; + int irq_host_wakeup; + + struct delayed_work dwork; +#endif /*CONFIG_LTE_MODEM_CMC221*/ + + struct work_struct work; #if defined(CONFIG_MACH_U1_KOR_LGT) unsigned gpio_cp_reset_msm; @@ -751,13 +635,9 @@ struct modem_ctl { unsigned gpio_cp_ctrl2; #endif - /* Switch with 2 links in a modem */ - unsigned gpio_link_switch; - - const struct attribute_group *group; - - struct delayed_work dwork; - struct work_struct work; +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga_cs_n; +#endif struct modemctl_ops ops; struct io_device *iod; @@ -771,45 +651,119 @@ struct modem_ctl { bool need_switch_to_usb; bool sim_polarity; - - bool sim_shutdown_req; - void (*modem_complete)(struct modem_ctl *mc); }; int sipc4_init_io_device(struct io_device *iod); int sipc5_init_io_device(struct io_device *iod); -bool sipc5_start_valid(u8 *frm); -bool sipc5_padding_exist(u8 *frm); -bool sipc5_multi_frame(u8 *frm); -bool sipc5_ext_len(u8 *frm); -int sipc5_get_hdr_len(u8 *frm); -u8 sipc5_get_ch_id(u8 *frm); -u8 sipc5_get_ctrl_field(u8 *frm); -int sipc5_get_frame_len(u8 *frm); -int sipc5_calc_padding_size(int len); -int sipc5_get_total_len(u8 *frm); - -u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count); -void sipc5_build_header(struct io_device *iod, struct link_device *ld, - u8 *buff, u8 cfg, u8 ctrl, u32 count); +/** + * sipc5_start_valid + * @cfg: configuration field of an SIPC5 link frame + * + * Returns TRUE if the start (configuration field) of an SIPC5 link frame + * is valid or returns FALSE if it is not valid. + * + */ +static inline int sipc5_start_valid(u8 cfg) +{ + return (cfg & SIPC5_START_MASK) == SIPC5_START_MASK; +} + +/** + * sipc5_get_hdr_len + * @cfg: configuration field of an SIPC5 link frame + * + * Returns the length of SIPC5 link layer header in an SIPC5 link frame + * + */ +static inline unsigned sipc5_get_hdr_len(u8 cfg) +{ + if (cfg & SIPC5_EXT_FIELD_EXIST) { + if (cfg & SIPC5_CTL_FIELD_EXIST) + return SIPC5_HEADER_SIZE_WITH_CTL_FLD; + else + return SIPC5_HEADER_SIZE_WITH_EXT_LEN; + } else { + return SIPC5_MIN_HEADER_SIZE; + } +} + +/** + * sipc5_get_ch_id + * @frm: pointer to an SIPC5 frame + * + * Returns the channel ID in an SIPC5 link frame + * + */ +static inline u8 sipc5_get_ch_id(u8 *frm) +{ + return *(frm + SIPC5_CH_ID_OFFSET); +} + +/** + * sipc5_get_frame_sz16 + * @frm: pointer to an SIPC5 link frame + * + * Returns the length of an SIPC5 link frame without the extended length field + * + */ +static inline unsigned sipc5_get_frame_sz16(u8 *frm) +{ + return *((u16 *)(frm + SIPC5_LEN_OFFSET)); +} + +/** + * sipc5_get_frame_sz32 + * @frm: pointer to an SIPC5 frame + * + * Returns the length of an SIPC5 link frame with the extended length field + * + */ +static inline unsigned sipc5_get_frame_sz32(u8 *frm) +{ + return *((u32 *)(frm + SIPC5_LEN_OFFSET)); +} + +/** + * sipc5_calc_padding_size + * @len: length of an SIPC5 link frame + * + * Returns the padding size for an SIPC5 link frame + * + */ +static inline unsigned sipc5_calc_padding_size(unsigned len) +{ + unsigned residue = len & 0x3; + return residue ? (4 - residue) : 0; +} + +extern void set_sromc_access(bool access); #if defined(CONFIG_TDSCDMA_MODEM_SPRD8803) && defined(CONFIG_LINK_DEVICE_SPI) extern int spi_sema_init(void); extern int sprd_boot_done; -#endif +struct ipc_spi { + struct class *class; + struct device *dev; + struct cdev cdev; + dev_t devid; -#define STD_UDL_STEP_MASK 0x0000000F -#define STD_UDL_SEND 0x1 -#define STD_UDL_CRC 0xC + wait_queue_head_t waitq; + struct fasync_struct *async_queue; + u32 mailbox; -struct std_dload_info { - u32 size; - u32 mtu; - u32 num_frames; -} __packed; + unsigned long base; + unsigned long size; + void __iomem *mmio; -u32 std_udl_get_cmd(u8 *frm); -bool std_udl_with_payload(u32 cmd); + int irq; + + struct completion comp; + atomic_t ref_sem; + unsigned long flags; + + const struct attribute_group *group; +}; +#endif #endif diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c index 366d0fa..c8e83ed 100644 --- a/drivers/misc/modem_if/modem_sim_slot_switch.c +++ b/drivers/misc/modem_if/modem_sim_slot_switch.c @@ -17,7 +17,7 @@ static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr //return '0' slot path is '||', return '1' slot path is 'X' value = gpio_get_value(GPIO_UIM_SIM_SEL); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev <= 7) + if (system_rev < 7) value = (~value & 0x1); #endif printk("Current Slot is %x\n", value); @@ -34,7 +34,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr switch(value) { case 0: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev <= 7) + if (system_rev < 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif @@ -43,7 +43,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr break; case 1: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev <= 7) + if (system_rev < 7) gpio_set_value(GPIO_UIM_SIM_SEL, 0); else #endif @@ -57,8 +57,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr return size; } -static DEVICE_ATTR(slot_sel, S_IRUGO | S_IWUSR | S_IWGRP, - get_slot_switch, set_slot_switch); +static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch); static int __init slot_switch_manager_init(void) { @@ -76,7 +75,7 @@ static int __init slot_switch_manager_init(void) gpio_direction_output(GPIO_UIM_SIM_SEL, 1); s3c_gpio_setpull(GPIO_UIM_SIM_SEL, S3C_GPIO_PULL_NONE); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev <= 7) + if (system_rev < 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c index 0bdd3f0..d62aaa6 100644 --- a/drivers/misc/modem_if/modem_utils.c +++ b/drivers/misc/modem_if/modem_utils.c @@ -12,35 +12,18 @@ * */ -#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 "modem_prj.h" -#include "modem_variation.h" #include "modem_utils.h" #define CMD_SUSPEND ((unsigned short)(0x00CA)) @@ -52,29 +35,42 @@ "mif: ------------------------------------------------------------" #define LINE_BUFF_SIZE 80 -static const char *hex = "0123456789abcdef"; - -void ts2utc(struct timespec *ts, struct utc_time *utc) +#ifdef CONFIG_LINK_DEVICE_DPRAM +#include "modem_link_device_dpram.h" +int mif_dump_dpram(struct io_device *iod) { - struct tm tm; - - time_to_tm((ts->tv_sec - (sys_tz.tz_minuteswest * 60)), 0, &tm); - utc->year = 1900 + tm.tm_year; - utc->mon = 1 + tm.tm_mon; - utc->day = tm.tm_mday; - utc->hour = tm.tm_hour; - utc->min = tm.tm_min; - utc->sec = tm.tm_sec; - utc->msec = ns2ms(ts->tv_nsec); -} + struct link_device *ld = get_current_link(iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u32 size = dpld->dp_size; + unsigned long read_len = 0; + struct sk_buff *skb; + char *buff; -void get_utc_time(struct utc_time *utc) -{ - struct timespec ts; - getnstimeofday(&ts); - ts2utc(&ts, utc); + buff = kzalloc(size, GFP_ATOMIC); + if (!buff) { + pr_err("[MIF] <%s> alloc dpram buff failed\n", __func__); + return -ENOMEM; + } else { + dpld->dpram_dump(ld, buff); + } + + while (read_len < size) { + skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); + if (!skb) { + pr_err("[MIF] <%s> alloc skb failed\n", __func__); + kfree(buff); + return -ENOMEM; + } + memcpy(skb_put(skb, MAX_IPC_SKB_SIZE), + buff + read_len, MAX_IPC_SKB_SIZE); + skb_queue_tail(&iod->sk_rx_q, skb); + read_len += MAX_IPC_SKB_SIZE; + wake_up(&iod->wq); + } + kfree(buff); + return 0; } -EXPORT_SYMBOL(get_utc_time); +#endif int mif_dump_log(struct modem_shared *msd, struct io_device *iod) { @@ -86,7 +82,7 @@ int mif_dump_log(struct modem_shared *msd, struct io_device *iod) while (read_len < MAX_MIF_BUFF_SIZE) { skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); if (!skb) { - mif_err("ERR! alloc_skb fail\n"); + pr_err("[MIF] <%s> alloc skb failed\n", __func__); spin_unlock_irqrestore(&msd->lock, flags); return -ENOMEM; } @@ -168,6 +164,7 @@ void _mif_com_log(enum mif_log_id id, struct mif_common_block *block; unsigned long int flags; va_list args; + int ret; spin_lock_irqsave(&msd->lock, flags); @@ -182,7 +179,7 @@ void _mif_com_log(enum mif_log_id id, block->time = get_kernel_time(); va_start(args, format); - vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); + ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); va_end(args); } @@ -212,12 +209,13 @@ void _mif_time_log(enum mif_log_id id, struct modem_shared *msd, /* dump2hex * dump data to hex as fast as possible. - * the length of @buff must be greater than "@len * 3" + * the length of @buf must be greater than "@len * 3" * it need 3 bytes per one data byte to print. */ -static inline int dump2hex(char *buff, const char *data, size_t len) +static inline int dump2hex(char *buf, const char *data, size_t len) { - char *dest = buff; + static const char *hex = "0123456789abcdef"; + char *dest = buf; int i; for (i = 0; i < len; i++) { @@ -230,26 +228,24 @@ static inline int dump2hex(char *buff, const char *data, size_t len) *dest = '\0'; - return dest - buff; + return dest - buf; } -void pr_ipc(int level, const char *tag, const char *data, size_t len) +int pr_ipc(const char *str, const char *data, size_t len) { - struct utc_time utc; - unsigned char str[128]; + struct timeval tv; + struct tm date; + unsigned char hexstr[128]; - if (level < 0) - return; + do_gettimeofday(&tv); + time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); - get_utc_time(&utc); - dump2hex(str, data, (len > 32 ? 32 : len)); - if (level > 0) { - pr_err("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, - utc.hour, utc.min, utc.sec, utc.msec, tag, str); - } else { - pr_info("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, - utc.hour, utc.min, utc.sec, utc.msec, tag, str); - } + dump2hex(hexstr, data, (len > 40 ? 40 : len)); + + return pr_info("mif: %s: [%02d-%02d %02d:%02d:%02d.%03ld] %s\n", + str, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec, + (tv.tv_usec > 0 ? tv.tv_usec / 1000 : 0), hexstr); } /* print buffer as hex string */ @@ -257,12 +253,12 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len) { size_t len = min(data_len, max_len); - unsigned char str[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ - dump2hex(str, data, len); + unsigned char hexstr[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ + dump2hex(hexstr, data, len); /* don't change this printk to mif_debug for print this as level7 */ - return printk(KERN_INFO "%s: %s(%u): %s%s\n", MIF_TAG, tag, data_len, - str, (len == data_len) ? "" : " ..."); + return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr, + len == data_len ? "" : " ..."); } /* flow control CM from CP, it use in serial devices */ @@ -289,7 +285,7 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) break; default: - mif_err("flowctl BAD CMD: %04X\n", *cmd); + mif_err("flowctl BACMD: %04X\n", *cmd); break; } } @@ -415,42 +411,6 @@ void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type) iodevs_for_each(msd, iodev_set_tx_link, ld); } -void mif_netif_stop(struct link_device *ld) -{ - struct io_device *iod; - - if (ld->ipc_version < SIPC_VER_50) - iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); - else - iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); - - if (iod) - iodevs_for_each(iod->msd, iodev_netif_stop, 0); -} - -void mif_netif_wake(struct link_device *ld) -{ - struct io_device *iod; - - /** - * If ld->suspend_netif_tx is true, this means that there was a SUSPEND - * flow control command from CP so MIF must wait for a RESUME command - * from CP. - */ - if (ld->suspend_netif_tx) { - mif_info("%s: waiting for FLOW_CTRL_RESUME\n", ld->name); - return; - } - - if (ld->ipc_version < SIPC_VER_50) - iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); - else - iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); - - if (iod) - iodevs_for_each(iod->msd, iodev_netif_wake, 0); -} - /** * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer) * @return: return zero when errors occurred @@ -487,7 +447,7 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, add_timer(timer); } -void mif_print_data(const char *buff, int len) +void mif_print_data(char *buf, int len) { int words = len >> 4; int residue = len - (words << 4); @@ -498,8 +458,11 @@ void mif_print_data(const char *buff, int len) /* Make the last line, if ((len % 16) > 0) */ if (residue > 0) { + memset(last, 0, sizeof(last)); + memset(tb, 0, sizeof(tb)); + b = buf + (words << 4); + sprintf(last, "%04X: ", (words << 4)); - b = (char *)buff + (words << 4); for (i = 0; i < residue; i++) { sprintf(tb, "%02x ", b[i]); strcat(last, tb); @@ -511,7 +474,7 @@ void mif_print_data(const char *buff, int len) } for (i = 0; i < words; i++) { - b = (char *)buff + (i << 4); + b = buf + (i << 4); mif_err("%04X: " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -525,141 +488,13 @@ void mif_print_data(const char *buff, int len) mif_err("%s\n", last); } -void mif_dump2format16(const char *data, int len, char *buff, char *tag) -{ - char *d; - int i; - int words = len >> 4; - int residue = len - (words << 4); - char line[LINE_BUFF_SIZE]; - char tb[8]; - - for (i = 0; i < words; i++) { - memset(line, 0, LINE_BUFF_SIZE); - d = (char *)data + (i << 4); - - if (tag) - sprintf(line, "%s%04X| " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x\n", - tag, (i << 4), - d[0], d[1], d[2], d[3], - d[4], d[5], d[6], d[7], - d[8], d[9], d[10], d[11], - d[12], d[13], d[14], d[15]); - else - sprintf(line, "%04X| " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x\n", - (i << 4), - d[0], d[1], d[2], d[3], - d[4], d[5], d[6], d[7], - d[8], d[9], d[10], d[11], - d[12], d[13], d[14], d[15]); - - strcat(buff, line); - } - - /* Make the last line, if (len % 16) > 0 */ - if (residue > 0) { - memset(line, 0, LINE_BUFF_SIZE); - memset(tb, 0, sizeof(tb)); - d = (char *)data + (words << 4); - - if (tag) - sprintf(line, "%s%04X|", tag, (words << 4)); - else - sprintf(line, "%04X|", (words << 4)); - - for (i = 0; i < residue; i++) { - sprintf(tb, " %02x", d[i]); - strcat(line, tb); - if ((i & 0x3) == 0x3) { - sprintf(tb, " "); - strcat(line, tb); - } - } - strcat(line, "\n"); - - strcat(buff, line); - } -} - -void mif_dump2format4(const char *data, int len, char *buff, char *tag) -{ - char *d; - int i; - int words = len >> 2; - int residue = len - (words << 2); - char line[LINE_BUFF_SIZE]; - char tb[8]; - - for (i = 0; i < words; i++) { - memset(line, 0, LINE_BUFF_SIZE); - d = (char *)data + (i << 2); - - if (tag) - sprintf(line, "%s%04X| %02x %02x %02x %02x\n", - tag, (i << 2), d[0], d[1], d[2], d[3]); - else - sprintf(line, "%04X| %02x %02x %02x %02x\n", - (i << 2), d[0], d[1], d[2], d[3]); - - strcat(buff, line); - } - - /* Make the last line, if (len % 4) > 0 */ - if (residue > 0) { - memset(line, 0, LINE_BUFF_SIZE); - memset(tb, 0, sizeof(tb)); - d = (char *)data + (words << 2); - - if (tag) - sprintf(line, "%s%04X|", tag, (words << 2)); - else - sprintf(line, "%04X|", (words << 2)); - - for (i = 0; i < residue; i++) { - sprintf(tb, " %02x", d[i]); - strcat(line, tb); - } - strcat(line, "\n"); - - strcat(buff, line); - } -} - -void mif_print_dump(const char *data, int len, int width) -{ - char *buff; - - buff = kzalloc(len << 3, GFP_ATOMIC); - if (!buff) { - mif_err("ERR! kzalloc fail\n"); - return; - } - - if (width == 16) - mif_dump2format16(data, len, buff, LOG_TAG); - else - mif_dump2format4(data, len, buff, LOG_TAG); - - pr_info("%s", buff); - - kfree(buff); -} - void print_sipc4_hdlc_fmt_frame(const u8 *psrc) { u8 *frm; /* HDLC Frame */ struct fmt_hdr *hh; /* HDLC Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - int hh_len = sizeof(struct fmt_hdr); - int fh_len = sizeof(struct sipc_fmt_hdr); + u16 hh_len = sizeof(struct fmt_hdr); + u16 fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -696,7 +531,7 @@ void print_sipc4_hdlc_fmt_frame(const u8 *psrc) void print_sipc4_fmt_frame(const u8 *psrc) { struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)psrc; - int fh_len = sizeof(struct sipc_fmt_hdr); + u16 fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -723,8 +558,8 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) u8 *lf; /* Link Frame */ struct sipc5_link_hdr *lh; /* Link Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - int lh_len; - int fh_len; + u16 lh_len; + u16 fh_len; u8 *data; int dlen; @@ -732,7 +567,10 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) /* Point HDLC header and IPC header */ lh = (struct sipc5_link_hdr *)lf; - lh_len = (u16)sipc5_get_hdr_len((u8 *)lh); + if (lh->cfg & SIPC5_CTL_FIELD_EXIST) + lh_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; + else + lh_len = SIPC5_MIN_HEADER_SIZE; fh = (struct sipc_fmt_hdr *)(lf + lh_len); fh_len = sizeof(struct sipc_fmt_hdr); @@ -763,8 +601,8 @@ static void strcat_tcp_header(char *buff, u8 *pkt) { struct tcphdr *tcph = (struct tcphdr *)pkt; int eol; - char line[LINE_BUFF_SIZE] = {0, }; - char flag_str[32] = {0, }; + char line[LINE_BUFF_SIZE]; + char flag_str[32]; /*------------------------------------------------------------------------- @@ -792,17 +630,21 @@ static void strcat_tcp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: TCP:: Src.Port %u, Dst.Port %u\n", - MIF_TAG, ntohs(tcph->source), ntohs(tcph->dest)); + "mif: TCP:: Src.Port %u, Dst.Port %u\n", + ntohs(tcph->source), ntohs(tcph->dest)); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", - MIF_TAG, ntohs(tcph->seq), ntohs(tcph->seq), + "mif: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", + ntohs(tcph->seq), ntohs(tcph->seq), ntohs(tcph->ack_seq), ntohs(tcph->ack_seq)); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); + memset(flag_str, 0, sizeof(flag_str)); if (tcph->cwr) strcat(flag_str, "CWR "); if (tcph->ece) @@ -822,12 +664,12 @@ static void strcat_tcp_header(char *buff, u8 *pkt) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "%s: TCP:: Flags {%s}\n", - MIF_TAG, flag_str); + snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: TCP:: Window %u, Checksum 0x%04X, Urgent %u\n", MIF_TAG, + "mif: TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n", ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr)); strcat(buff, line); } @@ -835,7 +677,7 @@ static void strcat_tcp_header(char *buff, u8 *pkt) static void strcat_udp_header(char *buff, u8 *pkt) { struct udphdr *udph = (struct udphdr *)pkt; - char line[LINE_BUFF_SIZE] = {0, }; + char line[LINE_BUFF_SIZE]; /*------------------------------------------------------------------------- @@ -851,39 +693,41 @@ static void strcat_udp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: UDP:: Src.Port %u, Dst.Port %u\n", - MIF_TAG, ntohs(udph->source), ntohs(udph->dest)); + "mif: UDP:: Src.Port %u, Dst.Port %u\n", + ntohs(udph->source), ntohs(udph->dest)); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: UDP:: Length %u, Checksum 0x%04X\n", - MIF_TAG, ntohs(udph->len), ntohs(udph->check)); + "mif: UDP:: Length %u, Checksum 0x%04X\n", + ntohs(udph->len), ntohs(udph->check)); strcat(buff, line); if (ntohs(udph->dest) == 53) { - snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS query!!!\n", - MIF_TAG); + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n"); strcat(buff, line); } if (ntohs(udph->source) == 53) { - snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS response!!!\n", - MIF_TAG); + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n"); strcat(buff, line); } } -void print_ip4_packet(const u8 *ip_pkt, bool tx) +void print_ip4_packet(u8 *ip_pkt, bool tx) { char *buff; struct iphdr *iph = (struct iphdr *)ip_pkt; - u8 *pkt = (u8 *)ip_pkt + (iph->ihl << 2); + u8 *pkt = ip_pkt + (iph->ihl << 2); u16 flags = (ntohs(iph->frag_off) & 0xE000); u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF); int eol; - char line[LINE_BUFF_SIZE] = {0, }; - char flag_str[16] = {0, }; + char line[LINE_BUFF_SIZE]; + char flag_str[16]; /*--------------------------------------------------------------------------- IPv4 Header Format @@ -920,25 +764,32 @@ void print_ip4_packet(const u8 *ip_pkt, bool tx) if (!buff) return; + + memset(line, 0, LINE_BUFF_SIZE); if (tx) snprintf(line, LINE_BUFF_SIZE, "\n%s\n", TX_SEPARATOR); else snprintf(line, LINE_BUFF_SIZE, "\n%s\n", RX_SEPARATOR); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", - MIF_TAG, iph->version, (iph->ihl << 2), iph->tos, - ntohs(iph->tot_len)); + "mif: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", + iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len)); strcat(buff, line); - snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: ID %u, Fragment Offset %u\n", - MIF_TAG, ntohs(iph->id), frag_off); + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: IP4:: ID %u, Fragment Offset %u\n", + ntohs(iph->id), frag_off); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); + memset(flag_str, 0, sizeof(flag_str)); if (flags & IP_CE) strcat(flag_str, "CE "); if (flags & IP_DF) @@ -948,18 +799,19 @@ void print_ip4_packet(const u8 *ip_pkt, bool tx) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: Flags {%s}\n", - MIF_TAG, flag_str); + snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", - MIF_TAG, iph->ttl, iph->protocol, ntohs(iph->check)); + "mif: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", + iph->ttl, iph->protocol, ntohs(iph->check)); strcat(buff, line); + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "%s: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", - MIF_TAG, ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], + "mif: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", + ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]); strcat(buff, line); @@ -976,6 +828,7 @@ void print_ip4_packet(const u8 *ip_pkt, bool tx) break; } + memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); @@ -984,7 +837,7 @@ void print_ip4_packet(const u8 *ip_pkt, bool tx) kfree(buff); } -bool is_dns_packet(const u8 *ip_pkt) +bool is_dns_packet(u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2)); @@ -999,7 +852,7 @@ bool is_dns_packet(const u8 *ip_pkt) return false; } -bool is_syn_packet(const u8 *ip_pkt) +bool is_syn_packet(u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2)); @@ -1014,248 +867,124 @@ bool is_syn_packet(const u8 *ip_pkt) return false; } -/** - * mif_register_isr - * @irq: IRQ number for a DPRAM interrupt - * @isr: function pointer to an interrupt service routine - * @flags: set of interrupt flags - * @name: name of the interrupt - * @data: pointer to a data for @isr - * - * Registers the ISR for the IRQ number. - */ -int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, - const char *name, void *data) +int memcmp16_to_io(const void __iomem *to, void *from, int size) { - int ret; - - ret = request_irq(irq, isr, flags, name, data); - if (ret) { - mif_err("%s: ERR! request_irq fail (err %d)\n", name, ret); - return ret; + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + int count = size >> 1; + int diff = 0; + int i; + u16 d1; + u16 s1; + + for (i = 0; i < count; i++) { + d1 = ioread16(d); + s1 = *s; + if (d1 != s1) { + diff++; + mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); + } + d++; + s++; } - ret = enable_irq_wake(irq); - if (ret) - mif_err("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); - - mif_err("%s (#%d) handler registered\n", name, irq); - - return 0; + return diff; } -int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes) +int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size) { - u16 i; - u16 words = bytes >> 1; - u16 __iomem *dst = (u16 __iomem *)start; + u8 __iomem *dst; + int i; u16 val; - int err_cnt = 0; - - mif_err("%s: start 0x%p, bytes %d\n", dp_name, start, bytes); - - mif_err("%s: 0/6 stage ...\n", dp_name); - for (i = 1; i <= 100; i++) { - iowrite16(0x1234, dst); - val = ioread16(dst); - if (val != 0x1234) { - mif_err("%s: [0x0000] read 0x%04X != written 0x1234 " - "(try# %d)\n", dp_name, val, i); - err_cnt++; - } - } - if (err_cnt > 0) { - mif_err("%s: FAIL!!!\n", dp_name); - return -EINVAL; - } - - mif_err("%s: 1/6 stage ...\n", dp_name); - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { - iowrite16(0, dst); - dst++; - } - - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { - val = ioread16(dst); - if (val != 0x0000) { - mif_err("%s: ERR! [0x%04X] read 0x%04X != written " - "0x0000\n", dp_name, i, val); - err_cnt++; - } - dst++; - } + mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size); - if (err_cnt > 0) { - mif_err("%s: FAIL!!!\n", dp_name); - return -EINVAL; - } - - mif_err("%s: 2/6 stage ...\n", dp_name); - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { - iowrite16(0xFFFF, dst); - dst++; + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16((i & 0xFFFF), dst); + dst += 2; } - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { val = ioread16(dst); - if (val != 0xFFFF) { - mif_err("%s: ERR! [0x%04X] read 0x%04X != written " - "0xFFFF\n", dp_name, i, val); - err_cnt++; + if (val != (i & 0xFFFF)) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n", + dp_name, i, val, (i & 0xFFFF)); + return -EINVAL; } - dst++; + dst += 2; } - if (err_cnt > 0) { - mif_err("%s: FAIL!!!\n", dp_name); - return -EINVAL; - } - - mif_err("%s: 3/6 stage ...\n", dp_name); - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { iowrite16(0x00FF, dst); - dst++; + dst += 2; } - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { val = ioread16(dst); if (val != 0x00FF) { - mif_err("%s: ERR! [0x%04X] read 0x%04X != written " - "0x00FF\n", dp_name, i, val); - err_cnt++; + mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n", + dp_name, i, val); + return -EINVAL; } - dst++; + dst += 2; } - if (err_cnt > 0) { - mif_err("%s: FAIL!!!\n", dp_name); - return -EINVAL; - } - - mif_err("%s: 4/6 stage ...\n", dp_name); - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { iowrite16(0x0FF0, dst); - dst++; + dst += 2; } - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { val = ioread16(dst); if (val != 0x0FF0) { - mif_err("%s: ERR! [0x%04X] read 0x%04X != written " - "0x0FF0\n", dp_name, i, val); - err_cnt++; + mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n", + dp_name, i, val); + return -EINVAL; } - dst++; - } - - if (err_cnt > 0) { - mif_err("%s: FAIL!!!\n", dp_name); - return -EINVAL; + dst += 2; } - mif_err("%s: 5/6 stage ...\n", dp_name); - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { iowrite16(0xFF00, dst); - dst++; + dst += 2; } - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { val = ioread16(dst); if (val != 0xFF00) { - mif_err("%s: ERR! [0x%04X] read 0x%04X != written " - "0xFF00\n", dp_name, i, val); - err_cnt++; + mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n", + dp_name, i, val); + return -EINVAL; } - dst++; + dst += 2; } - mif_err("%s: 6/6 stage ...\n", dp_name); - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { - iowrite16((i & 0xFFFF), dst); - dst++; + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16(0, dst); + dst += 2; } - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { + dst = start; + for (i = 0; i < (size >> 1); i++) { val = ioread16(dst); - if (val != (i & 0xFFFF)) { - mif_err("%s: ERR! [0x%04X] read 0x%04X != written " - "0x%04X\n", dp_name, i, val, (i & 0xFFFF)); - err_cnt++; + if (val != 0) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0\n", + dp_name, i, val); + return -EINVAL; } - dst++; - } - - if (err_cnt > 0) { - mif_err("%s: FAIL!!!\n", dp_name); - return -EINVAL; - } - - mif_err("%s: PASS!!!\n", dp_name); - - dst = (u16 __iomem *)start; - for (i = 0; i < words; i++) { - iowrite16(0, dst); - dst++; + dst += 2; } + mif_info("%s: PASS!!!\n", dp_name); return 0; } -struct file *mif_open_file(const char *path) -{ - struct file *fp; - mm_segment_t old_fs; - - old_fs = get_fs(); - set_fs(get_ds()); - - fp = filp_open(path, O_RDWR|O_CREAT|O_APPEND, 0666); - - set_fs(old_fs); - - if (IS_ERR(fp)) - return NULL; - - return fp; -} - -void mif_save_file(struct file *fp, const char *buff, size_t size) -{ - int ret; - mm_segment_t old_fs; - - old_fs = get_fs(); - set_fs(get_ds()); - - ret = fp->f_op->write(fp, buff, size, &fp->f_pos); - if (ret < 0) - mif_err("ERR! write fail\n"); - - set_fs(old_fs); -} - -void mif_close_file(struct file *fp) -{ - mm_segment_t old_fs; - - old_fs = get_fs(); - set_fs(get_ds()); - - filp_close(fp, NULL); - - set_fs(old_fs); -} - diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h index 15edb43..7225ec2 100644 --- a/drivers/misc/modem_if/modem_utils.h +++ b/drivers/misc/modem_if/modem_utils.h @@ -16,7 +16,6 @@ #define __MODEM_UTILS_H__ #include -#include "modem_prj.h" #define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type)) @@ -27,8 +26,6 @@ #define MAX_IPC_SKB_SIZE 4096 #define MAX_LOG_SIZE 64 -#define MIF_TAG "mif" - #define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE) #define MIF_ID_SIZE sizeof(enum mif_log_id) @@ -99,19 +96,7 @@ struct mif_time_block { char buff[MAX_TIM_LOG_SIZE]; }; -static inline int ns2us(long ns) -{ - return (ns > 0) ? (ns / 1000) : 0; -} - -static inline int ns2ms(long ns) -{ - return (ns > 0) ? (ns / 1000000) : 0; -} - -void ts2utc(struct timespec *ts, struct utc_time *utc); -void get_utc_time(struct utc_time *utc); - +int mif_dump_dpram(struct io_device *); int mif_dump_log(struct modem_shared *, struct io_device *); #define mif_irq_log(msd, map, data, len) \ @@ -156,7 +141,7 @@ static inline unsigned int countbits(unsigned int n) } /* print IPC message as hex string with UTC time */ -void pr_ipc(int level, const char *tag, const char *data, size_t len); +int pr_ipc(const char *str, const char *data, size_t len); /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, @@ -171,13 +156,10 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, pr_buffer(tag, (char *)((urb)->transfer_buffer), \ (size_t)((urb)->actual_length), (size_t)16) -/* Stop/wake all TX queues in network interfaces */ -void mif_netif_stop(struct link_device *ld); -void mif_netif_wake(struct link_device *ld); - /* flow control CMD from CP, it use in serial devices */ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len); + /* get iod from tree functions */ struct io_device *get_iod_with_format(struct modem_shared *msd, @@ -222,14 +204,12 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, void (*function)(unsigned long), unsigned long data); /* debug helper functions for sipc4, sipc5 */ -void mif_print_data(const char *buff, int len); -void mif_dump2format16(const char *data, int len, char *buff, char *tag); -void mif_dump2format4(const char *data, int len, char *buff, char *tag); -void mif_print_dump(const char *data, int len, int width); +void mif_print_data(char *buf, int len); void print_sipc4_hdlc_fmt_frame(const u8 *psrc); void print_sipc4_fmt_frame(const u8 *psrc); void print_sipc5_link_fmt_frame(const u8 *psrc); + /*--------------------------------------------------------------------------- IPv4 Header Format @@ -302,17 +282,23 @@ void print_sipc5_link_fmt_frame(const u8 *psrc); -------------------------------------------------------------------------*/ #define UDP_HDR_SIZE 8 -void print_ip4_packet(const u8 *ip_pkt, bool tx); -bool is_dns_packet(const u8 *ip_pkt); -bool is_syn_packet(const u8 *ip_pkt); +void print_ip4_packet(u8 *ip_pkt, bool tx); +bool is_dns_packet(u8 *ip_pkt); +bool is_syn_packet(u8 *ip_pkt); -int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, - const char *name, void *data); -int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes); +int memcmp16_to_io(const void __iomem *to, void *from, int size); +int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size); -struct file *mif_open_file(const char *path); -void mif_save_file(struct file *fp, const char *buff, size_t size); -void mif_close_file(struct file *fp); +static const inline char *get_dev_name(int dev) +{ + if (dev == IPC_FMT) + return "FMT"; + else if (dev == IPC_RAW) + return "RAW"; + else if (dev == IPC_RFS) + return "RFS"; + else + return "NONE"; +} #endif/*__MODEM_UTILS_H__*/ - diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h index 8799246..b5ec61b 100644 --- a/drivers/misc/modem_if/modem_variation.h +++ b/drivers/misc/modem_if/modem_variation.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -60,30 +61,12 @@ DECLARE_MODEM_INIT(cbp72); DECLARE_MODEM_INIT_DUMMY(cbp72) #endif -#ifdef CONFIG_CDMA_MODEM_CBP82 -DECLARE_MODEM_INIT(cbp82); -#else -DECLARE_MODEM_INIT_DUMMY(cbp82) -#endif - -#ifdef CONFIG_LTE_MODEM_CMC220 -DECLARE_MODEM_INIT(cmc220); -#else -DECLARE_MODEM_INIT_DUMMY(cmc220) -#endif - #ifdef CONFIG_LTE_MODEM_CMC221 DECLARE_MODEM_INIT(cmc221); #else DECLARE_MODEM_INIT_DUMMY(cmc221) #endif -#ifdef CONFIG_UMTS_MODEM_SS222 -DECLARE_MODEM_INIT(ss222); -#else -DECLARE_MODEM_INIT_DUMMY(ss222) -#endif - #ifdef CONFIG_CDMA_MODEM_MDM6600 DECLARE_MODEM_INIT(mdm6600); #else @@ -96,12 +79,6 @@ DECLARE_MODEM_INIT(esc6270); DECLARE_MODEM_INIT_DUMMY(esc6270) #endif -#ifdef CONFIG_CDMA_MODEM_QSC6085 -DECLARE_MODEM_INIT(qsc6085); -#else -DECLARE_MODEM_INIT_DUMMY(qsc6085) -#endif - #ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 DECLARE_MODEM_INIT(sprd8803); #else @@ -117,18 +94,6 @@ DECLARE_LINK_INIT(mipi); DECLARE_LINK_INIT_DUMMY(mipi) #endif -#ifdef CONFIG_LINK_DEVICE_USB -DECLARE_LINK_INIT(usb); -#else -DECLARE_LINK_INIT_DUMMY(usb) -#endif - -#ifdef CONFIG_LINK_DEVICE_HSIC -DECLARE_LINK_INIT(hsic); -#else -DECLARE_LINK_INIT_DUMMY(hsic) -#endif - #ifdef CONFIG_LINK_DEVICE_DPRAM DECLARE_LINK_INIT(dpram); #else @@ -141,52 +106,53 @@ DECLARE_LINK_INIT(pld); DECLARE_LINK_INIT_DUMMY(pld) #endif -#ifdef CONFIG_LINK_DEVICE_C2C -DECLARE_LINK_INIT(c2c); +#ifdef CONFIG_LINK_DEVICE_SPI +DECLARE_LINK_INIT(spi); #else -DECLARE_LINK_INIT_DUMMY(c2c) +DECLARE_LINK_INIT_DUMMY(spi) #endif -#ifdef CONFIG_LINK_DEVICE_SHMEM -DECLARE_LINK_INIT(shmem); +#ifdef CONFIG_LINK_DEVICE_USB +DECLARE_LINK_INIT(usb); #else -DECLARE_LINK_INIT_DUMMY(shmem) +DECLARE_LINK_INIT_DUMMY(usb) #endif -#ifdef CONFIG_LINK_DEVICE_SPI -DECLARE_LINK_INIT(spi); +#ifdef CONFIG_LINK_DEVICE_HSIC +DECLARE_LINK_INIT(hsic); #else -DECLARE_LINK_INIT_DUMMY(spi) +DECLARE_LINK_INIT_DUMMY(hsic) +#endif + +#ifdef CONFIG_LINK_DEVICE_C2C +DECLARE_LINK_INIT(c2c); +#else +DECLARE_LINK_INIT_DUMMY(c2c) #endif typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *); -static modem_init_call modem_init_func[MAX_MODEM_TYPE] = { - [IMC_XMM6260] = MODEM_INIT_CALL(xmm6260), - [IMC_XMM6262] = MODEM_INIT_CALL(xmm6262), - [VIA_CBP71] = MODEM_INIT_CALL(cbp71), - [VIA_CBP72] = MODEM_INIT_CALL(cbp72), - [VIA_CBP82] = MODEM_INIT_CALL(cbp82), - [SEC_CMC220] = MODEM_INIT_CALL(cmc220), - [SEC_CMC221] = MODEM_INIT_CALL(cmc221), - [SEC_SS222] = MODEM_INIT_CALL(ss222), - [QC_MDM6600] = MODEM_INIT_CALL(mdm6600), - [QC_ESC6270] = MODEM_INIT_CALL(esc6270), - [QC_QSC6085] = MODEM_INIT_CALL(qsc6085), - [SPRD_SC8803] = MODEM_INIT_CALL(sprd8803), - [DUMMY] = MODEM_INIT_CALL(dummy), +static modem_init_call modem_init_func[] = { + MODEM_INIT_CALL(xmm6260), + MODEM_INIT_CALL(xmm6262), + MODEM_INIT_CALL(cbp71), + MODEM_INIT_CALL(cbp72), + MODEM_INIT_CALL(cmc221), + MODEM_INIT_CALL(mdm6600), + MODEM_INIT_CALL(esc6270), + MODEM_INIT_CALL(sprd8803), + MODEM_INIT_CALL(dummy), }; typedef struct link_device *(*link_init_call)(struct platform_device *); -static link_init_call link_init_func[LINKDEV_MAX] = { - [LINKDEV_UNDEFINED] = LINK_INIT_CALL(undefined), - [LINKDEV_MIPI] = LINK_INIT_CALL(mipi), - [LINKDEV_USB] = LINK_INIT_CALL(usb), - [LINKDEV_HSIC] = LINK_INIT_CALL(hsic), - [LINKDEV_DPRAM] = LINK_INIT_CALL(dpram), - [LINKDEV_PLD] = LINK_INIT_CALL(pld), - [LINKDEV_C2C] = LINK_INIT_CALL(c2c), - [LINKDEV_SHMEM] = LINK_INIT_CALL(shmem), - [LINKDEV_SPI] = LINK_INIT_CALL(spi), +static link_init_call link_init_func[] = { + LINK_INIT_CALL(undefined), + LINK_INIT_CALL(mipi), + LINK_INIT_CALL(dpram), + LINK_INIT_CALL(spi), + LINK_INIT_CALL(usb), + LINK_INIT_CALL(hsic), + LINK_INIT_CALL(c2c), + LINK_INIT_CALL(pld), }; static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata) diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c index 21adf9e..28f95f7 100644 --- a/drivers/misc/modem_if/sipc4_io_device.c +++ b/drivers/misc/modem_if/sipc4_io_device.c @@ -1,4 +1,6 @@ -/* +/* /linux/drivers/misc/modem_if/modem_io_device.c + * + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -25,6 +27,9 @@ #include #include +#ifdef CONFIG_LINK_DEVICE_C2C +#include +#endif #include "modem_prj.h" #include "modem_utils.h" @@ -35,8 +40,7 @@ * So, give restriction to allocation size below 1 page to prevent * big pages broken. */ -#define MAX_RXDATA_SIZE (4096 - 512) -#define MAX_BOOTDATA_SIZE 0x4008 /* EBL package format*/ +#define MAX_RXDATA_SIZE 0x0E00 /* 4 * 1024 - 512 */ #define MAX_MULTI_FMT_SIZE 0x4000 /* 16 * 1024 */ static const char hdlc_start[1] = { HDLC_START }; @@ -246,6 +250,22 @@ static int rx_hdlc_head_check(struct io_device *iod, struct link_device *ld, hdr->start = HDLC_START; hdr->len = 0; + /* debug print */ + switch (iod->format) { + case IPC_FMT: + case IPC_RAW: + case IPC_MULTI_RAW: + case IPC_RFS: + /* TODO: print buf... */ + break; + + case IPC_CMD: + case IPC_BOOT: + case IPC_RAMDUMP: + default: + break; + } + buf += len; done_len += len; rest -= len; /* rest, call by value */ @@ -389,6 +409,12 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ +#if 0 + mif_err("\n<%s> Rx FMT frame (len %d)\n", + iod->name, rcvd); + print_sipc4_fmt_frame(data); + mif_err("\n"); +#endif skb_queue_tail(&iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -423,6 +449,12 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_info("The Last (ID %d, %d bytes received)\n", id, skb->len); +#if 0 + mif_err("\n<%s> Rx FMT frame (len %d)\n", + iod->name, skb->len); + print_sipc4_fmt_frame(skb->data); + mif_err("\n"); +#endif skb_queue_tail(&iod->sk_rx_q, skb); iod->skb[id] = NULL; mif_info("wake up wq of %s\n", iod->name); @@ -459,6 +491,12 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ +#if 0 + mif_err("\n<%s> Rx FMT frame (len %d)\n", + iod->name, rcvd); + print_sipc4_fmt_frame(data); + mif_err("\n"); +#endif skb_queue_tail(&real_iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -492,6 +530,12 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_err("The Last (ID %d, %d bytes received)\n", id, skb->len); +#if 0 + mif_err("\n<%s> Rx FMT frame (len %d)\n", + iod->name, skb->len); + print_sipc4_fmt_frame(skb->data); + mif_err("\n"); +#endif skb_queue_tail(&real_iod->sk_rx_q, skb); real_iod->skb[id] = NULL; mif_info("wake up wq of %s\n", real_iod->name); @@ -775,6 +819,55 @@ exit: return err; } +static int rx_rfs_packet(struct io_device *iod, struct link_device *ld, + const char *data, unsigned size) +{ + int err = 0; + int pad = 0; + int rcvd = 0; + struct sk_buff *skb; + + if (data[0] != HDLC_START) { + mif_err("Dropping RFS packet ... " + "size = %d, start = %02X %02X %02X %02X\n", + size, + data[0], data[1], data[2], data[3]); + return -EINVAL; + } + + if (data[size-1] != HDLC_END) { + for (pad = 1; pad < 4; pad++) + if (data[(size-1)-pad] == HDLC_END) + break; + + if (pad >= 4) { + char *b = (char *)data; + unsigned sz = size; + mif_err("size %d, No END_FLAG!!!\n", size); + mif_err("end = %02X %02X %02X %02X\n", + b[sz-4], b[sz-3], b[sz-2], b[sz-1]); + return -EINVAL; + } else { + mif_info("padding = %d\n", pad); + } + } + + skb = rx_alloc_skb(size, iod, ld); + if (unlikely(!skb)) { + mif_err("alloc_skb fail\n"); + return -ENOMEM; + } + + /* copy the RFS haeder to skb->data */ + rcvd = size - sizeof(hdlc_start) - sizeof(hdlc_end) - pad; + memcpy(skb_put(skb, rcvd), ((char *)data + sizeof(hdlc_start)), rcvd); + + fragdata(iod, ld)->skb_recv = skb; + err = rx_iodev_skb(fragdata(iod, ld)->skb_recv); + + return err; +} + /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) @@ -798,9 +891,14 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, */ switch (iod->format) { + case IPC_RFS: +#ifdef CONFIG_IPC_CMC22x_OLD_RFS + err = rx_rfs_packet(iod, ld, data, len); + return err; +#endif + case IPC_FMT: case IPC_RAW: - case IPC_RFS: case IPC_MULTI_RAW: if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); @@ -863,17 +961,12 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - struct modem_ctl *mc = iod->mc; - int old_state = mc->phone_state; + iod->mc->phone_state = state; + mif_err("modem state changed. (iod: %s, state: %d)\n", + iod->name, state); - if (old_state != state) { - mc->phone_state = state; - mif_err("%s state changed (%s -> %s)\n", mc->name, - get_cp_state_str(old_state), get_cp_state_str(state)); - } - - if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || - state == STATE_NV_REBUILDING) + if ((state == STATE_CRASH_RESET) || (state == STATE_CRASH_EXIT) + || (state == STATE_NV_REBUILDING)) wake_up(&iod->wq); } @@ -884,24 +977,10 @@ static void io_dev_modem_state_changed(struct io_device *iod, */ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) { - -#if defined(CONFIG_MACH_KONA) && defined(CONFIG_UMTS_MODEM_XMM6262) - mif_err("modem_current_state is %d\n", iod->mc->phone_state); -#endif - if (atomic_read(&iod->opened) == 0) { - mif_err("iod is not opened: %s\n", iod->name); - /* update latest sim status */ - iod->mc->sim_state.online = sim_online; - } -#if defined(CONFIG_LINK_DEVICE_HSIC) && defined(CONFIG_UMTS_MODEM_XMM6262) // fixed modem unknown issue (kina 3G) - else if (iod->mc->phone_state == STATE_BOOTING) { - mif_err("modem_current_state is STATE_BOOTING\n"); - /* update latest sim status */ - iod->mc->sim_state.online = sim_online; - } -#endif - else if (iod->mc->sim_state.online == sim_online) { + mif_err("iod is not opened: %s\n", + iod->name); + } else if (iod->mc->sim_state.online == sim_online) { mif_err("sim state not changed.\n"); } else { iod->mc->sim_state.online = sim_online; @@ -916,7 +995,6 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) } } - static int misc_open(struct inode *inode, struct file *filp) { struct io_device *iod = to_io_device(filp->private_data); @@ -1000,32 +1078,32 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case IOCTL_MODEM_ON: - mif_debug("%s: IOCTL_MODEM_ON\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_ON\n"); return iod->mc->ops.modem_on(iod->mc); case IOCTL_MODEM_OFF: - mif_debug("%s: IOCTL_MODEM_OFF\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_OFF\n"); return iod->mc->ops.modem_off(iod->mc); case IOCTL_MODEM_RESET: - mif_debug("%s: IOCTL_MODEM_RESET\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_RESET\n"); return iod->mc->ops.modem_reset(iod->mc); case IOCTL_MODEM_BOOT_ON: - mif_debug("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_ON\n"); return iod->mc->ops.modem_boot_on(iod->mc); case IOCTL_MODEM_BOOT_OFF: - mif_debug("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_OFF\n"); return iod->mc->ops.modem_boot_off(iod->mc); /* TODO - will remove this command after ril updated */ case IOCTL_MODEM_BOOT_DONE: - mif_debug("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n"); return 0; case IOCTL_MODEM_STATUS: - mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_STATUS\n"); p_state = iod->mc->phone_state; if ((p_state == STATE_CRASH_RESET) || @@ -1048,7 +1126,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return p_state; case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); + mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_SUSPEND\n"); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1056,16 +1134,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) iodevs_for_each(iod->msd, iodev_netif_stop, 0); return 0; - case IOCTL_MODEM_DL_START: - mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); - return ld->dload_start(ld, iod); - - case IOCTL_MODEM_FW_UPDATE: - mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); - return ld->firm_update(ld, iod, arg); - case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); + mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_RESUME\n"); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1074,29 +1144,21 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_START: - mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); - return ld->dump_start(ld, iod); - - case IOCTL_MODEM_RAMDUMP_START: - mif_err("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); + mif_err("misc_ioctl : IOCTL_MODEM_DUMP_START\n"); return ld->dump_start(ld, iod); case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_DUMP_UPDATE\n"); return ld->dump_update(ld, iod, arg); - case IOCTL_MODEM_RAMDUMP_STOP: - mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); - return ld->dump_finish(ld, iod, arg); - case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_debug("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); + mif_debug("misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n"); if (iod->mc->ops.modem_force_crash_exit) return iod->mc->ops.modem_force_crash_exit(iod->mc); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: - mif_err("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); + mif_err("misc_ioctl : IOCTL_MODEM_CP_UPLOAD\n"); if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), (void __user *)arg, MAX_CPINFO_SIZE) != 0) panic("CP Crash"); @@ -1105,12 +1167,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_RESET: - mif_err("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); + mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n"); return iod->mc->ops.modem_dump_reset(iod->mc); #if defined(CONFIG_SEC_DUAL_MODEM_MODE) case IOCTL_MODEM_SWITCH_MODEM: - mif_err("%s: IOCTL_MODEM_SWITCH_MODEM\n", iod->name); + mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n"); iod->mc->phone_state = STATE_MODEM_SWITCH; wake_up(&iod->wq); return 0; @@ -1126,6 +1188,20 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_dump_log(iod->mc->msd, iod); return 0; + case IOCTL_MIF_DPRAM_DUMP: +#ifdef CONFIG_LINK_DEVICE_DPRAM + if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { + size = iod->mc->mdm_data->dpram_ctl->dp_size; + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) + return -EFAULT; + mif_dump_dpram(iod); + return 0; + } +#endif + return -EINVAL; + default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1133,48 +1209,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_err("%s: ioctl 0x%X is not defined\n", iod->name, cmd); + mif_err("misc_ioctl : ioctl 0x%X is not defined.\n", cmd); return -EINVAL; } return 0; } -static size_t _boot_write(struct io_device *iod, const char __user *buf, - size_t count) -{ - int rest_len = count, frame_len = 0; - char *cur = (char *)buf; - struct sk_buff *skb = NULL; - struct link_device *ld = get_current_link(iod); - int ret; - - while (rest_len) { - frame_len = min(rest_len, MAX_BOOTDATA_SIZE); - skb = alloc_skb(frame_len, GFP_KERNEL); - if (!skb) { - mif_err("fail alloc skb (%d)\n", __LINE__); - return -ENOMEM; - } - if (copy_from_user( - skb_put(skb, frame_len), cur, frame_len) != 0) { - dev_kfree_skb_any(skb); - return -EFAULT; - } - rest_len -= frame_len; - cur += frame_len; - - skbpriv(skb)->iod = iod; - skbpriv(skb)->ld = ld; - - ret = ld->send(ld, iod, skb); - if (ret < 0) { - dev_kfree_skb_any(skb); - return ret; - } - } - return count; -} - static ssize_t misc_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { @@ -1197,10 +1237,6 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb = alloc_skb(frame_len, GFP_KERNEL); if (!skb) { - if (frame_len > MAX_BOOTDATA_SIZE && iod->format == IPC_BOOT) { - mif_info("large alloc fail\n"); - return _boot_write(iod, buf, count); - } mif_err("fail alloc skb (%d)\n", __LINE__); return -ENOMEM; } @@ -1242,6 +1278,31 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb_put(skb, calc_padding_size(iod, ld, skb->len)); +#if 0 + if (iod->format == IPC_FMT) { + mif_err("\n<%s> Tx HDLC FMT frame (len %d)\n", + iod->name, skb->len); + print_sipc4_hdlc_fmt_frame(skb->data); + mif_err("\n"); + } +#endif +#if 0 + if (iod->format == IPC_RAW) { + mif_err("\n<%s> Tx HDLC RAW frame (len %d)\n", + iod->name, skb->len); + mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); + mif_err("\n"); + } +#endif +#if 0 + if (iod->format == IPC_RFS) { + mif_err("\n<%s> Tx HDLC RFS frame (len %d)\n", + iod->name, skb->len); + mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); + mif_err("\n"); + } +#endif + /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data */ @@ -1348,6 +1409,43 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return pktsize; } +#ifdef CONFIG_LINK_DEVICE_C2C +static int misc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int r = 0; + unsigned long size = 0; + unsigned long pfn = 0; + unsigned long offset = 0; + struct io_device *iod = (struct io_device *)filp->private_data; + + if (!vma) + return -EFAULT; + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { + mif_err("offset + size > C2C_CP_RGN_SIZE\n"); + return -EINVAL; + } + + /* Set the noncacheable property to the region */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= VM_RESERVED | VM_IO; + + pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); + r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); + if (r) { + mif_err("Failed in remap_pfn_range()!!!\n"); + return -EAGAIN; + } + + mif_err("VA = 0x%08lx, offset = 0x%lx, size = %lu\n", + vma->vm_start, offset, size); + + return 0; +} +#endif + static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1356,6 +1454,9 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, +#ifdef CONFIG_LINK_DEVICE_C2C + .mmap = misc_mmap, +#endif }; static int vnet_open(struct net_device *ndev) diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c index 542b482..59e2de9 100644 --- a/drivers/misc/modem_if/sipc4_modem.c +++ b/drivers/misc/modem_if/sipc4_modem.c @@ -328,22 +328,9 @@ static int modem_resume(struct device *pdev) return 0; } -#ifdef CONFIG_FAST_BOOT -static void modem_complete(struct device *pdev) -{ - struct modem_ctl *mc = dev_get_drvdata(pdev); - - if (mc->modem_complete) - mc->modem_complete(mc); -} -#endif - static const struct dev_pm_ops modem_pm_ops = { .suspend = modem_suspend, .resume = modem_resume, -#ifdef CONFIG_FAST_BOOT - .complete = modem_complete, -#endif }; static struct platform_driver modem_driver = { diff --git a/drivers/misc/modem_if/sipc5_common.c b/drivers/misc/modem_if/sipc5_common.c deleted file mode 100644 index 277f669..0000000 --- a/drivers/misc/modem_if/sipc5_common.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2010 Samsung Electronics. - * - * 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 "modem_prj.h" -#include "modem_utils.h" - -/** - * sipc5_start_valid - * @cfg: configuration field of an SIPC5 link frame - * - * Returns TRUE if the start (configuration field) of an SIPC5 link frame - * is valid or returns FALSE if it is not valid. - * - */ -bool sipc5_start_valid(u8 *frm) -{ - return (*frm & SIPC5_START_MASK) == SIPC5_START_MASK; -} - -bool sipc5_padding_exist(u8 *frm) -{ - return (*frm & SIPC5_PADDING_EXIST) ? true : false; -} - -bool sipc5_multi_frame(u8 *frm) -{ - return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_CTL_FIELD_MASK; -} - -bool sipc5_ext_len(u8 *frm) -{ - return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_EXT_LENGTH_MASK; -} - -/** - * sipc5_get_hdr_len - * @cfg: configuration field of an SIPC5 link frame - * - * Returns the length of SIPC5 link layer header in an SIPC5 link frame - * - */ -int sipc5_get_hdr_len(u8 *frm) -{ - if (*frm & SIPC5_EXT_FIELD_EXIST) { - if (*frm & SIPC5_CTL_FIELD_EXIST) - return SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - return SIPC5_HEADER_SIZE_WITH_EXT_LEN; - } else { - return SIPC5_MIN_HEADER_SIZE; - } -} - -/** - * sipc5_get_ch_id - * @frm: pointer to an SIPC5 frame - * - * Returns the channel ID in an SIPC5 link frame - * - */ -u8 sipc5_get_ch_id(u8 *frm) -{ - return *(frm + SIPC5_CH_ID_OFFSET); -} - -/** - * sipc5_get_ctrl_field - * @frm: pointer to an SIPC5 frame - * - * Returns the control field in an SIPC5 link frame - * - */ -u8 sipc5_get_ctrl_field(u8 *frm) -{ - return *(frm + SIPC5_CTL_OFFSET); -} - -/** - * sipc5_get_frame_len - * @frm: pointer to an SIPC5 link frame - * - * Returns the length of an SIPC5 link frame - * - */ -int sipc5_get_frame_len(u8 *frm) -{ - u8 cfg = frm[0]; - u16 *sz16 = (u16 *)(frm + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(frm + SIPC5_LEN_OFFSET); - - if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { - if (cfg & SIPC5_CTL_FIELD_EXIST) - return (int)(*sz16); - else - return (int)(*sz32); - } else { - return (int)(*sz16); - } -} - -/** - * sipc5_calc_padding_size - * @len: length of an SIPC5 link frame - * - * Returns the padding size for an SIPC5 link frame - * - */ -int sipc5_calc_padding_size(int len) -{ - int residue = len & 0x3; - return residue ? (4 - residue) : 0; -} - -/** - * sipc5_get_total_len - * @frm: pointer to an SIPC5 link frame - * - * Returns the total length of an SIPC5 link frame with padding - * - */ -int sipc5_get_total_len(u8 *frm) -{ - int len = sipc5_get_frame_len(frm); - int pad = sipc5_padding_exist(frm) ? sipc5_calc_padding_size(len) : 0; - return len + pad; -} - -/** - * sipc5_build_config - * @iod: pointer to the IO device - * @ld: pointer to the link device - * @count: length of the data to be transmitted - * - * Builds a config value for an SIPC5 link frame header - * - * Returns the config value for the header or 0 for non-SIPC formats - */ -u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count) -{ - u8 cfg = SIPC5_START_MASK; - - if (iod->format > IPC_MULTI_RAW && iod->id == 0) - return 0; - - if (ld->aligned) - cfg |= SIPC5_PADDING_EXIST; - -#if 0 - if ((count + SIPC5_MIN_HEADER_SIZE) > ld->mtu[dev]) - cfg |= SIPC5_CTL_FIELD_MASK; - else -#endif - if ((count + SIPC5_MIN_HEADER_SIZE) > 0xFFFF) - cfg |= SIPC5_EXT_LENGTH_MASK; - - return cfg; -} - -/** - * sipc5_build_header - * @iod: pointer to the IO device - * @ld: pointer to the link device - * @buff: pointer to a buffer in which an SIPC5 link frame header will be stored - * @cfg: value for the config field in the header - * @ctrl: value for the control field in the header - * @count: length of data in the SIPC5 link frame to be transmitted - * - * Builds the link layer header of an SIPC5 frame - */ -void sipc5_build_header(struct io_device *iod, struct link_device *ld, - u8 *buff, u8 cfg, u8 ctrl, u32 count) -{ - u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); - u32 hdr_len = sipc5_get_hdr_len(&cfg); - - /* Store the config field and the channel ID field */ - buff[SIPC5_CONFIG_OFFSET] = cfg; - buff[SIPC5_CH_ID_OFFSET] = iod->id; - - /* Store the frame length field */ - if (sipc5_ext_len(buff)) - *sz32 = (u32)(hdr_len + count); - else - *sz16 = (u16)(hdr_len + count); - - /* Store the control field */ - if (sipc5_multi_frame(buff)) - buff[SIPC5_CTL_OFFSET] = ctrl; -} - -/** - * std_udl_get_cmd - * @frm: pointer to an SIPC5 link frame - * - * Returns the standard BOOT/DUMP (STD_UDL) command in an SIPC5 BOOT/DUMP frame. - */ -u32 std_udl_get_cmd(u8 *frm) -{ - u8 *cmd = frm + sipc5_get_hdr_len(frm); - return *((u32 *)cmd); -} - -/** - * std_udl_with_payload - * @cmd: standard BOOT/DUMP command - * - * Returns true if the STD_UDL command has a payload. - */ -bool std_udl_with_payload(u32 cmd) -{ - u32 mask = cmd & STD_UDL_STEP_MASK; - return (mask && mask < STD_UDL_CRC) ? true : false; -} - diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c index a0174fd..a9932c1 100644 --- a/drivers/misc/modem_if/sipc5_io_device.c +++ b/drivers/misc/modem_if/sipc5_io_device.c @@ -1,4 +1,5 @@ -/* +/* /linux/drivers/misc/modem_if/sipc5_io_device.c + * * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -23,9 +24,11 @@ #include #include #include -#include #include +#ifdef CONFIG_LINK_DEVICE_C2C +#include +#endif #include "modem_prj.h" #include "modem_utils.h" @@ -101,7 +104,7 @@ static void iodev_showtxlink(struct io_device *iod, void *args) struct link_device *ld = get_current_link(iod); if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) - *p += sprintf(*p, "%s<->%s\n", iod->name, ld->name); + *p += sprintf(*p, "%s: %s\n", iod->name, ld->name); } static ssize_t show_txlink(struct device *dev, @@ -127,107 +130,181 @@ static ssize_t store_txlink(struct device *dev, static struct device_attribute attr_txlink = __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink); -static int netif_flow_ctrl(struct link_device *ld, struct sk_buff *skb) +/** + * rx_check_frame_cfg + * @cfg: configuration field of a link layer header + * @frm: pointer to the sipc5_frame_data buffer + * + * 1) Checks whether or not an extended field exists + * 2) Calculates the length of a link layer header + * + * Returns the size of a link layer header + * + * Must be invoked only when the configuration field of the link layer header + * is validated with sipc5_start_valid() function + */ +static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm) { - u8 cmd = skb->data[0]; - - if (cmd == FLOW_CTRL_SUSPEND) { - if (ld->suspend_netif_tx) - goto exit; - ld->suspend_netif_tx = true; - mif_netif_stop(ld); - mif_info("%s: FLOW_CTRL_SUSPEND\n", ld->name); - } else if (cmd == FLOW_CTRL_RESUME) { - if (!ld->suspend_netif_tx) - goto exit; - ld->suspend_netif_tx = false; - mif_netif_wake(ld); - mif_info("%s: FLOW_CTRL_RESUME\n", ld->name); + frm->config = cfg; + + if (likely(cfg & SIPC5_PADDING_EXIST)) + frm->padding = true; + + if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { + if (cfg & SIPC5_CTL_FIELD_EXIST) { + frm->ctl_fld = true; + frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; + } else { + frm->ext_len = true; + frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN; + } } else { - mif_info("%s: ERR! invalid command %02X\n", ld->name, cmd); + frm->hdr_len = SIPC5_MIN_HEADER_SIZE; } -exit: - dev_kfree_skb_any(skb); - return 0; + return frm->hdr_len; } -static inline int queue_skb_to_iod(struct sk_buff *skb, struct io_device *iod) +/** + * rx_build_meta_data + * @ld: pointer to the link device + * @frm: pointer to the sipc5_frame_data buffer + * + * Fills each field of sipc5_frame_data from a link layer header + * 1) Extracts the channel ID + * 2) Calculates the length of a link layer frame + * 3) Extracts a control field if exists + * 4) Calculates the length of an IPC message packet in the link layer frame + * + */ +static void rx_build_meta_data(struct link_device *ld, + struct sipc5_frame_data *frm) { - struct sk_buff_head *rxq = &iod->sk_rx_q; + u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET); - skb_queue_tail(rxq, skb); + frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET]; - if (iod->format < IPC_MULTI_RAW && rxq->qlen > MAX_IOD_RXQ_LEN) { - struct sk_buff *victim = skb_dequeue(rxq); - mif_err("%s: %s application may be dead (rxq->qlen %d > %d)\n", - iod->name, iod->app ? iod->app : "corresponding", - rxq->qlen, MAX_IOD_RXQ_LEN); - if (victim) - dev_kfree_skb_any(victim); - return -ENOSPC; - } else { - mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); - return 0; - } -} + if (unlikely(frm->ext_len)) + frm->len = *sz32; + else + frm->len = *sz16; -static int rx_drain(struct sk_buff *skb) -{ - dev_kfree_skb_any(skb); - return 0; + if (unlikely(frm->ctl_fld)) + frm->control = frm->hdr[SIPC5_CTL_OFFSET]; + + frm->data_len = frm->len - frm->hdr_len; + + mif_debug("%s: FRM ch:%d len:%d ctl:%02X data.len:%d\n", + ld->name, frm->ch_id, frm->len, frm->control, frm->data_len); } -static int rx_loopback(struct sk_buff *skb) +/** + * tx_build_link_header + * @frm: pointer to the sipc5_frame_data buffer + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @count: length of the data to be transmitted + * + * Builds the meta data for an SIPC5 frame and the link layer header of it + * Returns the link layer header length for an SIPC5 frame or 0 for other frame + */ +static unsigned tx_build_link_header(struct sipc5_frame_data *frm, + struct io_device *iod, struct link_device *ld, ssize_t count) { - struct io_device *iod = skbpriv(skb)->iod; - struct link_device *ld = skbpriv(skb)->ld; - int ret; + u8 *buff = frm->hdr; + u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); - ret = ld->send(ld, iod, skb); - if (ret < 0) { - mif_err("%s->%s: ERR! ld->send fail (err %d)\n", - iod->name, ld->name, ret); + memset(frm, 0, sizeof(struct sipc5_frame_data)); + + if (iod->format == IPC_CMD || + iod->format == IPC_BOOT || + iod->format == IPC_RAMDUMP) { + frm->len = count; + return 0; } - return ret; -} + frm->config = SIPC5_START_MASK; -static int rx_fmt_frame(struct sk_buff *skb) -{ - struct link_device *ld = skbpriv(skb)->ld; - struct io_device *iod = skbpriv(skb)->iod; - struct sk_buff *rx_skb; - int hdr_len = sipc5_get_hdr_len(skb->data); - u8 ctrl; - u8 id; + if (iod->format == IPC_FMT && count > 2048) { + frm->ctl_fld = true; + frm->config |= SIPC5_EXT_FIELD_EXIST; + frm->config |= SIPC5_CTL_FIELD_EXIST; + } - if (!sipc5_multi_frame(skb->data)) { - skb_pull(skb, hdr_len); - queue_skb_to_iod(skb, iod); - wake_up(&iod->wq); - return 0; + if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) { + frm->ext_len = true; + frm->config |= SIPC5_EXT_FIELD_EXIST; } - /* Get the control field */ - ctrl = sipc5_get_ctrl_field(skb->data); + if (ld->aligned) + frm->config |= SIPC5_PADDING_EXIST; + + frm->ch_id = iod->id; + + frm->hdr_len = sipc5_get_hdr_len(frm->config); + frm->data_len = count; + frm->len = frm->hdr_len + frm->data_len; - /* Extract the control ID from the control field */ - id = ctrl & 0x7F; + buff[SIPC5_CONFIG_OFFSET] = frm->config; + buff[SIPC5_CH_ID_OFFSET] = frm->ch_id; + + if (unlikely(frm->ext_len)) + *sz32 = (u32)frm->len; + else + *sz16 = (u16)frm->len; - /* Remove SIPC5 link header */ - skb_pull(skb, hdr_len); + if (unlikely(frm->ctl_fld)) + buff[SIPC5_CTL_OFFSET] = frm->control; + + return frm->hdr_len; +} + +static int rx_fmt_frame(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = skbpriv(skb)->ld; + struct sk_buff_head *rxq = &iod->sk_rx_q; + struct sipc_fmt_hdr *fh; + struct sk_buff *rx_skb; + u8 ctrl = skbpriv(skb)->control; + unsigned id = ctrl & 0x7F; - /* If there has been no multiple frame with this ID, ... */ if (iod->skb[id] == NULL) { - struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)skb->data; + /* + ** There has been no multiple frame with this ID. + */ + if ((ctrl & 0x80) == 0) { + /* + ** It is a single frame because the "more" bit is 0. + */ + skb_queue_tail(rxq, skb); + if (unlikely(rxq->qlen > 2048)) { + struct sk_buff *victim; + mif_info("%s: WARNING! rxq->qlen %d > 2048\n", + iod->name, rxq->qlen); + victim = skb_dequeue(rxq); + dev_kfree_skb_any(victim); + } else { + mif_debug("%s: rxq->qlen = %d\n", + iod->name, rxq->qlen); + } - mif_err("%s->%s: start of multi-frame (ID:%d len:%d)\n", - ld->name, iod->name, id, fh->len); + wake_up(&iod->wq); + return 0; + } + + /* + ** The start of multiple frames + */ + fh = (struct sipc_fmt_hdr *)skb->data; + mif_debug("%s: start multi-frame (ID:%d len:%d)\n", + iod->name, id, fh->len); rx_skb = rx_alloc_skb(fh->len, iod, ld); if (!rx_skb) { - mif_err("%s: ERR! rx_alloc_skb fail\n", iod->name); + mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name); return -ENOMEM; } @@ -236,19 +313,31 @@ static int rx_fmt_frame(struct sk_buff *skb) rx_skb = iod->skb[id]; } - /* Perform multi-frame processing */ + /* + ** Start multi-frame processing + */ memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); dev_kfree_skb_any(skb); if (ctrl & 0x80) { /* The last frame has not arrived yet. */ - mif_info("%s->%s: recv multi-frame (ID:%d rcvd:%d)\n", - ld->name, iod->name, id, rx_skb->len); + mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n", + iod->name, id, rx_skb->len); } else { /* It is the last frame because the "more" bit is 0. */ - mif_err("%s->%s: end of multi-frame (ID:%d rcvd:%d)\n", - ld->name, iod->name, id, rx_skb->len); - queue_skb_to_iod(rx_skb, iod); + mif_debug("%s: end multi-frame (ID:%d rcvd:%d)\n", + iod->name, id, rx_skb->len); + skb_queue_tail(rxq, rx_skb); + if (unlikely(rxq->qlen > 2048)) { + struct sk_buff *victim; + mif_info("%s: WARNING! rxq->qlen %d > 2048\n", + iod->name, rxq->qlen); + victim = skb_dequeue(rxq); + dev_kfree_skb_any(victim); + } else { + mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); + } + iod->skb[id] = NULL; wake_up(&iod->wq); } @@ -256,26 +345,89 @@ static int rx_fmt_frame(struct sk_buff *skb) return 0; } -static int rx_raw_misc(struct sk_buff *skb) +static int rx_rfs_frame(struct sk_buff *skb) { struct io_device *iod = skbpriv(skb)->iod; + struct sk_buff_head *rxq = &iod->sk_rx_q; - /* Remove the SIPC5 link header */ - skb_pull(skb, sipc5_get_hdr_len(skb->data)); + skb_queue_tail(rxq, skb); + if (unlikely(rxq->qlen > 2048)) { + struct sk_buff *victim; + mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); + victim = skb_dequeue(rxq); + dev_kfree_skb_any(victim); + } else { + mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); + } - queue_skb_to_iod(skb, iod); wake_up(&iod->wq); return 0; } -static int rx_multi_pdp(struct sk_buff *skb) +static int rx_loopback(struct sk_buff *skb) { - struct link_device *ld = skbpriv(skb)->ld; struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = get_current_link(iod); + struct sipc5_frame_data frm; + unsigned headroom; + unsigned tailroom = 0; + int ret; + + headroom = tx_build_link_header(&frm, iod, ld, skb->len); + + if (ld->aligned) + tailroom = sipc5_calc_padding_size(headroom + skb->len); + + /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb) + * already alloc 32bytes padding in headroom. 32bytes are enough. + */ + + /* store IPC link header to start of skb + * this is skb_push not skb_put. different with misc_write. + */ + memcpy(skb_push(skb, headroom), frm.hdr, headroom); + + /* store padding */ + if (tailroom) + skb_put(skb, tailroom); + + /* forward */ + ret = ld->send(ld, iod, skb); + if (ret < 0) + mif_err("%s->%s: ld->send fail: %d\n", iod->name, + ld->name, ret); + return ret; +} + +static int rx_raw_misc(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ + struct sk_buff_head *rxq = &iod->sk_rx_q; + + skb_queue_tail(rxq, skb); + if (unlikely(rxq->qlen > 2048)) { + struct sk_buff *victim; + mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); + victim = skb_dequeue(rxq); + dev_kfree_skb_any(victim); + } else { + mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); + } + + wake_up(&iod->wq); + + return 0; +} + +static int rx_multi_pdp(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ struct net_device *ndev; struct iphdr *iphdr; + struct ethhdr *ehdr; int ret; + const char source[ETH_ALEN] = SOURCE_MAC_ADDR; ndev = iod->ndev; if (!ndev) { @@ -283,9 +435,6 @@ static int rx_multi_pdp(struct sk_buff *skb) return -ENODEV; } - /* Remove the SIPC5 link header */ - skb_pull(skb, sipc5_get_hdr_len(skb->data)); - skb->dev = ndev; ndev->stats.rx_packets++; ndev->stats.rx_bytes += skb->len; @@ -298,15 +447,14 @@ static int rx_multi_pdp(struct sk_buff *skb) skb->protocol = htons(ETH_P_IP); if (iod->use_handover) { - struct ethhdr *ehdr; - const char source[ETH_ALEN] = SOURCE_MAC_ADDR; - - ehdr = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr)); + skb_push(skb, sizeof(struct ethhdr)); + ehdr = (void *)skb->data; memcpy(ehdr->h_dest, ndev->dev_addr, ETH_ALEN); memcpy(ehdr->h_source, source, ETH_ALEN); ehdr->h_proto = skb->protocol; skb->ip_summed = CHECKSUM_UNNECESSARY; skb_reset_mac_header(skb); + skb_pull(skb, sizeof(struct ethhdr)); } @@ -315,82 +463,48 @@ static int rx_multi_pdp(struct sk_buff *skb) else ret = netif_rx_ni(skb); - if (ret != NET_RX_SUCCESS) { - mif_err("%s->%s: ERR! netif_rx fail (err %d)\n", - ld->name, iod->name, ret); - } + if (ret != NET_RX_SUCCESS) + mif_info("%s: ERR! netif_rx fail (err %d)\n", iod->name, ret); return ret; } static int rx_demux(struct link_device *ld, struct sk_buff *skb) { - struct io_device *iod; - u8 ch = sipc5_get_ch_id(skb->data); -#ifdef DEBUG_MODEM_IF - struct modem_ctl *mc = ld->mc; - size_t len = (skb->len > 20) ? 20 : skb->len; - char tag[MIF_MAX_STR_LEN]; -#endif + struct io_device *iod = NULL; + char *link = ld->name; + u8 ch = skbpriv(skb)->ch_id; - if (unlikely(ch == 0)) { - mif_err("%s: ERR! invalid ch# %d\n", ld->name, ch); + if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) { + mif_info("%s: ERR! invalid ch# %d\n", link, ch); return -ENODEV; } - if (unlikely(ch == SIPC5_CH_ID_FLOW_CTRL)) - return netif_flow_ctrl(ld, skb); - /* IP loopback */ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr) ch = RMNET0_CH_ID; iod = link_get_iod_with_channel(ld, ch); if (unlikely(!iod)) { - mif_err("%s: ERR! no iod with ch# %d\n", ld->name, ch); + mif_info("%s: ERR! no iod for ch# %d\n", link, ch); return -ENODEV; } skbpriv(skb)->ld = ld; skbpriv(skb)->iod = iod; + skbpriv(skb)->real_iod = iod; - /* Don't care whether or not DATA_DRAIN_CHANNEL is opened */ - if (iod->id == DATA_DRAIN_CHANNEL) - return rx_drain(skb); - - /* Don't care whether or not DATA_LOOPBACK_CHANNEL is opened */ - if (iod->id == DATA_LOOPBACK_CHANNEL) + /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */ + if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL)) return rx_loopback(skb); -#ifdef DEBUG_MODEM_IF - snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", mc->name, iod->name); - if (unlikely(iod->format == IPC_FMT)) - pr_ipc(1, tag, skb->data, len); -#if 0 - if (iod->format == IPC_RAW) - pr_ipc(0, tag, skb->data, len); -#endif -#if 0 - if (iod->format == IPC_BOOT) - pr_ipc(0, tag, skb->data, len); -#endif -#if 0 - if (iod->format == IPC_RAMDUMP) - pr_ipc(0, tag, skb->data, len); -#endif -#if 0 - if (ch == 28) - pr_ipc(0, tag, skb->data, len); -#endif -#endif /*DEBUG_MODEM_IF*/ - if (atomic_read(&iod->opened) <= 0) { - mif_err("%s: ERR! %s is not opened\n", ld->name, iod->name); + mif_info("%s: ERR! %s is not opened\n", link, iod->name); return -ENODEV; } if (ch >= SIPC5_CH_ID_RFS_0) - return rx_raw_misc(skb); + return rx_rfs_frame(skb); else if (ch >= SIPC5_CH_ID_FMT_0) return rx_fmt_frame(skb); else if (iod->io_typ == IODEV_MISC) @@ -399,337 +513,342 @@ static int rx_demux(struct link_device *ld, struct sk_buff *skb) return rx_multi_pdp(skb); } -/** - * rx_frame_config - * @iod: pointer to an instance of io_device structure - * @ld: pointer to an instance of link_device structure - * @buff: pointer to a buffer in which incoming data is stored - * @size: size of data in the buffer - * @frm: pointer to an instance of sipc5_frame_data structure - * - * 1) Checks a config field - * 2) Calculates the length of link layer header in an incoming frame and stores - * the value to "frm->hdr_len" - * 3) Stores the config field to "frm->hdr" and add the size of config field to - * "frm->hdr_rcvd" - * - * Returns the length of a config field that was copied to "frm" - */ -static int rx_frame_config(struct io_device *iod, struct link_device *ld, - u8 *buff, int size, struct sipc5_frame_data *frm) +/* Check and store link layer header, then alloc an skb */ +static int rx_header_from_serial(struct io_device *iod, struct link_device *ld, + u8 *buff, unsigned size, struct sipc5_frame_data *frm) { - int rest; - int rcvd; + char *link = ld->name; + struct sk_buff *skb; + int len; + u8 cfg = buff[0]; - if (unlikely(!sipc5_start_valid(buff))) { - mif_err("%s->%s: ERR! INVALID config 0x%02x\n", - ld->name, iod->name, buff[0]); - return -EBADMSG; + mif_debug("%s: size %d\n", link, size); + + if (!frm->config) { + if (unlikely(!sipc5_start_valid(cfg))) { + mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); + return -EBADMSG; + } + rx_check_frame_cfg(cfg, frm); + + /* Copy the link layer header to the header buffer */ + len = min(frm->hdr_len, size); + memcpy(frm->hdr, buff, len); + } else { + /* Copy the link layer header to the header buffer */ + len = min((frm->hdr_len - frm->hdr_rcvd), size); + memcpy((frm->hdr + frm->hdr_rcvd), buff, len); } - frm->hdr_len = sipc5_get_hdr_len(buff); + frm->hdr_rcvd += len; - /* Calculate the size of a segment that will be copied */ - rest = frm->hdr_len; - rcvd = SIPC5_CONFIG_SIZE; - mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", - ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, - rcvd); + mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n", + link, frm->hdr_len, frm->hdr_rcvd); - /* Copy the config field of an SIPC5 link header to the header buffer */ - memcpy(frm->hdr, buff, rcvd); - frm->hdr_rcvd += rcvd; + if (frm->hdr_rcvd >= frm->hdr_len) { + rx_build_meta_data(ld, frm); + skb = rx_alloc_skb(frm->data_len, iod, ld); + fragdata(iod, ld)->skb_recv = skb; + skbpriv(skb)->ch_id = frm->ch_id; + skbpriv(skb)->control = frm->control; + } - return rcvd; + return len; } -/** - * rx_frame_prepare_skb - * @iod: pointer to an instance of io_device structure - * @ld: pointer to an instance of link_device structure - * @frm: pointer to an instance of sipc5_frame_data structure - * - * 1) Extracts the length of a link frame from the link header in "frm->hdr" - * 2) Allocates an skb - * 3) Calculates the payload size in the link frame - * 4) Calculates the padding size in the link frame - * - * Returns the pointer to an skb - */ -static struct sk_buff *rx_frame_prepare_skb(struct io_device *iod, - struct link_device *ld, struct sipc5_frame_data *frm) +/* copy data to skb */ +static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld, + u8 *buff, unsigned size, struct sipc5_frame_data *frm) { - struct sk_buff *skb; - - /* Get the frame length */ - frm->len = sipc5_get_frame_len(frm->hdr); + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + char *link = ld->name; + unsigned rest = frm->data_len - frm->data_rcvd; + unsigned len; - /* Allocate an skb */ - skb = rx_alloc_skb(frm->len, iod, ld); - if (!skb) { - mif_err("%s->%s: ERR! rx_alloc_skb fail (size %d)\n", - ld->name, iod->name, frm->len); - return NULL; - } + /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */ + rest = frm->data_len - frm->data_rcvd; + mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n", + link, frm->data_len, frm->data_rcvd, rest, size); - /* Calculates the payload size */ - frm->pay_len = frm->len - frm->hdr_len; + /* If there is no skb, data must be dropped. */ + len = min(rest, size); + if (skb) + memcpy(skb_put(skb, len), buff, len); - /* Calculates the padding size */ - if (sipc5_padding_exist(frm->hdr)) - frm->pad_len = sipc5_calc_padding_size(frm->len); + frm->data_rcvd += len; - mif_debug("%s->%s: size %d (header:%d payload:%d padding:%d)\n", - ld->name, iod->name, frm->len, frm->hdr_len, frm->pay_len, - frm->pad_len); + mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n", + link, frm->data_len, frm->data_rcvd); - return skb; + return len; } -/** - * rx_frame_header - * @iod: pointer to an instance of io_device structure - * @ld: pointer to an instance of link_device structure - * @buff: pointer to a buffer in which incoming data is stored - * @size: size of data in the buffer - * @frm: pointer to an instance of sipc5_frame_data structure - * - * 1) Stores a link layer header to "frm->hdr" temporarily while "frm->hdr_rcvd" - * is less than "frm->hdr_len" - * 2) Then, - * Allocates an skb - * Copies the link header from "frm" to "skb" - * Register the skb to receive payload - * - * Returns the size of a segment that was copied to "frm" - */ -static int rx_frame_header(struct io_device *iod, struct link_device *ld, - u8 *buff, int size, struct sipc5_frame_data *frm) +static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld, + const char *data, unsigned size) { + struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; - int rest; - int rcvd; + char *link = ld->name; + u8 *buff = (u8 *)data; + int rest = (int)size; + int err = 0; + int done = 0; - /* Calculate the size of a segment that will be copied */ - rest = frm->hdr_len - frm->hdr_rcvd; - rcvd = min(rest, size); - mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", - ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, - rcvd); + mif_debug("%s: size = %d\n", link, size); - /* Copy a segment of an SIPC5 link header to "frm" */ - memcpy((frm->hdr + frm->hdr_rcvd), buff, rcvd); - frm->hdr_rcvd += rcvd; + if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) { + /* + ** There is an skb that is waiting for more SIPC5 data. + ** In this case, rx_header_from_serial() must be skipped. + */ + mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n", + link, frm->data_len, frm->data_rcvd); + goto recv_data; + } - if (frm->hdr_rcvd >= frm->hdr_len) { - /* Prepare an skb with the information in {iod, ld, frm} */ - skb = rx_frame_prepare_skb(iod, ld, frm); - if (!skb) { - mif_err("%s->%s: ERR! rx_frame_prepare_skb fail\n", - ld->name, iod->name); - return -ENOMEM; +next_frame: + /* Receive and analyze header, then prepare an akb */ + err = done = rx_header_from_serial(iod, ld, buff, rest, frm); + if (err < 0) + goto err_exit; + + buff += done; + rest -= done; + mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest); + if (rest < 0) + goto err_range; + + if (rest == 0) + return size; + +recv_data: + err = 0; + + mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest); + + done = rx_payload_from_serial(iod, ld, buff, rest, frm); + buff += done; + rest -= done; + + mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest); + + if (rest == 0 && frm->data_rcvd < frm->data_len) { + /* + Data is being received and more data will come within the next + frame from the link device. + */ + return size; + } + + /* At this point, one complete link layer frame has been received. */ + + /* A padding size is applied to access the next IPC frame. */ + if (frm->padding) { + done = sipc5_calc_padding_size(frm->len); + if (done > rest) { + mif_info("%s: ERR! padding %d > rest %d\n", + link, done, rest); + goto err_exit; } - /* Copy an SIPC5 link header from "frm" to "skb" */ - memcpy(skb_put(skb, frm->hdr_len), frm->hdr, frm->hdr_len); + buff += done; + rest -= done; - /* Register the skb to receive payload */ - fragdata(iod, ld)->skb_recv = skb; + mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest); + + if (rest < 0) + goto err_range; + + } + + skb = fragdata(iod, ld)->skb_recv; + if (likely(skb)) { + mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); + err = rx_demux(ld, skb); + if (err < 0) + dev_kfree_skb_any(skb); + } else { + mif_debug("%s: len:%d -> drop\n", link, skb->len); + } + + /* initialize the skb_recv and the frame_data buffer */ + fragdata(iod, ld)->skb_recv = NULL; + memset(frm, 0, sizeof(struct sipc5_frame_data)); + + if (rest > 0) + goto next_frame; + + if (rest <= 0) + return size; + +err_exit: + if (fragdata(iod, ld)->skb_recv && + frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd >= frm->data_len) { + dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); + memset(frm, 0, sizeof(struct sipc5_frame_data)); + fragdata(iod, ld)->skb_recv = NULL; + mif_info("%s: ERR! clear frag\n", link); } + return err; - return rcvd; +err_range: + mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest); + return size; } /** - * rx_frame_payload - * @iod: pointer to an instance of io_device structure - * @ld: pointer to an instance of link_device structure - * @buff: pointer to a buffer in which incoming data is stored - * @size: size of data in the buffer - * @frm: pointer to an instance of sipc5_frame_data structure + * rx_header_from_mem + * @ld: pointer to the link device + * @buff: pointer to the frame + * @rest: size of the frame + * @frm: pointer to the sipc5_frame_data buffer * - * Stores a link layer payload to "skb" + * 1) Verifies a link layer header configuration of a frame + * 2) Stores the link layer header to the header buffer + * 3) Builds and stores the meta data of the frame into a meta data buffer + * 4) Verifies the length of the frame * - * Returns the size of a segment that was copied to "skb" + * Returns SIPC5 header length */ -static int rx_frame_payload(struct io_device *iod, struct link_device *ld, - u8 *buff, int size, struct sipc5_frame_data *frm) +static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest, + struct sipc5_frame_data *frm) { - struct sk_buff *skb = fragdata(iod, ld)->skb_recv; - int rest; - int rcvd; - - /* Calculate the size of a segment that will be copied */ - rest = frm->pay_len - frm->pay_rcvd; - rcvd = min(rest, size); - mif_debug("%s->%s: pay_len:%d pay_rcvd:%d rest:%d size:%d rcvd:%d\n", - ld->name, iod->name, frm->pay_len, frm->pay_rcvd, rest, size, - rcvd); + char *link = ld->name; + u8 cfg = buff[0]; - /* Copy an SIPC5 link payload to "skb" */ - memcpy(skb_put(skb, rcvd), buff, rcvd); - frm->pay_rcvd += rcvd; - - return rcvd; -} + /* Verify link layer header configuration */ + if (unlikely(!sipc5_start_valid(cfg))) { + mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); + return -EBADMSG; + } + rx_check_frame_cfg(cfg, frm); -static int rx_frame_padding(struct io_device *iod, struct link_device *ld, - u8 *buff, int size, struct sipc5_frame_data *frm) -{ - struct sk_buff *skb = fragdata(iod, ld)->skb_recv; - int rest; - int rcvd; + /* Store the link layer header to the header buffer */ + memcpy(frm->hdr, buff, frm->hdr_len); + frm->hdr_rcvd = frm->hdr_len; - /* Calculate the size of a segment that will be dropped as padding */ - rest = frm->pad_len - frm->pad_rcvd; - rcvd = min(rest, size); - mif_debug("%s->%s: pad_len:%d pad_rcvd:%d rest:%d size:%d rcvd:%d\n", - ld->name, iod->name, frm->pad_len, frm->pad_rcvd, rest, size, - rcvd); + /* Build and store the meta data of this frame */ + rx_build_meta_data(ld, frm); - /* Copy an SIPC5 link padding to "skb" */ - memcpy(skb_put(skb, rcvd), buff, rcvd); - frm->pad_rcvd += rcvd; + /* Verify frame length */ + if (unlikely(frm->len > rest)) { + mif_info("%s: ERR! frame length %d > rest %d\n", + link, frm->len, rest); + return -EBADMSG; + } - return rcvd; + return frm->hdr_rcvd; } -static int rx_frame_done(struct io_device *iod, struct link_device *ld, - struct sk_buff *skb) +/* copy data to skb */ +static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len) { - /* Cut off the padding of the current frame */ - skb_trim(skb, sipc5_get_frame_len(skb->data)); - mif_debug("%s->%s: frame length = %d\n", ld->name, iod->name, skb->len); - - return rx_demux(ld, skb); + /* If there is no skb, data must be dropped. */ + if (skb) + memcpy(skb_put(skb, len), buff, len); + return len; } -static int recv_frame_from_buff(struct io_device *iod, struct link_device *ld, +static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld, const char *data, unsigned size) { struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; + char *link = ld->name; u8 *buff = (u8 *)data; int rest = (int)size; - int done = 0; - int err = 0; + int len; + int done; - mif_debug("%s->%s: size %d (RX state = %s)\n", ld->name, iod->name, - size, get_rx_state_str(iod->curr_rx_state)); + mif_debug("%s: size = %d\n", link, size); while (rest > 0) { - switch (iod->curr_rx_state) { - case IOD_RX_ON_STANDBY: - fragdata(iod, ld)->skb_recv = NULL; - memset(frm, 0, sizeof(struct sipc5_frame_data)); - - done = rx_frame_config(iod, ld, buff, rest, frm); - if (done < 0) { - err = done; - goto err_exit; - } - - iod->next_rx_state = IOD_RX_HEADER; + /* Initialize the frame data buffer */ + memset(frm, 0, sizeof(struct sipc5_frame_data)); + skb = NULL; - break; + /* Receive and analyze link layer header */ + done = rx_header_from_mem(ld, buff, rest, frm); + if (unlikely(done < 0)) + return -EBADMSG; - case IOD_RX_HEADER: - done = rx_frame_header(iod, ld, buff, rest, frm); - if (done < 0) { - err = done; - goto err_exit; - } - - if (frm->hdr_rcvd >= frm->hdr_len) - iod->next_rx_state = IOD_RX_PAYLOAD; - else - iod->next_rx_state = IOD_RX_HEADER; + /* Verify rest size */ + rest -= done; + if (rest < 0) { + mif_info("%s: ERR! rx_header -> rest %d\n", link, rest); + return -ERANGE; + } - break; + /* Move buff pointer to the payload */ + buff += done; - case IOD_RX_PAYLOAD: - done = rx_frame_payload(iod, ld, buff, rest, frm); - if (done < 0) { - err = done; - goto err_exit; - } + /* Prepare an akb */ + len = frm->data_len; + skb = rx_alloc_skb(len, iod, ld); - if (frm->pay_rcvd >= frm->pay_len) { - if (frm->pad_len > 0) - iod->next_rx_state = IOD_RX_PADDING; - else - iod->next_rx_state = IOD_RX_ON_STANDBY; - } else { - iod->next_rx_state = IOD_RX_PAYLOAD; - } + /* Store channel ID and control fields to the CB of the skb */ + skbpriv(skb)->ch_id = frm->ch_id; + skbpriv(skb)->control = frm->control; - break; + /* Receive payload */ + mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n", + link, done, rest, len); + done = rx_payload_from_mem(skb, buff, len); + rest -= done; + if (rest < 0) { + mif_info("%s: ERR! rx_payload() -> rest %d\n", + link, rest); + if (skb) + dev_kfree_skb_any(skb); + return -ERANGE; + } + buff += done; - case IOD_RX_PADDING: - done = rx_frame_padding(iod, ld, buff, rest, frm); - if (done < 0) { - err = done; - goto err_exit; + /* A padding size is applied to access the next IPC frame. */ + if (frm->padding) { + done = sipc5_calc_padding_size(frm->len); + if (done > rest) { + mif_info("%s: ERR! padding %d > rest %d\n", + link, done, rest); + if (skb) + dev_kfree_skb_any(skb); + return -ERANGE; } - - if (frm->pad_rcvd >= frm->pad_len) - iod->next_rx_state = IOD_RX_ON_STANDBY; - else - iod->next_rx_state = IOD_RX_PADDING; - - break; - - default: - mif_err("%s->%s: ERR! INVALID RX state %d\n", - ld->name, iod->name, iod->curr_rx_state); - err = -EINVAL; - goto err_exit; + buff += done; + rest -= done; } - if (iod->next_rx_state == IOD_RX_ON_STANDBY) { - /* - ** A complete frame is in fragdata(iod, ld)->skb_recv. - */ - skb = fragdata(iod, ld)->skb_recv; - err = rx_frame_done(iod, ld, skb); - if (err < 0) - goto err_exit; + if (likely(skb)) { + mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); + if (rx_demux(ld, skb) < 0) + dev_kfree_skb_any(skb); + } else { + mif_debug("%s: len:%d -> drop\n", link, skb->len); } - - buff += done; - rest -= done; - if (rest < 0) - goto err_range; - - iod->curr_rx_state = iod->next_rx_state; - } - - return size; - -err_exit: - if (fragdata(iod, ld)->skb_recv) { - mif_err("%s->%s: ERR! clear frag (size:%d done:%d rest:%d)\n", - ld->name, iod->name, size, done, rest); - dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); - fragdata(iod, ld)->skb_recv = NULL; } - iod->curr_rx_state = IOD_RX_ON_STANDBY; - return err; -err_range: - mif_err("%s->%s: ERR! size:%d done:%d rest:%d\n", - ld->name, iod->name, size, done, rest); - iod->curr_rx_state = IOD_RX_ON_STANDBY; - return size; + return 0; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) { + struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; + char *link = ld->name; int err; + if (!data) { + mif_info("%s: ERR! !data\n", link); + return -EINVAL; + } + + if (len <= 0) { + mif_info("%s: ERR! len %d <= 0\n", link, len); + return -EINVAL; + } + switch (iod->format) { case IPC_FMT: case IPC_RAW: @@ -738,151 +857,97 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - err = recv_frame_from_buff(iod, ld, data, len); - if (err < 0) { - mif_err("%s->%s: ERR! recv_frame_from_buff fail " - "(err %d)\n", ld->name, iod->name, err); - } + if (ld->link_type == LINKDEV_DPRAM && ld->aligned) + err = rx_frame_from_mem(iod, ld, data, len); + else + err = rx_frame_from_serial(iod, ld, data, len); - return err; + if (err < 0) + mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n", + link, err); - default: - mif_debug("%s->%s: len %d\n", ld->name, iod->name, len); + return err; + case IPC_CMD: + case IPC_BOOT: + case IPC_RAMDUMP: /* save packet to sk_buff */ skb = rx_alloc_skb(len, iod, ld); if (!skb) { - mif_info("%s->%s: ERR! rx_alloc_skb fail\n", - ld->name, iod->name); + mif_info("%s: ERR! rx_alloc_skb fail\n", link); return -ENOMEM; } - memcpy(skb_put(skb, len), data, len); - - queue_skb_to_iod(skb, iod); + mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name); + memcpy(skb_put(skb, len), data, len); + skb_queue_tail(rxq, skb); + if (unlikely(rxq->qlen > 2048)) { + struct sk_buff *victim; + mif_info("%s: ERR! rxq->qlen %d > 2048\n", + iod->name, rxq->qlen); + victim = skb_dequeue(rxq); + dev_kfree_skb_any(victim); + } wake_up(&iod->wq); return len; + + default: + mif_info("%s: ERR! unknown format %d\n", link, iod->format); + return -EINVAL; } } -static int recv_frame_from_skb(struct io_device *iod, struct link_device *ld, +static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { - struct sk_buff *clone; - unsigned int rest; - unsigned int rcvd; - int tot; /* total length including padding */ - int err = 0; + struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; + u8 cfg = skb->data[0]; - /* - ** If there is only one SIPC5 frame in @skb, receive the SIPC5 frame and - ** return immediately. In this case, the frame verification must already - ** have been done at the link device. - */ - if (skbpriv(skb)->single_frame) { - err = rx_frame_done(iod, ld, skb); - if (err < 0) - goto exit; - return 0; - } + /* Initialize the frame data buffer */ + memset(frm, 0, sizeof(struct sipc5_frame_data)); /* - ** The routine from here is used only if there may be multiple SIPC5 - ** frames in @skb. + ** The start of a link layer header has already been checked in the + ** link device. */ - /* Check the config field of the first frame in @skb */ - if (!sipc5_start_valid(skb->data)) { - mif_err("%s->%s: ERR! INVALID config 0x%02X\n", - ld->name, iod->name, skb->data[0]); - err = -EINVAL; - goto exit; - } - - /* Get the total length of the frame with a padding */ - tot = sipc5_get_total_len(skb->data); + /* Analyze the configuration of the link layer header */ + rx_check_frame_cfg(cfg, frm); - /* Verify the total length of the first frame */ - rest = skb->len; - if (unlikely(tot > rest)) { - mif_err("%s->%s: ERR! tot %d > skb->len %d)\n", - ld->name, iod->name, tot, rest); - err = -EINVAL; - goto exit; - } + /* Store the link layer header to the header buffer */ + memcpy(frm->hdr, skb->data, frm->hdr_len); + frm->hdr_rcvd = frm->hdr_len; - /* If there is only one SIPC5 frame in @skb, */ - if (likely(tot == rest)) { - /* Receive the SIPC5 frame and return immediately */ - err = rx_frame_done(iod, ld, skb); - if (err < 0) - goto exit; - return 0; - } + /* Build and store the meta data of this frame */ + rx_build_meta_data(ld, frm); /* - ** This routine is used only if there are multiple SIPC5 frames in @skb. + ** The length of the frame has already been checked in the link device. */ - rcvd = 0; - while (rest > 0) { - clone = skb_clone(skb, GFP_ATOMIC); - if (unlikely(!clone)) { - mif_err("%s->%s: ERR! skb_clone fail\n", - ld->name, iod->name); - err = -ENOMEM; - goto exit; - } - - /* Get the start of an SIPC5 frame */ - skb_pull(clone, rcvd); - if (!sipc5_start_valid(clone->data)) { - mif_err("%s->%s: ERR! INVALID config 0x%02X\n", - ld->name, iod->name, clone->data[0]); - dev_kfree_skb_any(clone); - err = -EINVAL; - goto exit; - } - - /* Get the total length of the current frame with a padding */ - tot = sipc5_get_total_len(clone->data); - if (unlikely(tot > rest)) { - mif_err("%s->%s: ERR! dirty frame (tot %d > rest %d)\n", - ld->name, iod->name, tot, rest); - dev_kfree_skb_any(clone); - err = -EINVAL; - goto exit; - } - - /* Cut off the padding of the current frame */ - skb_trim(clone, sipc5_get_frame_len(clone->data)); - /* Demux the frame */ - err = rx_demux(ld, clone); - if (err < 0) { - mif_err("%s->%s: ERR! rx_demux fail (err %d)\n", - ld->name, iod->name, err); - dev_kfree_skb_any(clone); - goto exit; - } + /* Trim the link layer header off the frame */ + skb_pull(skb, frm->hdr_len); - /* Calculate the start of the next frame */ - rcvd += tot; + /* Store channel ID and control fields to the CB of the skb */ + skbpriv(skb)->ch_id = frm->ch_id; + skbpriv(skb)->control = frm->control; - /* Calculate the rest size of data in @skb */ - rest -= tot; + /* Demux the frame */ + if (rx_demux(ld, skb) < 0) { + mif_err("%s: ERR! rx_demux fail\n", ld->name); + return -EINVAL; } -exit: - dev_kfree_skb_any(skb); - return err; + return 0; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { + char *link = ld->name; enum dev_format dev = iod->format; int err; @@ -894,35 +959,17 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - err = recv_frame_from_skb(iod, ld, skb); + err = rx_frame_from_skb(iod, ld, skb); if (err < 0) { - mif_err("%s->%s: ERR! recv_frame_from_skb fail " - "(err %d)\n", ld->name, iod->name, err); - } - - return err; - - case IPC_BOOT: - case IPC_RAMDUMP: - if (!iod->id) { - mif_err("%s->%s: ERR! invalid iod\n", - ld->name, iod->name); - return -ENODEV; - } - - if (iod->waketime) - wake_lock_timeout(&iod->wakelock, iod->waketime); - - err = recv_frame_from_skb(iod, ld, skb); - if (err < 0) { - mif_err("%s->%s: ERR! recv_frame_from_skb fail " - "(err %d)\n", ld->name, iod->name, err); + dev_kfree_skb_any(skb); + mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n", + link, err); } return err; default: - mif_err("%s->%s: ERR! invalid iod\n", ld->name, iod->name); + mif_info("%s: ERR! unknown device %d\n", link, dev); return -EINVAL; } } @@ -933,14 +980,10 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - struct modem_ctl *mc = iod->mc; - int old_state = mc->phone_state; + mif_info("%s: %s state changed (state %d)\n", + iod->name, iod->mc->name, state); - if (old_state != state) { - mc->phone_state = state; - mif_err("%s state changed (%s -> %s)\n", mc->name, - get_cp_state_str(old_state), get_cp_state_str(state)); - } + iod->mc->phone_state = state; if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || state == STATE_NV_REBUILDING) @@ -981,27 +1024,23 @@ static int misc_open(struct inode *inode, struct file *filp) struct io_device *iod = to_io_device(filp->private_data); struct modem_shared *msd = iod->msd; struct link_device *ld; - int ref_cnt; int ret; filp->private_data = (void *)iod; + atomic_inc(&iod->opened); + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->init_comm) { ret = ld->init_comm(ld, iod); if (ret < 0) { - mif_err("%s<->%s: ERR! init_comm fail(%d)\n", - iod->name, ld->name, ret); + mif_info("%s: init_comm fail(%d)\n", + ld->name, ret); return ret; } } } - ref_cnt = atomic_inc_return(&iod->opened); - - if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) - mif_err("%s (opened %d)\n", iod->name, ref_cnt); - else - mif_info("%s (opened %d)\n", iod->name, ref_cnt); + mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); return 0; } @@ -1011,8 +1050,8 @@ static int misc_release(struct inode *inode, struct file *filp) struct io_device *iod = (struct io_device *)filp->private_data; struct modem_shared *msd = iod->msd; struct link_device *ld; - int ref_cnt; + atomic_dec(&iod->opened); skb_queue_purge(&iod->sk_rx_q); list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -1020,12 +1059,7 @@ static int misc_release(struct inode *inode, struct file *filp) ld->terminate_comm(ld, iod); } - ref_cnt = atomic_dec_return(&iod->opened); - - if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) - mif_err("%s (opened %d)\n", iod->name, ref_cnt); - else - mif_info("%s (opened %d)\n", iod->name, ref_cnt); + mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); return 0; } @@ -1033,23 +1067,20 @@ static int misc_release(struct inode *inode, struct file *filp) static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) { struct io_device *iod = (struct io_device *)filp->private_data; - struct modem_ctl *mc = iod->mc; poll_wait(filp, &iod->wq, wait); - if (!skb_queue_empty(&iod->sk_rx_q) && mc->phone_state != STATE_OFFLINE) + if (!skb_queue_empty(&iod->sk_rx_q) && + iod->mc->phone_state != STATE_OFFLINE) { return POLLIN | POLLRDNORM; - - if (mc->phone_state == STATE_CRASH_RESET - || mc->phone_state == STATE_CRASH_EXIT - || mc->phone_state == STATE_NV_REBUILDING - || mc->sim_state.changed) { + } else if ((iod->mc->phone_state == STATE_CRASH_RESET) || + (iod->mc->phone_state == STATE_CRASH_EXIT) || + (iod->mc->phone_state == STATE_NV_REBUILDING) || + (iod->mc->sim_state.changed)) { if (iod->format == IPC_RAW) { msleep(20); return 0; } - if (iod->format == IPC_RAMDUMP) - return 0; return POLLHUP; } else { return 0; @@ -1058,191 +1089,132 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + int p_state; struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); - struct modem_ctl *mc = iod->mc; - int p_state; - char *buff; - void __user *user_buff; + char cpinfo_buf[530] = "CP Crash "; unsigned long size; + int ret; switch (cmd) { case IOCTL_MODEM_ON: - if (mc->ops.modem_on) { - mif_err("%s: IOCTL_MODEM_ON\n", iod->name); - return mc->ops.modem_on(mc); - } - mif_err("%s: !mc->ops.modem_on\n", iod->name); - return -EINVAL; + mif_info("%s: IOCTL_MODEM_ON\n", iod->name); + return iod->mc->ops.modem_on(iod->mc); case IOCTL_MODEM_OFF: - if (mc->ops.modem_off) { - mif_err("%s: IOCTL_MODEM_OFF\n", iod->name); - return mc->ops.modem_off(mc); - } - mif_err("%s: !mc->ops.modem_off\n", iod->name); - return -EINVAL; + mif_info("%s: IOCTL_MODEM_OFF\n", iod->name); + return iod->mc->ops.modem_off(iod->mc); case IOCTL_MODEM_RESET: - if (mc->ops.modem_reset) { - mif_err("%s: IOCTL_MODEM_RESET\n", iod->name); - return mc->ops.modem_reset(mc); - } - mif_err("%s: !mc->ops.modem_reset\n", iod->name); - return -EINVAL; + mif_info("%s: IOCTL_MODEM_RESET\n", iod->name); + return iod->mc->ops.modem_reset(iod->mc); case IOCTL_MODEM_BOOT_ON: - if (mc->ops.modem_boot_on) { - mif_err("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); - return mc->ops.modem_boot_on(mc); - } - mif_err("%s: !mc->ops.modem_boot_on\n", iod->name); - return -EINVAL; + mif_info("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); + return iod->mc->ops.modem_boot_on(iod->mc); case IOCTL_MODEM_BOOT_OFF: - if (mc->ops.modem_boot_off) { - mif_err("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); - return mc->ops.modem_boot_off(mc); - } - mif_err("%s: !mc->ops.modem_boot_off\n", iod->name); - return -EINVAL; + mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); + return iod->mc->ops.modem_boot_off(iod->mc); case IOCTL_MODEM_BOOT_DONE: mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); - if (mc->ops.modem_boot_done) - return mc->ops.modem_boot_done(mc); - return 0; + if (iod->mc->ops.modem_boot_done) + return iod->mc->ops.modem_boot_done(iod->mc); + else + return 0; case IOCTL_MODEM_STATUS: mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); - p_state = mc->phone_state; + p_state = iod->mc->phone_state; if ((p_state == STATE_CRASH_RESET) || (p_state == STATE_CRASH_EXIT)) { - mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", - iod->name, get_cp_state_str(p_state)); - } else if (mc->sim_state.changed) { - int s_state = mc->sim_state.online ? + mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", + iod->name, p_state); + } else if (iod->mc->sim_state.changed) { + int s_state = iod->mc->sim_state.online ? STATE_SIM_ATTACH : STATE_SIM_DETACH; - mc->sim_state.changed = false; + iod->mc->sim_state.changed = false; return s_state; } else if (p_state == STATE_NV_REBUILDING) { - mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", - iod->name, get_cp_state_str(p_state)); - mc->phone_state = STATE_ONLINE; + mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", + iod->name, p_state); + iod->mc->phone_state = STATE_ONLINE; } return p_state; - case IOCTL_MODEM_XMIT_BOOT: - if (ld->xmit_boot) { - mif_info("%s: IOCTL_MODEM_XMIT_BOOT\n", iod->name); - return ld->xmit_boot(ld, iod, arg); - } - mif_err("%s: !ld->xmit_boot\n", iod->name); - return -EINVAL; + case IOCTL_MODEM_PROTOCOL_SUSPEND: + mif_debug("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", + iod->name); - case IOCTL_MODEM_DL_START: - if (ld->dload_start) { - mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); - return ld->dload_start(ld, iod); - } - mif_err("%s: !ld->dload_start\n", iod->name); - return -EINVAL; + if (iod->format != IPC_MULTI_RAW) + return -EINVAL; - case IOCTL_MODEM_FW_UPDATE: - if (ld->firm_update) { - mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); - return ld->firm_update(ld, iod, arg); - } - mif_err("%s: !ld->firm_update\n", iod->name); - return -EINVAL; + iodevs_for_each(iod->msd, iodev_netif_stop, 0); + return 0; - case IOCTL_MODEM_FORCE_CRASH_EXIT: - if (mc->ops.modem_force_crash_exit) { - mif_err("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", - iod->name); - return mc->ops.modem_force_crash_exit(mc); - } - mif_err("%s: !mc->ops.modem_force_crash_exit\n", iod->name); - return -EINVAL; + case IOCTL_MODEM_PROTOCOL_RESUME: + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", + iod->name); - case IOCTL_MODEM_DUMP_RESET: - if (mc->ops.modem_dump_reset) { - mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); - return mc->ops.modem_dump_reset(mc); - } - mif_err("%s: !mc->ops.modem_dump_reset\n", iod->name); - return -EINVAL; + if (iod->format != IPC_MULTI_RAW) + return -EINVAL; - case IOCTL_MODEM_DUMP_START: - if (ld->dump_start) { - mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); - return ld->dump_start(ld, iod); - } - mif_err("%s: !ld->dump_start\n", iod->name); - return -EINVAL; + iodevs_for_each(iod->msd, iodev_netif_wake, 0); + return 0; - case IOCTL_MODEM_RAMDUMP_START: - if (ld->dump_start) { - mif_info("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); - return ld->dump_start(ld, iod); - } - mif_err("%s: !ld->dump_start\n", iod->name); - return -EINVAL; + case IOCTL_MODEM_DUMP_START: + mif_info("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); case IOCTL_MODEM_DUMP_UPDATE: - if (ld->dump_update) { - mif_info("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); - return ld->dump_update(ld, iod, arg); - } - mif_err("%s: !ld->dump_update\n", iod->name); - return -EINVAL; + mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); + return ld->dump_update(ld, iod, arg); - case IOCTL_MODEM_RAMDUMP_STOP: - if (ld->dump_finish) { - mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); - return ld->dump_finish(ld, iod, arg); - } - mif_err("%s: !ld->dump_finish\n", iod->name); + case IOCTL_MODEM_FORCE_CRASH_EXIT: + mif_info("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); + if (iod->mc->ops.modem_force_crash_exit) + return iod->mc->ops.modem_force_crash_exit(iod->mc); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: mif_info("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); - strcpy(iod->msd->cp_crash_info, CP_CRASH_TAG); - if (arg) { - buff = iod->msd->cp_crash_info + strlen(CP_CRASH_TAG); - user_buff = (void __user *)arg; - if (copy_from_user(buff, user_buff, MAX_CPINFO_SIZE)) - return -EFAULT; - } - panic(iod->msd->cp_crash_info); + if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), + (void __user *)arg, MAX_CPINFO_SIZE) != 0) + return -EFAULT; + panic(cpinfo_buf); return 0; - case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); - if (iod->format == IPC_MULTI_RAW) { - iodevs_for_each(iod->msd, iodev_netif_stop, 0); - return 0; - } - return -EINVAL; - - case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); - if (iod->format != IPC_MULTI_RAW) { - iodevs_for_each(iod->msd, iodev_netif_wake, 0); - return 0; - } - return -EINVAL; + case IOCTL_MODEM_DUMP_RESET: + mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); + return iod->mc->ops.modem_dump_reset(iod->mc); case IOCTL_MIF_LOG_DUMP: iodevs_for_each(iod->msd, iodev_dump_status, 0); - user_buff = (void __user *)arg; size = MAX_MIF_BUFF_SIZE; - if (copy_to_user(user_buff, &size, sizeof(unsigned long))) + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) return -EFAULT; - mif_dump_log(mc->msd, iod); + + mif_dump_log(iod->mc->msd, iod); return 0; + case IOCTL_MIF_DPRAM_DUMP: +#ifdef CONFIG_LINK_DEVICE_DPRAM + if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { + size = iod->mc->mdm_data->dpram_ctl->dp_size; + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) + return -EFAULT; + mif_dump_dpram(iod); + return 0; + } +#endif + return -EINVAL; + default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1250,10 +1222,9 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_info("%s: ERR! undefined cmd 0x%X\n", iod->name, cmd); + mif_info("%s: ERR! cmd 0x%X not defined.\n", iod->name, cmd); return -EINVAL; } - return 0; } @@ -1263,76 +1234,49 @@ static ssize_t misc_write(struct file *filp, const char __user *data, struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); struct sk_buff *skb; - u8 *buff; int ret; - size_t headroom; - size_t tailroom; - size_t tx_bytes; - u8 cfg; + unsigned headroom = 0; + unsigned tailroom = 0; + size_t tx_size; + struct sipc5_frame_data frm; + struct timespec epoch; if (iod->format <= IPC_RFS && iod->id == 0) return -EINVAL; - cfg = sipc5_build_config(iod, ld, count); - - if (cfg) - headroom = sipc5_get_hdr_len(&cfg); - else - headroom = 0; + headroom = tx_build_link_header(&frm, iod, ld, count); if (ld->aligned) tailroom = sipc5_calc_padding_size(headroom + count); - else - tailroom = 0; - tx_bytes = headroom + count + tailroom; + tx_size = headroom + count + tailroom; - skb = alloc_skb(tx_bytes, GFP_KERNEL); + skb = alloc_skb(tx_size, GFP_KERNEL); if (!skb) { - mif_info("%s: ERR! alloc_skb fail (tx_bytes:%d)\n", - iod->name, tx_bytes); + mif_info("%s: ERR! alloc_skb fail (tx_size:%d)\n", + iod->name, tx_size); return -ENOMEM; } - /* Build SIPC5 link header*/ - if (cfg) { - buff = skb_put(skb, headroom); - sipc5_build_header(iod, ld, buff, cfg, 0, count); - } + /* store IPC link header*/ + memcpy(skb_put(skb, headroom), frm.hdr, headroom); - /* Store IPC message */ - buff = skb_put(skb, count); - if (copy_from_user(buff, data, count)) { - mif_err("%s->%s: ERR! copy_from_user fail (count %d)\n", - iod->name, ld->name, count); - dev_kfree_skb_any(skb); + /* store IPC message */ + if (copy_from_user(skb_put(skb, count), data, count) != 0) { + if (skb) + dev_kfree_skb_any(skb); return -EFAULT; } - /* Apply padding */ - if (tailroom) - skb_put(skb, tailroom); - - if (iod->format == IPC_FMT) { - struct timespec epoch; - u8 *msg = (skb->data + headroom); -#if 0 - char tag[MIF_MAX_STR_LEN]; - snprintf(tag, MIF_MAX_STR_LEN, "%s: RIL2MIF", iod->mc->name); - pr_ipc(1, tag, msg, (count > 20 ? 20 : count)); -#endif + if (iod->id == SIPC5_CH_ID_FMT_0) { getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); - mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, msg, count); + mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len); } -#if 0 - if (iod->format == IPC_RAMDUMP) { - char tag[MIF_MAX_STR_LEN]; - snprintf(tag, MIF_MAX_STR_LEN, "%s: DUMP2MIF", iod->name); - pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); - } -#endif + /* store padding */ + if (tailroom) + skb_put(skb, tailroom); /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data @@ -1342,15 +1286,13 @@ static ssize_t misc_write(struct file *filp, const char __user *data, ret = ld->send(ld, iod, skb); if (ret < 0) { - mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", - iod->name, ld->name, ret, tx_bytes); + mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); return ret; } - if (ret != tx_bytes) { - mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", - iod->name, ld->name, ret, tx_bytes, count); - } + if (ret != tx_size) + mif_info("%s: wrong tx size (count:%d tx_size:%d ret:%d)\n", + iod->name, count, tx_size, ret); return count; } @@ -1362,38 +1304,24 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; int copied = 0; + struct timespec epoch; - if (skb_queue_empty(rxq)) { + skb = skb_dequeue(rxq); + if (!skb) { mif_info("%s: ERR! no data in rxq\n", iod->name); return 0; } - skb = skb_dequeue(rxq); - - if (iod->format == IPC_FMT) { - struct timespec epoch; -#if 0 - char tag[MIF_MAX_STR_LEN]; - snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2RIL", iod->mc->name); - pr_ipc(0, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); -#endif + if (iod->id == SIPC5_CH_ID_FMT_0) { getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len); } -#if 0 - if (iod->format == IPC_RAMDUMP) { - char tag[MIF_MAX_STR_LEN]; - snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2DUMP", iod->name); - pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); - } -#endif - copied = skb->len > count ? count : skb->len; if (copy_to_user(buf, skb->data, copied)) { - mif_err("%s: ERR! copy_to_user fail\n", iod->name); + mif_info("%s: ERR! copy_to_user fail\n", iod->name); dev_kfree_skb_any(skb); return -EFAULT; } @@ -1411,6 +1339,43 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return copied; } +#ifdef CONFIG_LINK_DEVICE_C2C +static int misc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int r = 0; + unsigned long size = 0; + unsigned long pfn = 0; + unsigned long offset = 0; + struct io_device *iod = (struct io_device *)filp->private_data; + + if (!vma) + return -EFAULT; + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { + mif_info("ERR: offset + size > C2C_CP_RGN_SIZE\n"); + return -EINVAL; + } + + /* Set the noncacheable property to the region */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= VM_RESERVED | VM_IO; + + pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); + r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); + if (r) { + mif_info("ERR: Failed in remap_pfn_range()!!!\n"); + return -EAGAIN; + } + + mif_info("%s: VA = 0x%08lx, offset = 0x%lx, size = %lu\n", + iod->name, vma->vm_start, offset, size); + + return 0; +} +#endif + static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1419,6 +1384,9 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, +#ifdef CONFIG_LINK_DEVICE_C2C + .mmap = misc_mmap, +#endif }; static int vnet_open(struct net_device *ndev) @@ -1451,13 +1419,11 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct link_device *ld = get_current_link(iod); struct sk_buff *skb_new; int ret; - unsigned headroom; - unsigned tailroom; - size_t count; - size_t tx_bytes; + unsigned headroom = 0; + unsigned tailroom = 0; + unsigned long tx_bytes = skb->len; struct iphdr *ip_header = NULL; - u8 *buff; - u8 cfg; + struct sipc5_frame_data frm; /* When use `handover' with Network Bridge, * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here. @@ -1470,18 +1436,19 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_pull(skb, sizeof(struct ethhdr)); } - count = skb->len; - - cfg = sipc5_build_config(iod, ld, count); + headroom = tx_build_link_header(&frm, iod, ld, skb->len); - headroom = sipc5_get_hdr_len(&cfg); + /* ip loop-back */ + ip_header = (struct iphdr *)skb->data; + if (iod->msd->loopback_ipaddr && + ip_header->daddr == iod->msd->loopback_ipaddr) { + swap(ip_header->saddr, ip_header->daddr); + frm.ch_id = DATA_LOOPBACK_CHANNEL; + frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; + } if (ld->aligned) - tailroom = sipc5_calc_padding_size(headroom + count); - else - tailroom = 0; - - tx_bytes = headroom + count + tailroom; + tailroom = sipc5_calc_padding_size(frm.len); if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) { mif_debug("%s: skb_copy_expand needed\n", iod->name); @@ -1496,18 +1463,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_new = skb; } - /* Build SIPC5 link header*/ - buff = skb_push(skb_new, headroom); - sipc5_build_header(iod, ld, buff, cfg, 0, count); - - /* IP loop-back */ - ip_header = (struct iphdr *)skb->data; - if (iod->msd->loopback_ipaddr && - ip_header->daddr == iod->msd->loopback_ipaddr) { - swap(ip_header->saddr, ip_header->daddr); - buff[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; - } - + memcpy(skb_push(skb_new, headroom), frm.hdr, headroom); if (tailroom) skb_put(skb_new, tailroom); @@ -1517,18 +1473,12 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) ret = ld->send(ld, iod, skb_new); if (ret < 0) { netif_stop_queue(ndev); - mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", - iod->name, ld->name, ret, tx_bytes); + mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); return NETDEV_TX_BUSY; } - if (ret != tx_bytes) { - mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", - iod->name, ld->name, ret, tx_bytes, count); - } - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += count; + ndev->stats.tx_bytes += tx_bytes; return NETDEV_TX_OK; } @@ -1631,19 +1581,16 @@ int sipc5_init_io_device(struct io_device *iod) ret = misc_register(&iod->miscdev); if (ret) mif_info("%s: ERR! misc_register fail\n", iod->name); - ret = device_create_file(iod->miscdev.this_device, &attr_waketime); if (ret) mif_info("%s: ERR! device_create_file fail\n", iod->name); - ret = device_create_file(iod->miscdev.this_device, &attr_loopback); if (ret) mif_err("failed to create `loopback file' : %s\n", iod->name); - ret = device_create_file(iod->miscdev.this_device, &attr_txlink); if (ret) diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c index 8fc65e4..f5e33d3 100644 --- a/drivers/misc/modem_if/sipc5_modem.c +++ b/drivers/misc/modem_if/sipc5_modem.c @@ -34,7 +34,6 @@ #include #include -#include #include "modem_prj.h" #include "modem_variation.h" #include "modem_utils.h" @@ -78,31 +77,27 @@ static struct modem_shared *create_modem_shared_data(void) static struct modem_ctl *create_modemctl_device(struct platform_device *pdev, struct modem_shared *msd) { - struct modem_data *pdata = pdev->dev.platform_data; + int ret = 0; + struct modem_data *pdata; struct modem_ctl *modemctl; - int ret; + struct device *dev = &pdev->dev; /* create modem control device */ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL); - if (!modemctl) { - mif_err("%s: modemctl kzalloc fail\n", pdata->name); - mif_err("%s: xxx\n", pdata->name); + if (!modemctl) return NULL; - } modemctl->msd = msd; - modemctl->dev = &pdev->dev; + modemctl->dev = dev; modemctl->phone_state = STATE_OFFLINE; + pdata = pdev->dev.platform_data; modemctl->mdm_data = pdata; modemctl->name = pdata->name; /* init modemctl device for getting modemctl operations */ ret = call_modem_init_func(modemctl, pdata); if (ret) { - mif_err("%s: call_modem_init_func fail (err %d)\n", - pdata->name, ret); - mif_err("%s: xxx\n", pdata->name); kfree(modemctl); return NULL; } @@ -116,8 +111,8 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, struct modem_shared *msd, struct modem_ctl *modemctl, struct modem_data *pdata) { - int ret; - struct io_device *iod; + int ret = 0; + struct io_device *iod = NULL; iod = kzalloc(sizeof(struct io_device), GFP_KERNEL); if (!iod) { @@ -133,7 +128,6 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, iod->format = io_t->format; iod->io_typ = io_t->io_type; iod->link_types = io_t->links; - iod->app = io_t->app; iod->net_typ = pdata->modem_net; iod->use_handover = pdata->use_handover; iod->ipc_version = pdata->ipc_version; @@ -145,7 +139,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, modemctl->iod = iod; if (iod->format == IPC_BOOT) { modemctl->bootd = iod; - mif_err("BOOT device = %s\n", iod->name); + mif_info("Bood device = %s\n", iod->name); } /* link between io device and modem shared */ @@ -166,7 +160,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, return NULL; } - mif_info("%s created\n", iod->name); + mif_debug("%s is created!!!\n", iod->name); return iod; } @@ -174,6 +168,7 @@ static int attach_devices(struct io_device *iod, enum modem_link tx_link) { struct modem_shared *msd = iod->msd; struct link_device *ld; + unsigned ch; /* find link type for this io device */ list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -237,36 +232,36 @@ static int __devinit modem_probe(struct platform_device *pdev) { int i; struct modem_data *pdata = pdev->dev.platform_data; - struct modem_shared *msd; - struct modem_ctl *modemctl; + struct modem_shared *msd = NULL; + struct modem_ctl *modemctl = NULL; struct io_device *iod[pdata->num_iodevs]; struct link_device *ld; - mif_err("%s: +++\n", pdata->name); + + mif_err("%s\n", pdev->name); + memset(iod, 0, sizeof(iod)); msd = create_modem_shared_data(); if (!msd) { - mif_err("%s: msd == NULL\n", pdata->name); - return -ENOMEM; + mif_err("msd == NULL\n"); + goto err_free_modemctl; } modemctl = create_modemctl_device(pdev, msd); if (!modemctl) { - mif_err("%s: modemctl == NULL\n", pdata->name); - kfree(msd); - return -ENOMEM; + mif_err("modemctl == NULL\n"); + goto err_free_modemctl; } /* create link device */ /* support multi-link device */ - memset(iod, 0, sizeof(iod)); for (i = 0; i < LINKDEV_MAX ; i++) { /* find matching link type */ if (pdata->link_types & LINKTYPE(i)) { ld = call_link_init_func(pdev, i); if (!ld) - goto free_mc; + goto err_free_modemctl; - mif_err("%s: %s link created\n", pdata->name, ld->name); + mif_err("link created: %s\n", ld->name); ld->link_type = i; ld->mc = modemctl; ld->msd = msd; @@ -279,8 +274,8 @@ static int __devinit modem_probe(struct platform_device *pdev) iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl, pdata); if (!iod[i]) { - mif_err("%s: iod[%d] == NULL\n", pdata->name, i); - goto free_iod; + mif_err("iod[%d] == NULL\n", i); + goto err_free_modemctl; } attach_devices(iod[i], pdata->iodevs[i].tx_link); @@ -288,23 +283,21 @@ static int __devinit modem_probe(struct platform_device *pdev) platform_set_drvdata(pdev, modemctl); - mif_err("%s: ---\n", pdata->name); + mif_err("Complete!!!\n"); + return 0; -free_iod: - for (i = 0; i < pdata->num_iodevs; i++) { - if (iod[i]) +err_free_modemctl: + for (i = 0; i < pdata->num_iodevs; i++) + if (iod[i] != NULL) kfree(iod[i]); - } -free_mc: - if (modemctl) + if (modemctl != NULL) kfree(modemctl); - if (msd) + if (msd != NULL) kfree(msd); - mif_err("%s: xxx\n", pdata->name); return -ENOMEM; } @@ -312,19 +305,13 @@ static void modem_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct modem_ctl *mc = dev_get_drvdata(dev); - struct utc_time utc; - mc->ops.modem_off(mc); mc->phone_state = STATE_OFFLINE; - - get_utc_time(&utc); - mif_info("%s: at [%02d:%02d:%02d.%03d]\n", - mc->name, utc.hour, utc.min, utc.sec, utc.msec); } static int modem_suspend(struct device *pdev) { -#if !defined(CONFIG_LINK_DEVICE_HSIC) +#ifndef CONFIG_LINK_DEVICE_HSIC struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -336,7 +323,7 @@ static int modem_suspend(struct device *pdev) static int modem_resume(struct device *pdev) { -#if !defined(CONFIG_LINK_DEVICE_HSIC) +#ifndef CONFIG_LINK_DEVICE_HSIC struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -347,8 +334,8 @@ static int modem_resume(struct device *pdev) } static const struct dev_pm_ops modem_pm_ops = { - .suspend = modem_suspend, - .resume = modem_resume, + .suspend = modem_suspend, + .resume = modem_resume, }; static struct platform_driver modem_driver = { @@ -356,7 +343,7 @@ static struct platform_driver modem_driver = { .shutdown = modem_shutdown, .driver = { .name = "mif_sipc5", - .pm = &modem_pm_ops, + .pm = &modem_pm_ops, }, }; diff --git a/include/linux/platform_data/modem.h b/include/linux/platform_data/modem.h old mode 100755 new mode 100644 index bc4433e..9598763 --- a/include/linux/platform_data/modem.h +++ b/include/linux/platform_data/modem.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -15,21 +16,14 @@ #ifndef __MODEM_IF_H__ #define __MODEM_IF_H__ -#include -#include - enum modem_t { IMC_XMM6260, IMC_XMM6262, VIA_CBP71, VIA_CBP72, - VIA_CBP82, - SEC_CMC220, SEC_CMC221, - SEC_SS222, QC_MDM6600, QC_ESC6270, - QC_QSC6085, SPRD_SC8803, DUMMY, MAX_MODEM_TYPE @@ -39,11 +33,10 @@ enum dev_format { IPC_FMT, IPC_RAW, IPC_RFS, - IPC_MULTI_RAW, IPC_CMD, IPC_BOOT, + IPC_MULTI_RAW, IPC_RAMDUMP, - IPC_DEBUG, MAX_DEV_FORMAT, }; #define MAX_IPC_DEV (IPC_RFS + 1) /* FMT, RAW, RFS */ @@ -58,14 +51,13 @@ enum modem_io { enum modem_link { LINKDEV_UNDEFINED, LINKDEV_MIPI, + LINKDEV_DPRAM, + LINKDEV_SPI, LINKDEV_USB, LINKDEV_HSIC, - LINKDEV_DPRAM, - LINKDEV_PLD, LINKDEV_C2C, - LINKDEV_SHMEM, - LINKDEV_SPI, - LINKDEV_MAX + LINKDEV_PLD, + LINKDEV_MAX, }; #define LINKTYPE(modem_link) (1u << (modem_link)) @@ -74,12 +66,6 @@ enum modem_network { CDMA_NETWORK, TDSCDMA_NETWORK, LTE_NETWORK, - MAX_MODEM_NETWORK -}; - -enum ap_type { - S5P, - MAX_AP_TYPE }; enum sipc_ver { @@ -88,16 +74,15 @@ enum sipc_ver { SIPC_VER_41 = 41, SIPC_VER_42 = 42, SIPC_VER_50 = 50, - MAX_SIPC_VER + MAX_SIPC_VER, }; /** * struct modem_io_t - declaration for io_device * @name: device name - * @id: for SIPC4, contains format & channel information + * @id: contain format & channel information * (id & 11100000b)>>5 = format (eg, 0=FMT, 1=RAW, 2=RFS) * (id & 00011111b) = channel (valid only if format is RAW) - * for SIPC5, contains only 8-bit channel ID * @format: device format * @io_type: type of this io_device * @links: list of link_devices to use this io_device @@ -109,7 +94,7 @@ enum sipc_ver { * TX is only one link_device. * @app: the name of the application that will use this IO device * - * This structure is used in board-*-modems.c + * This structure is used in board-*-modem.c */ struct modem_io_t { char *name; @@ -118,7 +103,11 @@ struct modem_io_t { enum modem_io io_type; enum modem_link links; enum modem_link tx_link; +#ifdef CONFIG_MACH_U1 char *app; +#else + bool rx_gather; +#endif }; struct modemlink_pm_data { @@ -137,14 +126,10 @@ struct modemlink_pm_data { void *hub_pm_data; bool has_usbhub; -#ifdef CONFIG_EXYNOS4_CPUFREQ /* cpu/bus frequency lock */ atomic_t freqlock; - atomic_t freq_dpramlock; int (*freq_lock)(struct device *dev); int (*freq_unlock)(struct device *dev); - unsigned gpio_cpufreq_lock; -#endif int autosuspend_delay_ms; /* if zero, the default value is used */ void (*ehci_reg_dump)(struct device *); @@ -155,25 +140,31 @@ struct modemlink_pm_link_activectl { int gpio_request_host_active; }; -#define RES_DPRAM_MEM_ID 0 -#define RES_DPRAM_SFR_ID 1 +#define RES_CP_ACTIVE_IRQ_ID 0 +#define RES_DPRAM_MEM_ID 1 +#define RES_DPRAM_IRQ_ID 2 +#define RES_DPRAM_SFR_ID 3 +#ifdef CONFIG_MACH_U1 +#define STR_CP_ACTIVE_IRQ "cp_active_irq" #define STR_DPRAM_BASE "dpram_base" +#define STR_DPRAM_IRQ "dpram_irq" #define STR_DPRAM_SFR_BASE "dpram_sfr_base" +#endif enum dpram_type { EXT_DPRAM, AP_IDPRAM, CP_IDPRAM, - PLD_DPRAM, + SHM_DPRAM, MAX_DPRAM_TYPE }; -#define DPRAM_SIZE_8KB (8 << 10) -#define DPRAM_SIZE_16KB (16 << 10) -#define DPRAM_SIZE_32KB (32 << 10) -#define DPRAM_SIZE_64KB (64 << 10) -#define DPRAM_SIZE_128KB (128 << 10) +#define DPRAM_SIZE_8KB 0x02000 +#define DPRAM_SIZE_16KB 0x04000 +#define DPRAM_SIZE_32KB 0x08000 +#define DPRAM_SIZE_64KB 0x10000 +#define DPRAM_SIZE_128KB 0x20000 enum dpram_speed { DPRAM_SPEED_LOW, @@ -202,16 +193,7 @@ struct dpram_ipc_device { }; struct dpram_ipc_map { - u16 __iomem *magic; - u16 __iomem *access; - - struct dpram_ipc_device dev[MAX_IPC_DEV]; - - u16 __iomem *mbx_cp2ap; - u16 __iomem *mbx_ap2cp; -}; - -struct pld_ipc_map { +#if defined(CONFIG_LINK_DEVICE_PLD) u16 __iomem *mbx_ap2cp; u16 __iomem *magic_ap2cp; u16 __iomem *access_ap2cp; @@ -223,52 +205,50 @@ struct pld_ipc_map { struct dpram_ipc_device dev[MAX_IPC_DEV]; u16 __iomem *address_buffer; -}; +#else + u16 __iomem *magic; + u16 __iomem *access; + + struct dpram_ipc_device dev[MAX_IPC_DEV]; -struct modemlink_dpram_data { - enum dpram_type type; /* DPRAM type */ - enum ap_type ap; /* AP type for AP_IDPRAM */ + u16 __iomem *mbx_cp2ap; + u16 __iomem *mbx_ap2cp; +#endif +}; - /* Stirct I/O access (e.g. ioread16(), etc.) is required */ - bool strict_io_access; +struct modemlink_dpram_control { + void (*reset)(void); + void (*clear_intr)(void); + u16 (*recv_intr)(void); + void (*send_intr)(u16); + u16 (*recv_msg)(void); + void (*send_msg)(u16); - /* Aligned access is required */ - int aligned; + int (*wakeup)(void); + void (*sleep)(void); - /* Disabled during phone booting */ - bool disabled; + void (*setup_speed)(enum dpram_speed); - /* Virtual base address and size */ - u8 __iomem *base; - u32 size; + enum dpram_type dp_type; /* DPRAM type */ + int aligned; /* aligned access is required */ +#ifdef CONFIG_MACH_U1 + bool disabled; /* Disabled during phone booting */ +#endif + u8 __iomem *dp_base; + u32 dp_size; - /* Pointer to an IPC map (DPRAM or PLD) */ - void *ipc_map; + int dpram_irq; + unsigned long dpram_irq_flags; - /* Timeout of waiting for RES_ACK from CP (in msec) */ - unsigned long res_ack_wait_timeout; + int max_ipc_dev; + struct dpram_ipc_map *ipc_map; unsigned boot_size_offset; unsigned boot_tag_offset; unsigned boot_count_offset; unsigned max_boot_frame_size; - - void (*setup_speed)(enum dpram_speed); - void (*clear_int2ap)(void); }; -enum shmem_type { - REAL_SHMEM, - C2C_SHMEM, - MAX_SHMEM_TYPE -}; - -#define STR_SHMEM_BASE "shmem_base" - -#define SHMEM_SIZE_1MB (1 << 20) /* 1 MB */ -#define SHMEM_SIZE_2MB (2 << 20) /* 2 MB */ -#define SHMEM_SIZE_4MB (4 << 20) /* 4 MB */ - /* platform data */ struct modem_data { char *name; @@ -277,45 +257,20 @@ struct modem_data { unsigned gpio_cp_off; unsigned gpio_reset_req_n; unsigned gpio_cp_reset; - - /* for broadcasting AP's PM state (active or sleep) */ unsigned gpio_pda_active; - - /* for checking aliveness of CP */ unsigned gpio_phone_active; - int irq_phone_active; - - /* for AP-CP IPC interrupt */ - unsigned gpio_ipc_int2ap; - int irq_ipc_int2ap; - unsigned long irqf_ipc_int2ap; /* IRQ flags */ - unsigned gpio_ipc_int2cp; - - /* for AP-CP power management (PM) handshaking */ - unsigned gpio_ap_wakeup; - int irq_ap_wakeup; - unsigned gpio_ap_status; - unsigned gpio_cp_wakeup; - unsigned gpio_cp_status; - int irq_cp_status; - - /* for USB/HSIC PM */ - unsigned gpio_host_wakeup; - int irq_host_wakeup; - unsigned gpio_host_active; - unsigned gpio_slave_wakeup; - unsigned gpio_hub_suspend; - unsigned gpio_cp_dump_int; unsigned gpio_ap_dump_int; unsigned gpio_flm_uart_sel; - unsigned gpio_cp_warm_reset; #if defined(CONFIG_MACH_M0_CTC) unsigned gpio_flm_uart_sel_rev06; + unsigned gpio_host_wakeup; #endif - + unsigned gpio_cp_warm_reset; unsigned gpio_sim_detect; - int irq_sim_detect; +#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD) + unsigned gpio_dpram_int; +#endif #ifdef CONFIG_LINK_DEVICE_PLD unsigned gpio_fpga1_creset; @@ -329,6 +284,14 @@ struct modem_data { unsigned gpio_fpga2_cs_n; #endif +#ifdef CONFIG_LTE_MODEM_CMC221 + unsigned gpio_dpram_status; + unsigned gpio_dpram_wakeup; + unsigned gpio_slave_wakeup; + unsigned gpio_host_active; + unsigned gpio_host_wakeup; + int irq_host_wakeup; +#endif #ifdef CONFIG_MACH_U1_KOR_LGT unsigned gpio_cp_reset_msm; unsigned gpio_boot_sw_sel; @@ -353,74 +316,41 @@ struct modem_data { #endif /* Switch with 2 links in a modem */ - unsigned gpio_link_switch; - -#ifdef CONFIG_EXYNOS4_CPUFREQ - /* cpu/bus frequency lock */ - unsigned gpio_cpufreq_lock; -#endif + unsigned gpio_dynamic_switching; /* Modem component */ - enum modem_network modem_net; - enum modem_t modem_type; - enum modem_link link_types; - char *link_name; - + enum modem_network modem_net; + enum modem_t modem_type; + enum modem_link link_types; + char *link_name; +#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD) /* Link to DPRAM control functions dependent on each platform */ - struct modemlink_dpram_data *dpram; + struct modemlink_dpram_control *dpram_ctl; +#endif /* SIPC version */ enum sipc_ver ipc_version; - /* the number of real IPC devices -> (IPC_RAW + 1) or (IPC_RFS + 1) */ - int max_ipc_dev; - /* Information of IO devices */ - unsigned num_iodevs; - struct modem_io_t *iodevs; + unsigned num_iodevs; + struct modem_io_t *iodevs; /* Modem link PM support */ struct modemlink_pm_data *link_pm_data; + void (*gpio_revers_bias_clear)(void); + void (*gpio_revers_bias_restore)(void); + /* Handover with 2+ modems */ bool use_handover; + /* Debugging option */ + bool use_mif_log; /* SIM Detect polarity */ bool sim_polarity; - - void (*gpio_revers_bias_clear)(void); - void (*gpio_revers_bias_restore)(void); }; -#define MODEM_BOOT_DEV_SPI "modem_boot_spi" - -struct modem_boot_spi_platform_data { - const char *name; - unsigned int gpio_cp_status; -}; - -struct modem_boot_spi { - struct miscdevice dev; - struct spi_device *spi_dev; - struct mutex lock; - unsigned gpio_cp_status; -}; -#define to_modem_boot_spi(misc) container_of(misc, struct modem_boot_spi, dev); - -struct utc_time { - u16 year; - u8 mon:4, - day:4; - u8 hour; - u8 min; - u8 sec; - u16 msec; -} __packed; - -extern void get_utc_time(struct utc_time *utc); - -#define LOG_TAG "mif: " -#define CALLER (__builtin_return_address(0)) +#define LOG_TAG "mif: " #define mif_err(fmt, ...) \ pr_err(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) -- cgit v1.1 From 462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3 Mon Sep 17 00:00:00 2001 From: RGIB Date: Fri, 27 May 2016 15:41:13 +0200 Subject: smdk4412 : modem_if KK driver from N5100ZTCNL4 Change-Id: I7d1e6412700b5db293448aca99c53ac4a52fc1a8 --- drivers/misc/modem_if/Kconfig | 21 + drivers/misc/modem_if/Makefile | 32 +- drivers/misc/modem_if/lte_modem_bootloader.c | 2 +- drivers/misc/modem_if/modem.h | 435 +++ drivers/misc/modem_if/modem_boot_device_spi.c | 279 ++ drivers/misc/modem_if/modem_link_device_c2c.c | 2283 +++++++++++++- drivers/misc/modem_if/modem_link_device_c2c.h | 202 +- drivers/misc/modem_if/modem_link_device_dpram.c | 3288 +++++++++++++------- drivers/misc/modem_if/modem_link_device_dpram.h | 455 ++- .../misc/modem_if/modem_link_device_dpram_ext_op.c | 1619 +++++++--- drivers/misc/modem_if/modem_link_device_hsic.c | 33 +- drivers/misc/modem_if/modem_link_device_memory.c | 496 +++ drivers/misc/modem_if/modem_link_device_memory.h | 409 +++ drivers/misc/modem_if/modem_link_device_mipi.c | 2 +- drivers/misc/modem_if/modem_link_device_pld.c | 1464 ++++----- drivers/misc/modem_if/modem_link_device_pld.h | 578 ++-- .../misc/modem_if/modem_link_device_pld_ext_op.c | 277 +- drivers/misc/modem_if/modem_link_device_shmem.h | 700 +++++ drivers/misc/modem_if/modem_link_device_spi.c | 246 +- drivers/misc/modem_if/modem_link_device_spi.h | 24 +- drivers/misc/modem_if/modem_link_device_usb.c | 16 +- drivers/misc/modem_if/modem_link_pm_usb.c | 29 +- drivers/misc/modem_if/modem_link_pm_usb.h | 2 +- .../misc/modem_if/modem_modemctl_device_cbp71.c | 2 +- .../misc/modem_if/modem_modemctl_device_cbp72.c | 45 +- .../misc/modem_if/modem_modemctl_device_cbp82.c | 288 ++ .../misc/modem_if/modem_modemctl_device_cmc221.c | 105 +- .../misc/modem_if/modem_modemctl_device_esc6270.c | 18 +- .../misc/modem_if/modem_modemctl_device_mdm6600.c | 43 +- .../misc/modem_if/modem_modemctl_device_qsc6085.c | 218 ++ .../misc/modem_if/modem_modemctl_device_sprd8803.c | 60 +- .../misc/modem_if/modem_modemctl_device_ss222.c | 312 ++ .../misc/modem_if/modem_modemctl_device_xmm6260.c | 12 +- .../misc/modem_if/modem_modemctl_device_xmm6262.c | 92 +- .../misc/modem_if/modem_net_flowcontrol_device.c | 2 +- drivers/misc/modem_if/modem_prj.h | 514 +-- drivers/misc/modem_if/modem_sim_slot_switch.c | 11 +- drivers/misc/modem_if/modem_utils.c | 671 ++-- drivers/misc/modem_if/modem_utils.h | 56 +- drivers/misc/modem_if/modem_variation.h | 106 +- drivers/misc/modem_if/sipc4_io_device.c | 311 +- drivers/misc/modem_if/sipc4_modem.c | 15 +- drivers/misc/modem_if/sipc5_common.c | 239 ++ drivers/misc/modem_if/sipc5_io_device.c | 1561 +++++----- drivers/misc/modem_if/sipc5_modem.c | 87 +- 45 files changed, 12492 insertions(+), 5168 deletions(-) create mode 100644 drivers/misc/modem_if/modem.h create mode 100644 drivers/misc/modem_if/modem_boot_device_spi.c create mode 100644 drivers/misc/modem_if/modem_link_device_memory.c create mode 100644 drivers/misc/modem_if/modem_link_device_memory.h create mode 100644 drivers/misc/modem_if/modem_link_device_shmem.h create mode 100644 drivers/misc/modem_if/modem_modemctl_device_cbp82.c create mode 100644 drivers/misc/modem_if/modem_modemctl_device_qsc6085.c create mode 100644 drivers/misc/modem_if/modem_modemctl_device_ss222.c create mode 100644 drivers/misc/modem_if/sipc5_common.c diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig index 6a6eeab..a425439 100644 --- a/drivers/misc/modem_if/Kconfig +++ b/drivers/misc/modem_if/Kconfig @@ -24,11 +24,21 @@ config CDMA_MODEM_CBP72 depends on SEC_MODEM default n +config CDMA_MODEM_CBP82 + bool "modem chip : VIA CBP8.2" + depends on SEC_MODEM + default n + config LTE_MODEM_CMC221 bool "modem chip : SEC CMC221" depends on SEC_MODEM default n +config UMTS_MODEM_SS222 + bool "modem chip : SEC SS222" + depends on SEC_MODEM + default n + config CDMA_MODEM_MDM6600 bool "modem chip : QC MDM6600" depends on SEC_MODEM @@ -44,6 +54,11 @@ config GSM_MODEM_ESC6270 depends on SEC_MODEM default n +config CDMA_MODEM_QSC6085 + bool "modem chip : Qualcomm QSC6085" + depends on SEC_MODEM + default n + config LINK_DEVICE_MIPI bool "modem driver link device MIPI-HSI" depends on SEC_MODEM @@ -58,6 +73,7 @@ config LINK_DEVICE_PLD bool "modem driver link device PLD" depends on SEC_MODEM default n + config LINK_DEVICE_USB bool "modem driver link device USB" depends on SEC_MODEM @@ -78,6 +94,11 @@ config LINK_DEVICE_SPI depends on SEC_MODEM default n +config BOOT_DEVICE_SPI + bool "modem driver boot device SPI" + depends on SEC_MODEM + default n + config WORKQUEUE_FRONT bool "IPC: SPI workqueue front" depends on SEC_MODEM diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile index 5bd62ea..fbb00af 100644 --- a/drivers/misc/modem_if/Makefile +++ b/drivers/misc/modem_if/Makefile @@ -2,7 +2,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if -obj-y += sipc5_modem.o sipc5_io_device.o +obj-y += sipc5_modem.o sipc5_io_device.o sipc5_common.o obj-y += sipc4_modem.o sipc4_io_device.o obj-y += modem_net_flowcontrol_device.o modem_utils.o @@ -10,17 +10,43 @@ obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o +obj-$(CONFIG_CDMA_MODEM_CBP82) += modem_modemctl_device_cbp82.o obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o +obj-$(CONFIG_UMTS_MODEM_SS222) += modem_modemctl_device_ss222.o obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o +obj-$(CONFIG_CDMA_MODEM_QSC6085) += modem_modemctl_device_qsc6085.o obj-$(CONFIG_TDSCDMA_MODEM_SPRD8803) += modem_modemctl_device_sprd8803.o obj-$(CONFIG_LINK_DEVICE_MIPI) += modem_link_device_mipi.o -obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o -obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o +obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o +obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o obj-$(CONFIG_LINK_DEVICE_C2C) += modem_link_device_c2c.o obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o +obj-$(CONFIG_BOOT_DEVICE_SPI) += modem_boot_device_spi.o + obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o + +# Check whether or not memory-type interface +ifeq ($(CONFIG_LINK_DEVICE_DPRAM),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_PLD),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_C2C),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_SHMEM),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifdef LINK_DEVICE_MEMORY_INTERFACE +obj-y += modem_link_device_memory.o +endif diff --git a/drivers/misc/modem_if/lte_modem_bootloader.c b/drivers/misc/modem_if/lte_modem_bootloader.c index f259aae..0bc8894 100644 --- a/drivers/misc/modem_if/lte_modem_bootloader.c +++ b/drivers/misc/modem_if/lte_modem_bootloader.c @@ -31,7 +31,7 @@ #include #include -#include +#include "modem.h" #include #define LEN_XMIT_DELEY 100 diff --git a/drivers/misc/modem_if/modem.h b/drivers/misc/modem_if/modem.h new file mode 100644 index 0000000..bc4433e --- /dev/null +++ b/drivers/misc/modem_if/modem.h @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 __MODEM_IF_H__ +#define __MODEM_IF_H__ + +#include +#include + +enum modem_t { + IMC_XMM6260, + IMC_XMM6262, + VIA_CBP71, + VIA_CBP72, + VIA_CBP82, + SEC_CMC220, + SEC_CMC221, + SEC_SS222, + QC_MDM6600, + QC_ESC6270, + QC_QSC6085, + SPRD_SC8803, + DUMMY, + MAX_MODEM_TYPE +}; + +enum dev_format { + IPC_FMT, + IPC_RAW, + IPC_RFS, + IPC_MULTI_RAW, + IPC_CMD, + IPC_BOOT, + IPC_RAMDUMP, + IPC_DEBUG, + MAX_DEV_FORMAT, +}; +#define MAX_IPC_DEV (IPC_RFS + 1) /* FMT, RAW, RFS */ +#define MAX_SIPC5_DEV (IPC_RAW + 1) /* FMT, RAW */ + +enum modem_io { + IODEV_MISC, + IODEV_NET, + IODEV_DUMMY, +}; + +enum modem_link { + LINKDEV_UNDEFINED, + LINKDEV_MIPI, + LINKDEV_USB, + LINKDEV_HSIC, + LINKDEV_DPRAM, + LINKDEV_PLD, + LINKDEV_C2C, + LINKDEV_SHMEM, + LINKDEV_SPI, + LINKDEV_MAX +}; +#define LINKTYPE(modem_link) (1u << (modem_link)) + +enum modem_network { + UMTS_NETWORK, + CDMA_NETWORK, + TDSCDMA_NETWORK, + LTE_NETWORK, + MAX_MODEM_NETWORK +}; + +enum ap_type { + S5P, + MAX_AP_TYPE +}; + +enum sipc_ver { + NO_SIPC_VER = 0, + SIPC_VER_40 = 40, + SIPC_VER_41 = 41, + SIPC_VER_42 = 42, + SIPC_VER_50 = 50, + MAX_SIPC_VER +}; + +/** + * struct modem_io_t - declaration for io_device + * @name: device name + * @id: for SIPC4, contains format & channel information + * (id & 11100000b)>>5 = format (eg, 0=FMT, 1=RAW, 2=RFS) + * (id & 00011111b) = channel (valid only if format is RAW) + * for SIPC5, contains only 8-bit channel ID + * @format: device format + * @io_type: type of this io_device + * @links: list of link_devices to use this io_device + * for example, if you want to use DPRAM and USB in an io_device. + * .links = LINKTYPE(LINKDEV_DPRAM) | LINKTYPE(LINKDEV_USB) + * @tx_link: when you use 2+ link_devices, set the link for TX. + * If define multiple link_devices in @links, + * you can receive data from them. But, cannot send data to all. + * TX is only one link_device. + * @app: the name of the application that will use this IO device + * + * This structure is used in board-*-modems.c + */ +struct modem_io_t { + char *name; + int id; + enum dev_format format; + enum modem_io io_type; + enum modem_link links; + enum modem_link tx_link; + char *app; +}; + +struct modemlink_pm_data { + char *name; + /* link power contol 2 types : pin & regulator control */ + int (*link_ldo_enable)(bool); + unsigned gpio_link_enable; + unsigned gpio_link_active; + unsigned gpio_link_hostwake; + unsigned gpio_link_slavewake; + int (*link_reconnect)(void); + + /* usb hub only */ + int (*port_enable)(int, int); + int (*hub_standby)(void *); + void *hub_pm_data; + bool has_usbhub; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + atomic_t freqlock; + atomic_t freq_dpramlock; + int (*freq_lock)(struct device *dev); + int (*freq_unlock)(struct device *dev); + unsigned gpio_cpufreq_lock; +#endif + + int autosuspend_delay_ms; /* if zero, the default value is used */ + void (*ehci_reg_dump)(struct device *); +}; + +struct modemlink_pm_link_activectl { + int gpio_initialized; + int gpio_request_host_active; +}; + +#define RES_DPRAM_MEM_ID 0 +#define RES_DPRAM_SFR_ID 1 + +#define STR_DPRAM_BASE "dpram_base" +#define STR_DPRAM_SFR_BASE "dpram_sfr_base" + +enum dpram_type { + EXT_DPRAM, + AP_IDPRAM, + CP_IDPRAM, + PLD_DPRAM, + MAX_DPRAM_TYPE +}; + +#define DPRAM_SIZE_8KB (8 << 10) +#define DPRAM_SIZE_16KB (16 << 10) +#define DPRAM_SIZE_32KB (32 << 10) +#define DPRAM_SIZE_64KB (64 << 10) +#define DPRAM_SIZE_128KB (128 << 10) + +enum dpram_speed { + DPRAM_SPEED_LOW, + DPRAM_SPEED_MID, + DPRAM_SPEED_HIGH, + MAX_DPRAM_SPEED +}; + +struct dpram_circ { + u16 __iomem *head; + u16 __iomem *tail; + u8 __iomem *buff; + u32 size; +}; + +struct dpram_ipc_device { + char name[16]; + int id; + + struct dpram_circ txq; + struct dpram_circ rxq; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; +}; + +struct dpram_ipc_map { + u16 __iomem *magic; + u16 __iomem *access; + + struct dpram_ipc_device dev[MAX_IPC_DEV]; + + u16 __iomem *mbx_cp2ap; + u16 __iomem *mbx_ap2cp; +}; + +struct pld_ipc_map { + u16 __iomem *mbx_ap2cp; + u16 __iomem *magic_ap2cp; + u16 __iomem *access_ap2cp; + + u16 __iomem *mbx_cp2ap; + u16 __iomem *magic_cp2ap; + u16 __iomem *access_cp2ap; + + struct dpram_ipc_device dev[MAX_IPC_DEV]; + + u16 __iomem *address_buffer; +}; + +struct modemlink_dpram_data { + enum dpram_type type; /* DPRAM type */ + enum ap_type ap; /* AP type for AP_IDPRAM */ + + /* Stirct I/O access (e.g. ioread16(), etc.) is required */ + bool strict_io_access; + + /* Aligned access is required */ + int aligned; + + /* Disabled during phone booting */ + bool disabled; + + /* Virtual base address and size */ + u8 __iomem *base; + u32 size; + + /* Pointer to an IPC map (DPRAM or PLD) */ + void *ipc_map; + + /* Timeout of waiting for RES_ACK from CP (in msec) */ + unsigned long res_ack_wait_timeout; + + unsigned boot_size_offset; + unsigned boot_tag_offset; + unsigned boot_count_offset; + unsigned max_boot_frame_size; + + void (*setup_speed)(enum dpram_speed); + void (*clear_int2ap)(void); +}; + +enum shmem_type { + REAL_SHMEM, + C2C_SHMEM, + MAX_SHMEM_TYPE +}; + +#define STR_SHMEM_BASE "shmem_base" + +#define SHMEM_SIZE_1MB (1 << 20) /* 1 MB */ +#define SHMEM_SIZE_2MB (2 << 20) /* 2 MB */ +#define SHMEM_SIZE_4MB (4 << 20) /* 4 MB */ + +/* platform data */ +struct modem_data { + char *name; + + unsigned gpio_cp_on; + unsigned gpio_cp_off; + unsigned gpio_reset_req_n; + unsigned gpio_cp_reset; + + /* for broadcasting AP's PM state (active or sleep) */ + unsigned gpio_pda_active; + + /* for checking aliveness of CP */ + unsigned gpio_phone_active; + int irq_phone_active; + + /* for AP-CP IPC interrupt */ + unsigned gpio_ipc_int2ap; + int irq_ipc_int2ap; + unsigned long irqf_ipc_int2ap; /* IRQ flags */ + unsigned gpio_ipc_int2cp; + + /* for AP-CP power management (PM) handshaking */ + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* for USB/HSIC PM */ + unsigned gpio_host_wakeup; + int irq_host_wakeup; + unsigned gpio_host_active; + unsigned gpio_slave_wakeup; + unsigned gpio_hub_suspend; + + unsigned gpio_cp_dump_int; + unsigned gpio_ap_dump_int; + unsigned gpio_flm_uart_sel; + unsigned gpio_cp_warm_reset; +#if defined(CONFIG_MACH_M0_CTC) + unsigned gpio_flm_uart_sel_rev06; +#endif + + unsigned gpio_sim_detect; + int irq_sim_detect; + +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga1_creset; + unsigned gpio_fpga1_cdone; + unsigned gpio_fpga1_rst_n; + unsigned gpio_fpga1_cs_n; + + unsigned gpio_fpga2_creset; + unsigned gpio_fpga2_cdone; + unsigned gpio_fpga2_rst_n; + unsigned gpio_fpga2_cs_n; +#endif + +#ifdef CONFIG_MACH_U1_KOR_LGT + unsigned gpio_cp_reset_msm; + unsigned gpio_boot_sw_sel; + void (*vbus_on)(void); + void (*vbus_off)(void); + struct regulator *cp_vbus; +#endif + +#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 + unsigned gpio_ipc_mrdy; + unsigned gpio_ipc_srdy; + unsigned gpio_ipc_sub_mrdy; + unsigned gpio_ipc_sub_srdy; + unsigned gpio_ap_cp_int1; + unsigned gpio_ap_cp_int2; +#endif + +#ifdef CONFIG_SEC_DUAL_MODEM_MODE + unsigned gpio_sim_io_sel; + unsigned gpio_cp_ctrl1; + unsigned gpio_cp_ctrl2; +#endif + + /* Switch with 2 links in a modem */ + unsigned gpio_link_switch; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + unsigned gpio_cpufreq_lock; +#endif + + /* Modem component */ + enum modem_network modem_net; + enum modem_t modem_type; + enum modem_link link_types; + char *link_name; + + /* Link to DPRAM control functions dependent on each platform */ + struct modemlink_dpram_data *dpram; + + /* SIPC version */ + enum sipc_ver ipc_version; + + /* the number of real IPC devices -> (IPC_RAW + 1) or (IPC_RFS + 1) */ + int max_ipc_dev; + + /* Information of IO devices */ + unsigned num_iodevs; + struct modem_io_t *iodevs; + + /* Modem link PM support */ + struct modemlink_pm_data *link_pm_data; + + /* Handover with 2+ modems */ + bool use_handover; + + /* SIM Detect polarity */ + bool sim_polarity; + + void (*gpio_revers_bias_clear)(void); + void (*gpio_revers_bias_restore)(void); +}; + +#define MODEM_BOOT_DEV_SPI "modem_boot_spi" + +struct modem_boot_spi_platform_data { + const char *name; + unsigned int gpio_cp_status; +}; + +struct modem_boot_spi { + struct miscdevice dev; + struct spi_device *spi_dev; + struct mutex lock; + unsigned gpio_cp_status; +}; +#define to_modem_boot_spi(misc) container_of(misc, struct modem_boot_spi, dev); + +struct utc_time { + u16 year; + u8 mon:4, + day:4; + u8 hour; + u8 min; + u8 sec; + u16 msec; +} __packed; + +extern void get_utc_time(struct utc_time *utc); + +#define LOG_TAG "mif: " +#define CALLER (__builtin_return_address(0)) + +#define mif_err(fmt, ...) \ + pr_err(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) +#define mif_debug(fmt, ...) \ + pr_debug(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) +#define mif_info(fmt, ...) \ + pr_info(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) +#define mif_trace(fmt, ...) \ + printk(KERN_DEBUG "mif: %s: %d: called(%pF): " fmt, \ + __func__, __LINE__, __builtin_return_address(0), ##__VA_ARGS__) + +#endif diff --git a/drivers/misc/modem_if/modem_boot_device_spi.c b/drivers/misc/modem_if/modem_boot_device_spi.c new file mode 100644 index 0000000..369e473 --- /dev/null +++ b/drivers/misc/modem_if/modem_boot_device_spi.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2011 Samsung Electronics. + * + * 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 "modem.h" +#include "modem_prj.h" + +#define SPI_XMIT_DELEY 100 + +static int check_cp_status(unsigned int gpio_cp_status, unsigned int count) +{ + int ret = 0; + int cnt = 0; + + while (1) { + if (gpio_get_value(gpio_cp_status) != 0) { + ret = 0; + break; + } + + cnt++; + if (cnt >= count) { + mif_err("ERR! gpio_cp_status == 0 (cnt %d)\n", cnt); + ret = -EFAULT; + break; + } + + msleep(20); + } + + return ret; +} + +static inline int spi_send(struct modem_boot_spi *loader, const char val) +{ + char buff[1]; + int ret; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .tx_buf = buff, + }; + + buff[0] = val; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(loader->spi_dev, &msg); + if (ret < 0) + mif_err("ERR! spi_sync fail (err %d)\n", ret); + + return ret; +} + +static int spi_boot_write(struct modem_boot_spi *loader, const char *addr, + const long len) +{ + int i; + int ret = 0; + char *buff = NULL; + mif_err("+++\n"); + + buff = kzalloc(len, GFP_KERNEL); + if (!buff) { + mif_err("ERR! kzalloc(%ld) fail\n", len); + ret = -ENOMEM; + goto exit; + } + + ret = copy_from_user(buff, (const void __user *)addr, len); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit; + } + + for (i = 0 ; i < len ; i++) { + ret = spi_send(loader, buff[i]); + if (ret < 0) { + mif_err("ERR! spi_send fail (err %d)\n", ret); + goto exit; + } + } + +exit: + if (buff) + kfree(buff); + + mif_err("---\n"); + return ret; +} + +static int spi_boot_open(struct inode *inode, struct file *filp) +{ + struct modem_boot_spi *loader = to_modem_boot_spi(filp->private_data); + filp->private_data = loader; + return 0; +} + +static long spi_boot_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct modem_firmware img; + struct modem_boot_spi *loader = filp->private_data; + + mutex_lock(&loader->lock); + switch (cmd) { + case IOCTL_MODEM_XMIT_BOOT: + ret = copy_from_user(&img, (const void __user *)arg, + sizeof(struct modem_firmware)); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit_err; + } + mif_info("IOCTL_MODEM_XMIT_BOOT (size %d)\n", img.size); + + ret = spi_boot_write(loader, img.binary, img.size); + if (ret < 0) { + mif_err("ERR! spi_boot_write fail (err %d)\n", ret); + break; + } + + if (!loader->gpio_cp_status) + break; + + ret = check_cp_status(loader->gpio_cp_status, 100); + if (ret < 0) + mif_err("ERR! check_cp_status fail (err %d)\n", ret); + + break; + + default: + mif_err("ioctl cmd error\n"); + ret = -ENOIOCTLCMD; + + break; + } + mutex_unlock(&loader->lock); + +exit_err: + return ret; +} + +static const struct file_operations modem_spi_boot_fops = { + .owner = THIS_MODULE, + .open = spi_boot_open, + .unlocked_ioctl = spi_boot_ioctl, +}; + +static int __devinit modem_spi_boot_probe(struct spi_device *spi) +{ + int ret; + struct modem_boot_spi *loader; + struct modem_boot_spi_platform_data *pdata; + mif_debug("+++\n"); + + loader = kzalloc(sizeof(*loader), GFP_KERNEL); + if (!loader) { + mif_err("failed to allocate for modem_boot_spi\n"); + ret = -ENOMEM; + goto err_alloc; + } + mutex_init(&loader->lock); + + spi->bits_per_word = 8; + + if (spi_setup(spi)) { + mif_err("ERR! spi_setup fail\n"); + ret = -EINVAL; + goto err_setup; + } + loader->spi_dev = spi; + + if (!spi->dev.platform_data) { + mif_err("ERR! no platform_data\n"); + ret = -EINVAL; + goto err_setup; + } + pdata = (struct modem_boot_spi_platform_data *)spi->dev.platform_data; + + loader->gpio_cp_status = pdata->gpio_cp_status; + + spi_set_drvdata(spi, loader); + + loader->dev.minor = MISC_DYNAMIC_MINOR; + loader->dev.name = MODEM_BOOT_DEV_SPI; + loader->dev.fops = &modem_spi_boot_fops; + ret = misc_register(&loader->dev); + if (ret) { + mif_err("ERR! misc_register fail (err %d)\n", ret); + goto err_setup; + } + + mif_debug("---\n"); + return 0; + +err_setup: + mutex_destroy(&loader->lock); + kfree(loader); + +err_alloc: + mif_err("xxx\n"); + return ret; +} + +static int __devexit modem_spi_boot_remove(struct spi_device *spi) +{ + struct modem_boot_spi *loader = spi_get_drvdata(spi); + + misc_deregister(&loader->dev); + mutex_destroy(&loader->lock); + kfree(loader); + + return 0; +} + +static struct spi_driver modem_boot_device_spi_driver = { + .driver = { + .name = MODEM_BOOT_DEV_SPI, + .owner = THIS_MODULE, + }, + .probe = modem_spi_boot_probe, + .remove = __devexit_p(modem_spi_boot_remove), +}; + +static int __init modem_boot_device_spi_init(void) +{ + int err; + + err = spi_register_driver(&modem_boot_device_spi_driver); + if (err) { + mif_err("spi_register_driver fail (err %d)\n", err); + return err; + } + + return 0; +} + +static void __exit modem_boot_device_spi_exit(void) +{ + spi_unregister_driver(&modem_boot_device_spi_driver); +} + +module_init(modem_boot_device_spi_init); +module_exit(modem_boot_device_spi_exit); + +MODULE_DESCRIPTION("SPI Driver for Downloading Modem Bootloader"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/modem_if/modem_link_device_c2c.c b/drivers/misc/modem_if/modem_link_device_c2c.c index acbaadf..b51cf9e 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.c +++ b/drivers/misc/modem_if/modem_link_device_c2c.c @@ -1,6 +1,4 @@ -/* /linux/drivers/new_modem_if/link_dev_c2c.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -14,9 +12,9 @@ * */ -#include #include #include +#include #include #include #include @@ -24,38 +22,2279 @@ #include #include #include -#include #include #include +#include +#include +#ifdef CONFIG_FB +#include +#endif +#include +#include +#include +#include -#include -#include +#include "modem.h" #include "modem_prj.h" +#include "modem_utils.h" #include "modem_link_device_c2c.h" -struct link_device *c2c_create_link_device(struct platform_device *pdev) +static void trigger_forced_cp_crash(struct shmem_link_device *shmd); + +#ifdef DEBUG_MODEM_IF +static void save_mem_dump(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + char *path = shmd->dump_path; + struct file *fp; + struct utc_time t; + + get_utc_time(&t); + snprintf(path, MIF_MAX_PATH_LEN, "%s/%s_%d%02d%02d_%02d%02d%02d.dump", + MIF_LOG_DIR, ld->name, t.year, t.mon, t.day, t.hour, t.min, + t.sec); + + fp = mif_open_file(path); + if (!fp) { + mif_err("%s: ERR! %s open fail\n", ld->name, path); + return; + } + mif_err("%s: %s opened\n", ld->name, path); + + mif_save_file(fp, shmd->base, shmd->size); + + mif_close_file(fp); +} + +/** + * mem_dump_work + * @ws: pointer to an instance of work_struct structure + * + * Performs actual file operation for saving a DPRAM dump. + */ +static void mem_dump_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + + shmd = container_of(ws, struct shmem_link_device, dump_dwork.work); + if (!shmd) { + mif_err("ERR! no shmd\n"); + return; + } + + save_mem_dump(shmd); +} +#endif + +static void print_pm_status(struct shmem_link_device *shmd, int level) +{ +#ifdef DEBUG_MODEM_IF + struct utc_time t; + u32 magic; + int ap_wakeup; + int ap_status; + int cp_wakeup; + int cp_status; + + if (level < 0) + return; + + get_utc_time(&t); + magic = get_magic(shmd); + ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + ap_status = gpio_get_value(shmd->gpio_ap_status); + cp_wakeup = gpio_get_value(shmd->gpio_cp_wakeup); + cp_status = gpio_get_value(shmd->gpio_cp_status); + + /* + ** PM {ap_wakeup:cp_wakeup:cp_status:ap_status:magic:state} CALLER + */ + if (level > 0) { + pr_err(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " + "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, + cp_wakeup, cp_status, ap_status, magic, CALLER); + } else { + pr_info(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " + "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, + cp_wakeup, cp_status, ap_status, magic, CALLER); + } +#endif +} + +static inline void s5p_change_irq_type(int irq, int value) +{ + irq_set_irq_type(irq, value ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH); + /** + * Exynos BSP has a problem when using level-triggering interrupt. + * If the irq type is changed in an interrupt handler, the handler will + * be called again. + * Below is a temporary solution until SYS.LSI resolves this problem. + */ + __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); +} + +/** + * recv_int2ap + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the CP-to-AP interrupt register. + */ +static inline u16 recv_int2ap(struct shmem_link_device *shmd) +{ + if (shmd->type == C2C_SHMEM) + return (u16)c2c_read_interrupt(); + + if (shmd->mbx2ap) + return *shmd->mbx2ap; + + return 0; +} + +/** + * send_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * @mask: value to be written to the AP-to-CP interrupt register + */ +static inline void send_int2cp(struct shmem_link_device *shmd, u16 mask) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode != LINK_MODE_IPC) + mif_info("%s: mask = 0x%04X\n", ld->name, CALLER, mask); + + if (shmd->type == C2C_SHMEM) + c2c_send_interrupt(mask); + + if (shmd->mbx2cp) + *shmd->mbx2cp = mask; +} + +/** + * get_shmem_status + * @shmd: pointer to an instance of shmem_link_device structure + * @dir: direction of communication (TX or RX) + * @mst: pointer to an instance of mem_status structure + * + * Takes a snapshot of the current status of a SHMEM. + */ +static void get_shmem_status(struct shmem_link_device *shmd, + enum circ_dir_type dir, struct mem_status *mst) +{ +#ifdef DEBUG_MODEM_IF + getnstimeofday(&mst->ts); +#endif + + mst->dir = dir; + mst->magic = get_magic(shmd); + mst->access = get_access(shmd); + mst->head[IPC_FMT][TX] = get_txq_head(shmd, IPC_FMT); + mst->tail[IPC_FMT][TX] = get_txq_tail(shmd, IPC_FMT); + mst->head[IPC_FMT][RX] = get_rxq_head(shmd, IPC_FMT); + mst->tail[IPC_FMT][RX] = get_rxq_tail(shmd, IPC_FMT); + mst->head[IPC_RAW][TX] = get_txq_head(shmd, IPC_RAW); + mst->tail[IPC_RAW][TX] = get_txq_tail(shmd, IPC_RAW); + mst->head[IPC_RAW][RX] = get_rxq_head(shmd, IPC_RAW); + mst->tail[IPC_RAW][RX] = get_rxq_tail(shmd, IPC_RAW); +} + +static inline int check_link_status(struct shmem_link_device *shmd) +{ + u32 magic = get_magic(shmd); + int cnt; + + if (gpio_get_value(shmd->gpio_cp_status) != 0 && magic == SHM_IPC_MAGIC) + return 0; + + cnt = 0; + while (gpio_get_value(shmd->gpio_cp_status) == 0) { + if (gpio_get_value(shmd->gpio_ap_status) == 0) { + print_pm_status(shmd, 1); + gpio_set_value(shmd->gpio_ap_status, 1); + } + + cnt++; + if (cnt >= 100) { + mif_err("ERR! cp_status != 1 (cnt %d)\n", cnt); + return -EACCES; + } + + if (in_interrupt()) + udelay(100); + else + usleep_range(100, 200); + } + + cnt = 0; + while (1) { + magic = get_magic(shmd); + if (magic == SHM_IPC_MAGIC) + break; + + cnt++; + if (cnt >= 100) { + mif_err("ERR! magic 0x%X != SHM_IPC_MAGIC (cnt %d)\n", + magic, cnt); + return -EACCES; + } + + if (in_interrupt()) + udelay(100); + else + usleep_range(100, 200); + } + + return 0; +} + +static void release_cp_wakeup(struct work_struct *ws) { - struct c2c_link_device *dpld; + struct shmem_link_device *shmd; struct link_device *ld; - struct modem_data *pdata; + int i; + unsigned long flags; - pdata = pdev->dev.platform_data; + shmd = container_of(ws, struct shmem_link_device, cp_sleep_dwork.work); - dpld = kzalloc(sizeof(struct c2c_link_device), GFP_KERNEL); - if (!dpld) { - mif_err("dpld == NULL\n"); - return NULL; + spin_lock_irqsave(&shmd->pm_lock, flags); + i = atomic_read(&shmd->ref_cnt); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + if (i > 0) + goto reschedule; + + /* + * If there is any IPC message remained in a TXQ, AP must prevent CP + * from going to sleep. + */ + ld = &shmd->ld; + for (i = 0; i < ld->max_ipc_dev; i++) { + if (ld->skb_txq[i]->qlen > 0) + goto reschedule; } - wake_lock_init(&dpld->c2c_wake_lock, WAKE_LOCK_SUSPEND, "c2c_wakelock"); - wake_lock(&dpld->c2c_wake_lock); + if (gpio_get_value(shmd->gpio_ap_wakeup)) + goto reschedule; - ld = &dpld->ld; - dpld->pdata = pdata; + gpio_set_value(shmd->gpio_cp_wakeup, 0); + print_pm_status(shmd, 1); + return; - ld->name = "c2c"; +reschedule: + if (!work_pending(&shmd->cp_sleep_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } +} + +static void release_ap_status(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + int i; + unsigned long flags; - mif_info("%s is created!!!\n", dpld->ld.name); + shmd = container_of(ws, struct shmem_link_device, link_off_dwork.work); - return ld; + spin_lock_irqsave(&shmd->pm_lock, flags); + i = atomic_read(&shmd->ref_cnt); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + if (i > 0) + goto reschedule; + + if (gpio_get_value(shmd->gpio_cp_status)) + goto reschedule; + + gpio_set_value(shmd->gpio_ap_status, 0); + print_pm_status(shmd, 1); + + if (wake_lock_active(&shmd->cp_wlock)) + wake_unlock(&shmd->cp_wlock); + + return; + +reschedule: + if (!work_pending(&shmd->link_off_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, + msecs_to_jiffies(100)); + } +} + +/** + * forbid_cp_sleep + * @shmd: pointer to an instance of shmem_link_device structure + * + * Wakes up a CP if it can sleep and increases the "ref_cnt" counter in the + * shmem_link_device instance. + * + * CAUTION!!! permit_cp_sleep() MUST be invoked after forbid_cp_sleep() success + * to decrease the "ref_cnt" counter. + */ +static int forbid_cp_sleep(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + int err = 0; + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + atomic_inc(&shmd->ref_cnt); + if (gpio_get_value(shmd->gpio_ap_status) == 0) { + gpio_set_value(shmd->gpio_ap_status, 1); + print_pm_status(shmd, 1); + } + spin_unlock_irqrestore(&shmd->pm_lock, flags); + + if (work_pending(&shmd->cp_sleep_dwork.work)) + cancel_delayed_work(&shmd->cp_sleep_dwork); + + if (work_pending(&shmd->link_off_dwork.work)) + cancel_delayed_work(&shmd->link_off_dwork); + + if (gpio_get_value(shmd->gpio_cp_wakeup) == 0) { + gpio_set_value(shmd->gpio_cp_wakeup, 1); + print_pm_status(shmd, 1); + } + + if (check_link_status(shmd) < 0) { + print_pm_status(shmd, 1); + mif_err("%s: ERR! check_link_status fail\n", ld->name); + err = -EACCES; + goto exit; + } + +exit: + return err; +} + +/** + * permit_cp_sleep + * @shmd: pointer to an instance of shmem_link_device structure + * + * Decreases the "ref_cnt" counter in the shmem_link_device instance if it can + * sleep and allows a CP to sleep only if the value of "ref_cnt" counter is + * less than or equal to 0. + * + * MUST be invoked after forbid_cp_sleep() success to decrease the "ref_cnt" + * counter. + */ +static void permit_cp_sleep(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + + if (atomic_dec_return(&shmd->ref_cnt) > 0) { + spin_unlock_irqrestore(&shmd->pm_lock, flags); + return; + } + + atomic_set(&shmd->ref_cnt, 0); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + + /* Hold gpio_cp_wakeup for CP_WAKEUP_HOLD_TIME until CP finishes RX */ + if (!work_pending(&shmd->cp_sleep_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } +} + +/** + * ap_wakeup_handler: interrupt handler for a wakeup interrupt + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ap_wakeup_handler(int irq, void *data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + int ap_status = gpio_get_value(shmd->gpio_ap_status); + + s5p_change_irq_type(irq, ap_wakeup); + + if (ld->mode != LINK_MODE_IPC) + goto exit; + + if (work_pending(&shmd->cp_sleep_dwork.work)) + __cancel_delayed_work(&shmd->cp_sleep_dwork); + + print_pm_status(shmd, 1); + + if (ap_wakeup) { + if (work_pending(&shmd->link_off_dwork.work)) + __cancel_delayed_work(&shmd->link_off_dwork); + + if (!wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->ap_wlock); + + if (!c2c_suspended() && !ap_status) + gpio_set_value(shmd->gpio_ap_status, 1); + } else { + if (wake_lock_active(&shmd->ap_wlock)) + wake_unlock(&shmd->ap_wlock); + + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } + +exit: + return IRQ_HANDLED; +} + +static irqreturn_t cp_status_handler(int irq, void *data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + int cp_status = gpio_get_value(shmd->gpio_cp_status); + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + + s5p_change_irq_type(irq, cp_status); + + if (ld->mode != LINK_MODE_IPC) + goto exit; + + if (work_pending(&shmd->link_off_dwork.work)) + __cancel_delayed_work(&shmd->link_off_dwork); + + print_pm_status(shmd, 1); + + if (cp_status) { + if (!wake_lock_active(&shmd->cp_wlock)) + wake_lock(&shmd->cp_wlock); + } else { + if (atomic_read(&shmd->ref_cnt) > 0) { + queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, + msecs_to_jiffies(10)); + } else { + gpio_set_value(shmd->gpio_ap_status, 0); + if (wake_lock_active(&shmd->cp_wlock)) + wake_unlock(&shmd->cp_wlock); + } + } + +exit: + spin_unlock_irqrestore(&shmd->pm_lock, flags); + return IRQ_HANDLED; +} + +#if 1 +/* Functions for IPC/BOOT/DUMP RX */ +#endif + +/** + * handle_cp_crash + * @shmd: pointer to an instance of shmem_link_device structure + * + * Actual handler for the CRASH_EXIT command from a CP. + */ +static void handle_cp_crash(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod; + int i; + + if (shmd->forced_cp_crash) + shmd->forced_cp_crash = false; + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < MAX_SIPC5_DEV; i++) + skb_queue_purge(ld->skb_txq[i]); + + /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + /* time margin for taking state changes by rild */ + mdelay(100); + + /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ + iod = link_get_iod_with_format(ld, IPC_BOOT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); +} + +/** + * handle_no_cp_crash_ack + * @arg: pointer to an instance of shmem_link_device structure + * + * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no + * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. + */ +static void handle_no_cp_crash_ack(unsigned long arg) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)arg; + struct link_device *ld = &shmd->ld; + + mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name); + + handle_cp_crash(shmd); +} + +/** + * trigger_forced_cp_crash + * @shmd: pointer to an instance of shmem_link_device structure + * + * Triggers an enforced CP crash. + */ +static void trigger_forced_cp_crash(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct utc_time t; + + if (ld->mode == LINK_MODE_ULOAD) { + mif_err("%s: ALREADY in progress\n", ld->name, CALLER); + return; + } + ld->mode = LINK_MODE_ULOAD; + shmd->forced_cp_crash = true; + + get_utc_time(&t); + mif_err("%s: [%02d:%02d:%02d.%03d] \n", + ld->name, t.hour, t.min, t.sec, t.msec, CALLER); + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + +#ifdef DEBUG_MODEM_IF + if (in_interrupt()) + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); + else + save_mem_dump(shmd); +#endif + + /* Send CRASH_EXIT command to a CP */ + send_int2cp(shmd, INT_CMD(INT_CMD_CRASH_EXIT)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + + /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack() will be executed. */ + mif_add_timer(&shmd->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack, (unsigned long)shmd); + + return; +} + +/** + * cmd_crash_reset_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the CRASH_RESET command from a CP. + */ +static void cmd_crash_reset_handler(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod = NULL; + int i; + struct utc_time t; + + ld->mode = LINK_MODE_ULOAD; + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + + get_utc_time(&t); + mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC7 (CRASH_RESET)\n", + ld->name, t.hour, t.min, t.sec, t.msec); +#ifdef DEBUG_MODEM_IF + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); +#endif + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < MAX_SIPC5_DEV; i++) + skb_queue_purge(ld->skb_txq[i]); + + /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_RESET); + + /* time margin for taking state changes by rild */ + mdelay(100); + + /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ + iod = link_get_iod_with_format(ld, IPC_BOOT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_RESET); +} + +/** + * cmd_crash_exit_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the CRASH_EXIT command from a CP. + */ +static void cmd_crash_exit_handler(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct utc_time t; + + ld->mode = LINK_MODE_ULOAD; + + del_timer(&shmd->crash_ack_timer); + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + + get_utc_time(&t); + mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC9 (CRASH_EXIT)\n", + ld->name, t.hour, t.min, t.sec, t.msec); +#ifdef DEBUG_MODEM_IF + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); +#endif + + handle_cp_crash(shmd); +} + +/** + * cmd_phone_start_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the PHONE_START command from a CP. + */ +static void cmd_phone_start_handler(struct shmem_link_device *shmd) +{ + int err; + struct link_device *ld = &shmd->ld; + struct io_device *iod; + int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + int cp_status = gpio_get_value(shmd->gpio_cp_status); + + mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("%s: ERR! no iod\n", ld->name); + return; + } + + err = init_shmem_ipc(shmd); + if (err) + return; + + if (wake_lock_active(&shmd->wlock)) + wake_unlock(&shmd->wlock); + + s5p_change_irq_type(shmd->irq_ap_wakeup, ap_wakeup); + if (ap_wakeup && !wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->ap_wlock); + + s5p_change_irq_type(shmd->irq_cp_status, cp_status); + if (cp_status && !wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->cp_wlock); + + ld->mode = LINK_MODE_IPC; + iod->modem_state_changed(iod, STATE_ONLINE); +} + +/** + * cmd_handler: processes a SHMEM command from a CP + * @shmd: pointer to an instance of shmem_link_device structure + * @cmd: SHMEM command from a CP + */ +static void cmd_handler(struct shmem_link_device *shmd, u16 cmd) +{ + struct link_device *ld = &shmd->ld; + + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_CRASH_RESET: + cmd_crash_reset_handler(shmd); + break; + + case INT_CMD_CRASH_EXIT: + cmd_crash_exit_handler(shmd); + break; + + case INT_CMD_PHONE_START: + cmd_phone_start_handler(shmd); + complete_all(&ld->init_cmpl); + break; + + default: + mif_err("%s: unknown command 0x%04X\n", ld->name, cmd); + break; + } +} + +/** + * ipc_rx_work + * @ws: pointer to an instance of work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void ipc_rx_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + struct io_device *iod; + struct sk_buff *skb; + int i; + + shmd = container_of(ws, struct shmem_link_device, ipc_rx_dwork.work); + ld = &shmd->ld; + + for (i = 0; i < MAX_SIPC5_DEV; i++) { + iod = shmd->iod[i]; + while (1) { + skb = skb_dequeue(ld->skb_rxq[i]); + if (!skb) + break; + iod->recv_skb(iod, ld, skb); + } + } +} + +/** + * rx_ipc_frames + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_ipc_frames(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *rxq = ld->skb_rxq[dev]; + struct sk_buff *skb; + int ret; + /** + * variables for the status of the circular queue + */ + u8 *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int out; /* index to the start of current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + + src = circ->buff; + qsize = circ->qsize; + out = circ->out; + rcvd = circ->size; + + rest = rcvd; + tot = 0; + while (rest > 0) { + /* Copy the header in the frame to the header buffer */ + circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + mif_err("%s: ERR! %s INVALID config 0x%02X " + "(rcvd %d, rest %d)\n", ld->name, + get_dev_name(dev), hdr[0], rcvd, rest); + ret = -EBADMSG; + goto exit; + } + + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", + ld->name, get_dev_name(dev), tot, rest, rcvd); + ret = -EBADMSG; + goto exit; + } + + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + ret = -ENOMEM; + goto exit; + } + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + circ_read(dst, src, qsize, out, tot); + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(rxq, skb); + + /* Calculate new out value */ + rest -= tot; + out += tot; + if (unlikely(out >= qsize)) + out -= qsize; + } + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(shmd, dev, circ->in); + + return rcvd; + +exit: +#ifdef DEBUG_MODEM_IF + mif_err("%s: ERR! rcvd:%d tot:%d rest:%d\n", ld->name, rcvd, tot, rest); + pr_ipc(1, "c2c: ERR! CP2MIF", (src + out), (rest > 20) ? 20 : rest); +#endif + + return ret; +} + +/** + * msg_handler: receives IPC messages from every RXQ + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every IPC RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void msg_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ + struct link_device *ld = &shmd->ld; + struct circ_status circ; + int i = 0; + int ret = 0; + + if (!ipc_active(shmd)) + return; + + /* Read data from every RXQ */ + for (i = 0; i < MAX_SIPC5_DEV; i++) { + /* Invoke an RX function only when there is data in the RXQ */ + if (likely(mst->head[i][RX] != mst->tail[i][RX])) { + ret = get_rxq_rcvd(shmd, i, mst, &circ); + if (unlikely(ret < 0)) { + mif_err("%s: ERR! get_rxq_rcvd fail (err %d)\n", + ld->name, ret); +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + return; + } + + ret = rx_ipc_frames(shmd, i, &circ); + if (ret < 0) { +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + reset_rxq_circ(shmd, i); + } + } + } + + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &shmd->ipc_rx_dwork, 0); } + +static void msg_rx_task(unsigned long data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = &shmd->ld; + struct mem_status mst; + u16 mask = 0; + + get_shmem_status(shmd, RX, &mst); + + if ((mst.head[IPC_FMT][RX] != mst.tail[IPC_FMT][RX]) + || (mst.head[IPC_RAW][RX] != mst.tail[IPC_RAW][RX])) { +#if 0 + print_mem_status(ld, &mst); +#endif + msg_handler(shmd, &mst); + } + + /* + ** Check and process REQ_ACK (at this time, in == out) + */ + if (unlikely(shmd->dev[IPC_FMT]->req_ack_rcvd)) { + mask |= get_mask_res_ack(shmd, IPC_FMT); + shmd->dev[IPC_FMT]->req_ack_rcvd = 0; + } + + if (unlikely(shmd->dev[IPC_RAW]->req_ack_rcvd)) { + mask |= get_mask_res_ack(shmd, IPC_RAW); + shmd->dev[IPC_RAW]->req_ack_rcvd = 0; + } + + if (unlikely(mask)) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: send RES_ACK 0x%04X\n", ld->name, mask); +#endif + send_int2cp(shmd, INT_NON_CMD(mask)); + } +} + +/** + * ipc_handler: processes a SHMEM command or receives IPC messages + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * Invokes cmd_handler for a SHMEM command or msg_handler for IPC messages. + */ +static void ipc_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ +#ifdef DEBUG_MODEM_IF + struct link_device *ld = &shmd->ld; +#endif + u16 intr = mst->int2ap; + + if (unlikely(INT_CMD_VALID(intr))) { + cmd_handler(shmd, intr); + return; + } + + /* + ** Check REQ_ACK from CP -> REQ_ACK will be processed in the RX tasklet + */ + if (unlikely(intr & get_mask_req_ack(shmd, IPC_FMT))) + shmd->dev[IPC_FMT]->req_ack_rcvd = 1; + + if (unlikely(intr & get_mask_req_ack(shmd, IPC_RAW))) + shmd->dev[IPC_RAW]->req_ack_rcvd = 1; + + /* + ** Check and process RES_ACK from CP + */ + if (unlikely(intr & get_mask_res_ack(shmd, IPC_FMT))) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv FMT RES_ACK\n", ld->name); +#endif + complete(&shmd->req_ack_cmpl[IPC_FMT]); + } + + if (unlikely(intr & get_mask_res_ack(shmd, IPC_RAW))) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv RAW RES_ACK\n", ld->name); +#endif + complete(&shmd->req_ack_cmpl[IPC_RAW]); + } + + /* + ** Schedule RX tasklet + */ + tasklet_hi_schedule(&shmd->rx_tsk); +} + +/** + * rx_udl_frames + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_udl_frames(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod; + struct sk_buff *skb; + int ret; + /** + * variables for the status of the circular queue + */ + u8 *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int out; /* index to the start of current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + + src = circ->buff; + qsize = circ->qsize; + out = circ->out; + rcvd = circ->size; + + rest = rcvd; + tot = 0; + while (rest > 0) { + /* Copy the header in the frame to the header buffer */ + circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + mif_err("%s: ERR! %s INVALID config 0x%02X " + "(rest %d, rcvd %d)\n", ld->name, + get_dev_name(dev), hdr[0], rest, rcvd); + pr_ipc(1, "UDL", (src + out), (rest > 20) ? 20 : rest); + ret = -EBADMSG; + goto exit; + } + + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", + ld->name, get_dev_name(dev), tot, rest, rcvd); + ret = -ENODATA; + goto exit; + } + + /* Allocate an skb */ + skb = alloc_skb(tot + NET_SKB_PAD, GFP_KERNEL); + if (!skb) { + mif_err("%s: ERR! %s alloc_skb fail\n", + ld->name, get_dev_name(dev)); + ret = -ENOMEM; + goto exit; + } + skb_reserve(skb, NET_SKB_PAD); + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + circ_read(dst, src, qsize, out, tot); + + /* Pass the skb to an iod */ + iod = link_get_iod_with_channel(ld, sipc5_get_ch_id(skb->data)); + if (!iod) { + mif_err("%s: ERR! no IPC_BOOT iod\n", ld->name); + break; + } + +#ifdef DEBUG_MODEM_IF + if (!std_udl_with_payload(std_udl_get_cmd(skb->data))) { + if (ld->mode == LINK_MODE_DLOAD) { + pr_ipc(0, "[CP->AP] DL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } else { + pr_ipc(0, "[CP->AP] UL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } + } +#endif + + iod->recv_skb(iod, ld, skb); + + /* Calculate new out value */ + rest -= tot; + out += tot; + if (unlikely(out >= qsize)) + out -= qsize; + } + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(shmd, dev, circ->in); + + return rcvd; + +exit: + return ret; +} + +/** + * udl_rx_work + * @ws: pointer to an instance of the work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void udl_rx_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + struct sk_buff_head *rxq; + struct mem_status mst; + struct circ_status circ; + int dev = IPC_RAW; + + shmd = container_of(ws, struct shmem_link_device, udl_rx_dwork.work); + ld = &shmd->ld; + rxq = ld->skb_rxq[dev]; + + while (1) { + get_shmem_status(shmd, RX, &mst); + if (mst.head[dev][RX] == mst.tail[dev][RX]) + break; + + /* Invoke an RX function only when there is data in the RXQ */ + if (get_rxq_rcvd(shmd, dev, &mst, &circ) < 0) { + mif_err("%s: ERR! get_rxq_rcvd fail\n", ld->name); +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + break; + } + + if (rx_udl_frames(shmd, dev, &circ) < 0) { + skb_queue_purge(rxq); + break; + } + } +} + +/** + * udl_handler: receives BOOT/DUMP IPC messages from every RXQ + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every IPC RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void udl_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ + u16 intr = mst->int2ap; + + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &shmd->udl_rx_dwork, 0); + + /* Check and process RES_ACK */ + if (intr & INT_MASK_RES_ACK_SET) { + if (intr & get_mask_res_ack(shmd, IPC_RAW)) { +#ifdef DEBUG_MODEM_IF + struct link_device *ld = &shmd->ld; + mif_info("%s: recv RAW RES_ACK\n", ld->name); + print_circ_status(ld, IPC_RAW, mst); +#endif + complete(&shmd->req_ack_cmpl[IPC_RAW]); + } + } +} + +/** + * c2c_irq_handler: interrupt handler for a C2C interrupt + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + * + * Flow for normal interrupt handling: + * c2c_irq_handler -> udl_handler + * c2c_irq_handler -> ipc_handler -> cmd_handler -> cmd_xxx_handler + * c2c_irq_handler -> ipc_handler -> msg_handler -> rx_ipc_frames -> ... + */ +static void c2c_irq_handler(void *data, u32 intr) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + struct mem_status *mst = msq_get_free_slot(&shmd->stat_list); + + get_shmem_status(shmd, RX, mst); + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { + mif_info("%s: ld->mode == LINK_MODE_OFFLINE\n", ld->name); + return; + } + + if (unlikely(!INT_VALID(intr))) { + mif_info("%s: ERR! invalid intr 0x%X\n", ld->name, intr); + return; + } + + mst->int2ap = intr; + + if (ld->mode == LINK_MODE_DLOAD || ld->mode == LINK_MODE_ULOAD) + udl_handler(shmd, mst); + else + ipc_handler(shmd, mst); +} + +#if 1 +/* Functions for IPC/BOOT/DUMP TX */ +#endif + +/** + * write_ipc_to_txq + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @circ: pointer to an instance of circ_status structure + * @skb: pointer to an instance of sk_buff structure + * + * Must be invoked only when there is enough space in the TXQ. + */ +static void write_ipc_to_txq(struct shmem_link_device *shmd, int dev, + struct circ_status *circ, struct sk_buff *skb) +{ + u32 qsize = circ->qsize; + u32 in = circ->in; + u8 *buff = circ->buff; + u8 *src = skb->data; + u32 len = skb->len; +#ifdef DEBUG_MODEM_IF + struct io_device *iod = skbpriv(skb)->iod; + struct modem_ctl *mc = shmd->ld.mc; + char tag[MIF_MAX_STR_LEN]; + + snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", iod->name, mc->name); + + if (dev == IPC_FMT) + pr_ipc(1, tag, src, (len > 20 ? 20 : len)); +#if 0 + if (dev == IPC_RAW) + pr_ipc(0, tag, src, (len > 20 ? 20 : len)); +#endif +#endif + + /* Write data to the TXQ */ + circ_write(buff, src, qsize, in, len); + + /* Update new head (in) pointer */ + set_txq_head(shmd, dev, circ_new_pointer(qsize, in, len)); +} + +/** + * xmit_ipc_msg + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. + * + * Returns total length of IPC messages transmitted or an error code. + */ +static int xmit_ipc_msg(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long flags; + struct circ_status circ; + int space; + int copied = 0; + + /* Acquire the spin lock for a TXQ */ + spin_lock_irqsave(&shmd->tx_lock[dev], flags); + + while (1) { + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (unlikely(space < 0)) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(shmd); +#endif + /* Empty out the TXQ */ + reset_txq_circ(shmd, dev); + copied = -EIO; + break; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + /* Check the free space size comparing with skb->len */ + if (unlikely(space < skb->len)) { +#ifdef DEBUG_MODEM_IF + struct mem_status mst; +#endif + /* Set res_required flag for the "dev" */ + atomic_set(&shmd->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_err("%s: NOSPC in %s_TXQ" + "{qsize:%u in:%u out:%u} free:%u < len:%u\n", + ld->name, CALLER, get_dev_name(dev), + circ.qsize, circ.in, circ.out, space, skb->len); +#ifdef DEBUG_MODEM_IF + get_shmem_status(shmd, TX, &mst); + print_circ_status(ld, dev, &mst); +#endif + copied = -ENOSPC; + break; + } + +#ifdef DEBUG_MODEM_IF + if (!ipc_active(shmd)) { + if (get_magic(shmd) == SHM_PM_MAGIC) { + mif_err("%s: Going to SLEEP\n", ld->name); + copied = -EBUSY; + } else { + mif_err("%s: IPC is NOT active\n", ld->name); + copied = -EIO; + } + break; + } +#endif + + /* TX only when there is enough space in the TXQ */ + write_ipc_to_txq(shmd, dev, &circ, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + /* Release the spin lock */ + spin_unlock_irqrestore(&shmd->tx_lock[dev], flags); + + return copied; +} + +/** + * wait_for_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Sends an REQ_ACK interrupt for @dev to CP. + * 2) Waits for the corresponding RES_ACK for @dev from CP. + * + * Returns the return value from wait_for_completion_interruptible_timeout(). + */ +static int wait_for_res_ack(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct completion *cmpl = &shmd->req_ack_cmpl[dev]; + unsigned long timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); + int ret; + u16 mask; + +#ifdef DEBUG_MODEM_IF + mif_info("%s: send %s REQ_ACK\n", ld->name, get_dev_name(dev)); +#endif + + mask = get_mask_req_ack(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + + /* ret < 0 if interrupted, ret == 0 on timeout */ + ret = wait_for_completion_interruptible_timeout(cmpl, timeout); + if (ret < 0) { + mif_err("%s: %s: wait_for_completion interrupted! (ret %d)\n", + ld->name, get_dev_name(dev), ret); + goto exit; + } + + if (ret == 0) { + struct mem_status mst; + get_shmem_status(shmd, TX, &mst); + + mif_err("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", + ld->name, get_dev_name(dev)); + + /* + ** The TXQ must be checked whether or not it is empty, because + ** an interrupt mask can be overwritten by the next interrupt. + */ + if (mst.head[dev][TX] == mst.tail[dev][TX]) { + ret = get_txq_buff_size(shmd, dev); +#ifdef DEBUG_MODEM_IF + mif_err("%s: %s_TXQ has been emptied\n", + ld->name, get_dev_name(dev)); + print_circ_status(ld, dev, &mst); +#endif + } + } + +exit: + return ret; +} + +/** + * process_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Restarts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + * + * Returns the return value from xmit_ipc_msg(). + */ +static int process_res_ack(struct shmem_link_device *shmd, int dev) +{ + int ret; + u16 mask; + + ret = xmit_ipc_msg(shmd, dev); + if (ret > 0) { + mask = get_mask_send(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + } + + if (ret >= 0) + atomic_set(&shmd->res_required[dev], 0); + + return ret; +} + +/** + * fmt_tx_work: performs TX for FMT IPC device under SHMEM flow control + * @ws: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of FMT IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts SHMEM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. + */ +static void fmt_tx_work(struct work_struct *ws) +{ + struct link_device *ld; + struct shmem_link_device *shmd; + int ret; + + ld = container_of(ws, struct link_device, fmt_tx_dwork.work); + shmd = to_shmem_link_device(ld); + + ret = wait_for_res_ack(shmd, IPC_FMT); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); + return; + } + + ret = process_res_ack(shmd, IPC_FMT); + if (ret >= 0) { + permit_cp_sleep(shmd); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], + msecs_to_jiffies(1)); + } +} + +/** + * raw_tx_work: performs TX for RAW IPC device under SHMEM flow control. + * @ws: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RAW IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts SHMEM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. + */ +static void raw_tx_work(struct work_struct *ws) +{ + struct link_device *ld; + struct shmem_link_device *shmd; + int ret; + + ld = container_of(ws, struct link_device, raw_tx_dwork.work); + shmd = to_shmem_link_device(ld); + + ret = wait_for_res_ack(shmd, IPC_RAW); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); + return; + } + + ret = process_res_ack(shmd, IPC_RAW); + if (ret >= 0) { + permit_cp_sleep(shmd); + mif_netif_wake(ld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], + msecs_to_jiffies(1)); + } +} + +/** + * c2c_send_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @skb: pointer to an skb that will be transmitted + * + * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static int c2c_send_ipc(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + int ret; + u16 mask; + + if (atomic_read(&shmd->res_required[dev]) > 0) { + mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + return 0; + } + + ret = xmit_ipc_msg(shmd, dev); + if (likely(ret > 0)) { + mask = get_mask_send(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + goto exit; + } + + /* If there was no TX, just exit */ + if (ret == 0) + goto exit; + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + /* Prohibit CP from sleeping until the TXQ buffer is empty */ + if (forbid_cp_sleep(shmd) < 0) { + trigger_forced_cp_crash(shmd); + goto exit; + } + + /*----------------------------------------------------*/ + /* shmd->res_required[dev] was set in xmit_ipc_msg(). */ + /*----------------------------------------------------*/ + + if (dev == IPC_RAW) + mif_netif_stop(ld); + + queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], + msecs_to_jiffies(1)); + } + +exit: + return ret; +} + +/** + * c2c_try_send_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages with c2c_send_ipc(). + */ +static void c2c_try_send_ipc(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + int ret; + + if (forbid_cp_sleep(shmd) < 0) { + trigger_forced_cp_crash(shmd); + goto exit; + } + + if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { + mif_err("%s: %s txq->qlen %d >= %d\n", ld->name, + get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); + dev_kfree_skb_any(skb); + goto exit; + } + + skb_queue_tail(txq, skb); + + ret = c2c_send_ipc(shmd, dev); + if (ret < 0) { + mif_err("%s->%s: ERR! c2c_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } + +exit: + permit_cp_sleep(shmd); +} + +static int c2c_send_udl_cmd(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + u8 *buff; + u8 *src; + u32 qsize; + u32 in; + int space; + int tx_bytes; + struct circ_status circ; + + if (iod->format == IPC_BOOT) { + pr_ipc(0, "[AP->CP] DL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } else { + pr_ipc(0, "[AP->CP] UL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } + + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (space < 0) { + reset_txq_circ(shmd, dev); + tx_bytes = -EIO; + goto exit; + } + + /* Get the size of data to be sent */ + tx_bytes = skb->len; + + /* Check the size of free space */ + if (space < tx_bytes) { + mif_err("%s: NOSPC in %s_TXQ {qsize:%u in:%u out:%u}, " + "free:%u < tx_bytes:%u\n", ld->name, get_dev_name(dev), + circ.qsize, circ.in, circ.out, space, tx_bytes); + tx_bytes = -ENOSPC; + goto exit; + } + + /* Write data to the TXQ */ + buff = circ.buff; + src = skb->data; + qsize = circ.qsize; + in = circ.in; + circ_write(buff, src, qsize, in, tx_bytes); + + /* Update new head (in) pointer */ + set_txq_head(shmd, dev, circ_new_pointer(qsize, circ.in, tx_bytes)); + +exit: + dev_kfree_skb_any(skb); + return tx_bytes; +} + +static int c2c_send_udl_data(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + u8 *src; + int tx_bytes; + int copied; + u8 *buff; + u32 qsize; + u32 in; + u32 out; + int space; + struct circ_status circ; + + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (space < 0) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(shmd); +#endif + /* Empty out the TXQ */ + reset_txq_circ(shmd, dev); + return -EFAULT; + } + + buff = circ.buff; + qsize = circ.qsize; + in = circ.in; + out = circ.out; + space = circ.size; + + copied = 0; + while (1) { + skb = skb_dequeue(txq); + if (!skb) + break; + + /* Get the size of data to be sent */ + src = skb->data; + tx_bytes = skb->len; + + /* Check the free space size comparing with skb->len */ + if (space < tx_bytes) { + /* Set res_required flag for the "dev" */ + atomic_set(&shmd->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_info("NOSPC in RAW_TXQ {qsize:%u in:%u out:%u}, " + "space:%u < tx_bytes:%u\n", + qsize, in, out, space, tx_bytes); + break; + } + + /* + ** TX only when there is enough space in the TXQ + */ + circ_write(buff, src, qsize, in, tx_bytes); + + copied += tx_bytes; + in = circ_new_pointer(qsize, in, tx_bytes); + space -= tx_bytes; + + dev_kfree_skb_any(skb); + } + + /* Update new head (in) pointer */ + if (copied > 0) { + in = circ_new_pointer(qsize, circ.in, copied); + set_txq_head(shmd, dev, in); + } + + return copied; +} + +/** + * c2c_send_udl + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 4) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static void c2c_send_udl(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct completion *cmpl = &shmd->req_ack_cmpl[dev]; + struct std_dload_info *dl_info = &shmd->dl_info; + struct mem_status mst; + u32 timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); + u32 udl_cmd; + int ret; + u16 mask = get_mask_send(shmd, dev); + + udl_cmd = std_udl_get_cmd(skb->data); + if (iod->format == IPC_RAMDUMP || !std_udl_with_payload(udl_cmd)) { + ret = c2c_send_udl_cmd(shmd, dev, iod, skb); + if (ret > 0) + send_int2cp(shmd, INT_NON_CMD(mask)); + else + mif_err("ERR! c2c_send_udl_cmd fail (err %d)\n", ret); + goto exit; + } + + skb_queue_tail(txq, skb); + if (txq->qlen < dl_info->num_frames) + goto exit; + + mask |= get_mask_req_ack(shmd, dev); + while (1) { + ret = c2c_send_udl_data(shmd, dev); + if (ret < 0) { + mif_err("ERR! c2c_send_udl_data fail (err %d)\n", ret); + skb_queue_purge(txq); + break; + } + + if (skb_queue_empty(txq)) { + send_int2cp(shmd, INT_NON_CMD(mask)); + break; + } + + send_int2cp(shmd, INT_NON_CMD(mask)); + + do { + ret = wait_for_completion_timeout(cmpl, timeout); + get_shmem_status(shmd, TX, &mst); + } while (mst.head[dev][TX] != mst.tail[dev][TX]); + } + +exit: + return; +} + +/** + * c2c_send + * @ld: pointer to an instance of the link_device structure + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * Returns the length of data transmitted or an error code. + * + * Normal call flow for an IPC message: + * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on congestion in a IPC TXQ: + * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work + * => xxx_tx_work -> wait_for_res_ack + * => msg_handler + * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) + */ +static int c2c_send(struct link_device *ld, struct io_device *iod, + struct sk_buff *skb) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + int dev = iod->format; + int len = skb->len; + + switch (dev) { + case IPC_FMT: + case IPC_RAW: + if (likely(ld->mode == LINK_MODE_IPC)) { + c2c_try_send_ipc(shmd, dev, iod, skb); + } else { + mif_err("%s->%s: ERR! ld->mode != LINK_MODE_IPC\n", + iod->name, ld->name); + dev_kfree_skb_any(skb); + } + return len; + + case IPC_BOOT: + case IPC_RAMDUMP: + c2c_send_udl(shmd, IPC_RAW, iod, skb); + return len; + + default: + mif_err("%s: ERR! no TXQ for %s\n", ld->name, iod->name); + dev_kfree_skb_any(skb); + return -ENODEV; + } +} + +#if 1 +/* Functions for BOOT/DUMP and INIT */ +#endif + +static int c2c_dload_start(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + u32 magic; + + ld->mode = LINK_MODE_DLOAD; + + clear_shmem_map(shmd); + + set_magic(shmd, SHM_BOOT_MAGIC); + magic = get_magic(shmd); + if (magic != SHM_BOOT_MAGIC) { + mif_err("%s: ERR! magic 0x%08X != SHM_BOOT_MAGIC 0x%08X\n", + ld->name, magic, SHM_BOOT_MAGIC); + return -EFAULT; + } + + return 0; +} + +/** + * c2c_set_dload_info + * @ld: pointer to an instance of link_device structure + * @iod: pointer to an instance of io_device structure + * @arg: pointer to an instance of std_dload_info structure in "user" memory + * + */ +static int c2c_set_dload_info(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + struct std_dload_info *dl_info = &shmd->dl_info; + int ret; + + ret = copy_from_user(dl_info, (void __user *)arg, + sizeof(struct std_dload_info)); + if (ret) { + mif_err("ERR! copy_from_user fail!\n"); + return -EFAULT; + } + + return 0; +} + +static int c2c_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + mif_err("+++\n"); + trigger_forced_cp_crash(shmd); + mif_err("---\n"); + return 0; +} + +static int c2c_dump_start(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + + ld->mode = LINK_MODE_ULOAD; + + clear_shmem_map(shmd); + + mif_err("%s: magic = 0x%08X\n", ld->name, SHM_DUMP_MAGIC); + set_magic(shmd, SHM_DUMP_MAGIC); + + return 0; +} + +static void c2c_remap_4mb_ipc_region(struct shmem_link_device *shmd) +{ + struct shmem_4mb_phys_map *map; + struct shmem_ipc_device *dev; + + map = (struct shmem_4mb_phys_map *)shmd->base; + + /* Magic code and access enable fields */ + shmd->ipc_map.magic = (u32 __iomem *)&map->magic; + shmd->ipc_map.access = (u32 __iomem *)&map->access; + + /* FMT */ + dev = &shmd->ipc_map.dev[IPC_FMT]; + + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; + + dev->txq.head = (u32 __iomem *)&map->fmt_tx_head; + dev->txq.tail = (u32 __iomem *)&map->fmt_tx_tail; + dev->txq.buff = (u8 __iomem *)&map->fmt_tx_buff[0]; + dev->txq.size = SHM_4M_FMT_TX_BUFF_SZ; + + dev->rxq.head = (u32 __iomem *)&map->fmt_rx_head; + dev->rxq.tail = (u32 __iomem *)&map->fmt_rx_tail; + dev->rxq.buff = (u8 __iomem *)&map->fmt_rx_buff[0]; + dev->rxq.size = SHM_4M_FMT_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_F; + dev->mask_res_ack = INT_MASK_RES_ACK_F; + dev->mask_send = INT_MASK_SEND_F; + + /* RAW */ + dev = &shmd->ipc_map.dev[IPC_RAW]; + + strcpy(dev->name, "RAW"); + dev->id = IPC_RAW; + + dev->txq.head = (u32 __iomem *)&map->raw_tx_head; + dev->txq.tail = (u32 __iomem *)&map->raw_tx_tail; + dev->txq.buff = (u8 __iomem *)&map->raw_tx_buff[0]; + dev->txq.size = SHM_4M_RAW_TX_BUFF_SZ; + + dev->rxq.head = (u32 __iomem *)&map->raw_rx_head; + dev->rxq.tail = (u32 __iomem *)&map->raw_rx_tail; + dev->rxq.buff = (u8 __iomem *)&map->raw_rx_buff[0]; + dev->rxq.size = SHM_4M_RAW_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_R; + dev->mask_res_ack = INT_MASK_RES_ACK_R; + dev->mask_send = INT_MASK_SEND_R; + + /* interrupt ports */ + shmd->ipc_map.mbx2ap = NULL; + shmd->ipc_map.mbx2cp = NULL; +} + +static int c2c_init_ipc_map(struct shmem_link_device *shmd) +{ + if (shmd->size >= SHMEM_SIZE_4MB) + c2c_remap_4mb_ipc_region(shmd); + else + return -EINVAL; + + memset(shmd->base, 0, shmd->size); + + shmd->magic = shmd->ipc_map.magic; + shmd->access = shmd->ipc_map.access; + + shmd->dev[IPC_FMT] = &shmd->ipc_map.dev[IPC_FMT]; + shmd->dev[IPC_RAW] = &shmd->ipc_map.dev[IPC_RAW]; + + shmd->mbx2ap = shmd->ipc_map.mbx2ap; + shmd->mbx2cp = shmd->ipc_map.mbx2cp; + + return 0; +} +static int c2c_init_communication(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + struct io_device *check_iod; + + if (iod->format == IPC_BOOT) + return 0; + + /* send 0xC2 */ + switch(iod->format) { + case IPC_FMT: + check_iod = link_get_iod_with_format(ld, IPC_RFS); + if (check_iod ? atomic_read(&check_iod->opened) : true) { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); + } else + mif_err("%s defined but not opened\n", check_iod->name); + break; + case IPC_RFS: + check_iod = link_get_iod_with_format(ld, IPC_FMT); + if (check_iod && atomic_read(&check_iod->opened)) { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); + } else + mif_err("not opened\n"); + break; + default: + break; + } + return 0; +} + +#if 0 +static void c2c_link_terminate(struct link_device *ld, struct io_device *iod) +{ + if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { + if (!atomic_read(&iod->opened)) { + ld->mode = LINK_MODE_OFFLINE; + mif_err("%s: %s: link mode changed: IPC -> OFFLINE\n", + iod->name, ld->name); + } + } + + return; +} +#endif + +struct link_device *c2c_create_link_device(struct platform_device *pdev) +{ + struct modem_data *modem; + struct shmem_link_device *shmd; + struct link_device *ld; + int err; + int i; + u32 phys_base; + u32 offset; + u32 size; + int irq; + unsigned long irq_flags; + char name[MIF_MAX_NAME_LEN]; + + /* + ** Get the modem (platform) data + */ + modem = (struct modem_data *)pdev->dev.platform_data; + if (!modem) { + mif_err("ERR! modem == NULL\n"); + return NULL; + } + mif_err("%s: %s\n", modem->link_name, modem->name); + + if (modem->ipc_version < SIPC_VER_50) { + mif_err("%s: %s: ERR! IPC version %d < SIPC_VER_50\n", + modem->link_name, modem->name, modem->ipc_version); + return NULL; + } + + /* + ** Alloc an instance of shmem_link_device structure + */ + shmd = kzalloc(sizeof(struct shmem_link_device), GFP_KERNEL); + if (!shmd) { + mif_err("%s: ERR! shmd kzalloc fail\n", modem->link_name); + goto error; + } + ld = &shmd->ld; + + /* + ** Retrieve modem data and SHMEM control data from the modem data + */ + ld->mdm_data = modem; + ld->name = modem->link_name; + ld->aligned = 1; + ld->ipc_version = modem->ipc_version; + ld->max_ipc_dev = MAX_SIPC5_DEV; + + /* + ** Set attributes as a link device + */ + ld->init_comm = c2c_init_communication; +#if 0 + ld->terminate_comm = c2c_link_terminate; +#endif + ld->send = c2c_send; + ld->dload_start = c2c_dload_start; + ld->firm_update = c2c_set_dload_info; + ld->force_dump = c2c_force_dump; + ld->dump_start = c2c_dump_start; + + INIT_LIST_HEAD(&ld->list); + + skb_queue_head_init(&ld->sk_fmt_tx_q); + skb_queue_head_init(&ld->sk_raw_tx_q); + ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q; + ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; + + skb_queue_head_init(&ld->sk_fmt_rx_q); + skb_queue_head_init(&ld->sk_raw_rx_q); + ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; + ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; + + init_completion(&ld->init_cmpl); + + /* + ** Retrieve SHMEM resource + */ + if (modem->link_types & LINKTYPE(LINKDEV_C2C)) { + shmd->type = C2C_SHMEM; + mif_debug("%s: shmd->type = C2C_SHMEM\n", ld->name); + } else if (modem->link_types & LINKTYPE(LINKDEV_SHMEM)) { + shmd->type = REAL_SHMEM; + mif_debug("%s: shmd->type = REAL_SHMEM\n", ld->name); + } else { + mif_err("%s: ERR! invalid type\n", ld->name); + goto error; + } + + phys_base = c2c_get_phys_base(); + offset = c2c_get_sh_rgn_offset(); + size = c2c_get_sh_rgn_size(); + mif_debug("%s: phys_base:0x%08X offset:0x%08X size:%d\n", + ld->name, phys_base, offset, size); + + shmd->start = phys_base + offset; + shmd->size = size; + shmd->base = c2c_request_sh_region(shmd->start, shmd->size); + if (!shmd->base) { + mif_err("%s: ERR! c2c_request_sh_region fail\n", ld->name); + goto error; + } + + mif_debug("%s: phys_addr:0x%08X virt_addr:0x%08X size:%d\n", + ld->name, shmd->start, (int)shmd->base, shmd->size); + + /* + ** Initialize SHMEM maps (physical map -> logical map) + */ + err = c2c_init_ipc_map(shmd); + if (err < 0) { + mif_err("%s: ERR! c2c_init_ipc_map fail (err %d)\n", + ld->name, err); + goto error; + } + + /* + ** Initialize locks, completions, and bottom halves + */ + sprintf(shmd->wlock_name, "%s_wlock", ld->name); + wake_lock_init(&shmd->wlock, WAKE_LOCK_SUSPEND, shmd->wlock_name); + + sprintf(shmd->ap_wlock_name, "%s_ap_wlock", ld->name); + wake_lock_init(&shmd->ap_wlock, WAKE_LOCK_SUSPEND, shmd->ap_wlock_name); + + sprintf(shmd->cp_wlock_name, "%s_cp_wlock", ld->name); + wake_lock_init(&shmd->cp_wlock, WAKE_LOCK_SUSPEND, shmd->cp_wlock_name); + + init_completion(&shmd->udl_cmpl); + for (i = 0; i < MAX_SIPC5_DEV; i++) + init_completion(&shmd->req_ack_cmpl[i]); + + tasklet_init(&shmd->rx_tsk, msg_rx_task, (unsigned long)shmd); + INIT_DELAYED_WORK(&shmd->ipc_rx_dwork, ipc_rx_work); + INIT_DELAYED_WORK(&shmd->udl_rx_dwork, udl_rx_work); + + for (i = 0; i < MAX_SIPC5_DEV; i++) { + spin_lock_init(&shmd->tx_lock[i]); + atomic_set(&shmd->res_required[i], 0); + } + + ld->tx_wq = create_singlethread_workqueue("shmem_tx_wq"); + if (!ld->tx_wq) { + mif_err("%s: ERR! fail to create tx_wq\n", ld->name); + goto error; + } + INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); + INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); + ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; + ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; + + spin_lock_init(&shmd->stat_list.lock); + spin_lock_init(&shmd->trace_list.lock); +#ifdef DEBUG_MODEM_IF + INIT_DELAYED_WORK(&shmd->dump_dwork, mem_dump_work); +#endif + + INIT_DELAYED_WORK(&shmd->cp_sleep_dwork, release_cp_wakeup); + INIT_DELAYED_WORK(&shmd->link_off_dwork, release_ap_status); + spin_lock_init(&shmd->pm_lock); + + /* + ** Retrieve SHMEM IRQ GPIO#, IRQ#, and IRQ flags + */ + shmd->gpio_pda_active = modem->gpio_pda_active; + mif_err("PDA_ACTIVE gpio# = %d (value %d)\n", + shmd->gpio_pda_active, gpio_get_value(shmd->gpio_pda_active)); + + shmd->gpio_ap_status = modem->gpio_ap_status; + shmd->gpio_ap_wakeup = modem->gpio_ap_wakeup; + shmd->irq_ap_wakeup = modem->irq_ap_wakeup; + if (!shmd->irq_ap_wakeup) { + mif_err("ERR! no irq_ap_wakeup\n"); + goto error; + } + mif_debug("CP2AP_WAKEUP IRQ# = %d\n", shmd->irq_ap_wakeup); + + shmd->gpio_cp_wakeup = modem->gpio_cp_wakeup; + shmd->gpio_cp_status = modem->gpio_cp_status; + shmd->irq_cp_status = modem->irq_cp_status; + if (!shmd->irq_cp_status) { + mif_err("ERR! no irq_cp_status\n"); + goto error; + } + mif_debug("CP2AP_STATUS IRQ# = %d\n", shmd->irq_cp_status); + + c2c_assign_gpio_ap_wakeup(shmd->gpio_ap_wakeup); + c2c_assign_gpio_ap_status(shmd->gpio_ap_status); + c2c_assign_gpio_cp_wakeup(shmd->gpio_cp_wakeup); + c2c_assign_gpio_cp_status(shmd->gpio_cp_status); + + gpio_set_value(shmd->gpio_pda_active, 1); + gpio_set_value(shmd->gpio_ap_status, 1); + + /* + ** Register interrupt handlers + */ + err = c2c_register_handler(c2c_irq_handler, shmd); + if (err) { + mif_err("%s: ERR! c2c_register_handler fail (err %d)\n", + ld->name, err); + goto error; + } + + snprintf(name, MIF_MAX_NAME_LEN, "%s_ap_wakeup", ld->name); + irq = shmd->irq_ap_wakeup; + irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); + err = mif_register_isr(irq, ap_wakeup_handler, irq_flags, name, shmd); + if (err) + goto error; + + snprintf(name, MIF_MAX_NAME_LEN, "%s_cp_status", ld->name); + irq = shmd->irq_cp_status; + irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); + err = mif_register_isr(irq, cp_status_handler, irq_flags, name, shmd); + if (err) + goto error; + + return ld; + +error: + mif_err("xxx\n"); + kfree(shmd); + return NULL; +} + diff --git a/drivers/misc/modem_if/modem_link_device_c2c.h b/drivers/misc/modem_if/modem_link_device_c2c.h index 7ec9aa6..9dba90b 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.h +++ b/drivers/misc/modem_if/modem_link_device_c2c.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -8,209 +7,18 @@ * * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ -#include #ifndef __MODEM_LINK_DEVICE_C2C_H__ #define __MODEM_LINK_DEVICE_C2C_H__ -#define DPRAM_ERR_MSG_LEN 128 -#define DPRAM_ERR_DEVICE "c2cerr" +#include +#include "modem_link_device_shmem.h" -#define MAX_IDX 2 - -#define DPRAM_BASE_PTR 0x4000000 - -#define DPRAM_START_ADDRESS 0 -#define DPRAM_MAGIC_CODE_ADDRESS DPRAM_START_ADDRESS -#define DPRAM_GOTA_MAGIC_CODE_SIZE 0x4 -#define DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS \ - (DPRAM_START_ADDRESS + DPRAM_GOTA_MAGIC_CODE_SIZE) -#define BSP_DPRAM_BASE_SIZE 0x1ff8 -#define DPRAM_END_OF_ADDRESS (BSP_DPRAM_BASE_SIZE - 1) -#define DPRAM_INTERRUPT_SIZE 0x2 -#define DPRAM_PDA2PHONE_INTERRUPT_ADDRESS \ - (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE - DPRAM_INTERRUPT_SIZE*2) -#define DPRAM_PHONE2PDA_INTERRUPT_ADDRESS \ - (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE) -#define DPRAM_BUFFER_SIZE \ - (DPRAM_PHONE2PDA_INTERRUPT_ADDRESS -\ - DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS) -#define DPRAM_INDEX_SIZE 0x2 - -#define MAGIC_DMDL 0x4445444C -#define MAGIC_UMDL 0x4445444D - -#define DPRAM_PACKET_DATA_SIZE 0x3f00 -#define DPRAM_PACKET_HEADER_SIZE 0x7 - -#define INT_GOTA_MASK_VALID 0xA000 -#define INT_DPRAM_DUMP_MASK_VALID 0xA000 -#define MASK_CMD_RECEIVE_READY_NOTIFICATION 0xA100 -#define MASK_CMD_DOWNLOAD_START_REQUEST 0xA200 -#define MASK_CMD_DOWNLOAD_START_RESPONSE 0xA301 -#define MASK_CMD_IMAGE_SEND_REQUEST 0xA400 -#define MASK_CMD_IMAGE_SEND_RESPONSE 0xA500 -#define MASK_CMD_SEND_DONE_REQUEST 0xA600 -#define MASK_CMD_SEND_DONE_RESPONSE 0xA701 -#define MASK_CMD_STATUS_UPDATE_NOTIFICATION 0xA800 -#define MASK_CMD_UPDATE_DONE_NOTIFICATION 0xA900 -#define MASK_CMD_EFS_CLEAR_RESPONSE 0xAB00 -#define MASK_CMD_ALARM_BOOT_OK 0xAC00 -#define MASK_CMD_ALARM_BOOT_FAIL 0xAD00 - -#define WRITEIMG_HEADER_SIZE 8 -#define WRITEIMG_TAIL_SIZE 4 -#define WRITEIMG_BODY_SIZE \ - (DPRAM_BUFFER_SIZE - WRITEIMG_HEADER_SIZE - WRITEIMG_TAIL_SIZE) - -#define DPDN_DEFAULT_WRITE_LEN WRITEIMG_BODY_SIZE -#define CMD_DL_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 - -#define CMD_UL_START_REQ 0x9200 -#define CMD_UL_START_READY 0x9400 -#define CMD_UL_SEND_RESP 0x9601 -#define CMD_UL_SEND_DONE_RESP 0x9801 -#define CMD_UL_SEND_REQ 0xA500 -#define CMD_UL_START_RESPONSE 0xA301 -#define CMD_UL_SEND_DONE_REQ 0xA700 -#define CMD_RECEIVE_READY_NOTIFICATION 0xA100 - -#define MASK_CMD_RESULT_FAIL 0x0002 -#define MASK_CMD_RESULT_SUCCESS 0x0001 - -#define START_INDEX 0x007F -#define END_INDEX 0x007E - -#define CMD_IMG_SEND_REQ 0x9400 - -#define CRC_TAB_SIZE 256 -#define CRC_16_L_SEED 0xFFFF - -struct c2c_device { - /* DPRAM memory addresses */ - u16 *in_head_addr; - u16 *in_tail_addr; - u8 *in_buff_addr; - unsigned long in_buff_size; - - u16 *out_head_addr; - u16 *out_tail_addr; - u8 *out_buff_addr; - unsigned long out_buff_size; - - unsigned long in_head_saved; - unsigned long in_tail_saved; - unsigned long out_head_saved; - unsigned long out_tail_saved; - - u16 mask_req_ack; - u16 mask_res_ack; - u16 mask_send; -}; - -struct memory_region { - u8 *control; - u8 *fmt_out; - u8 *raw_out; - u8 *fmt_in; - u8 *raw_in; - u8 *mbx; -}; - -struct UldDataHeader { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -}; - -struct c2c_link_device { - struct link_device ld; - - struct modem_data *pdata; - - /*only c2c*/ - struct wake_lock c2c_wake_lock; - atomic_t raw_txq_req_ack_rcvd; - atomic_t fmt_txq_req_ack_rcvd; - u8 net_stop_flag; - int phone_sync; - u8 phone_status; - - struct work_struct xmit_work_struct; - - struct workqueue_struct *gota_wq; - struct work_struct gota_cmd_work; - - struct c2c_device dev_map[MAX_IDX]; - - struct wake_lock dumplock; - - u8 c2c_read_data[131072]; - - int c2c_init_cmd_wait_condition; - wait_queue_head_t c2c_init_cmd_wait_q; - - int modem_pif_init_wait_condition; - wait_queue_head_t modem_pif_init_done_wait_q; - - struct completion gota_download_start_complete; - - int gota_send_done_cmd_wait_condition; - wait_queue_head_t gota_send_done_cmd_wait_q; - - int gota_update_done_cmd_wait_condition; - wait_queue_head_t gota_update_done_cmd_wait_q; - - int upload_send_req_wait_condition; - wait_queue_head_t upload_send_req_wait_q; - - int upload_send_done_wait_condition; - wait_queue_head_t upload_send_done_wait_q; - - int upload_start_req_wait_condition; - wait_queue_head_t upload_start_req_wait_q; - - int upload_packet_start_condition; - wait_queue_head_t upload_packet_start_wait_q; - - u16 gota_irq_handler_cmd; - - u16 c2c_dump_handler_cmd; - - int dump_region_number; - - unsigned int is_c2c_err ; - - int c2c_dump_start; - int gota_start; - - char c2c_err_buf[DPRAM_ERR_MSG_LEN]; - - struct fasync_struct *c2c_err_async_q; - - void (*clear_interrupt)(struct c2c_link_device *); - - struct memory_region m_region; - - unsigned long fmt_out_buff_size; - unsigned long raw_out_buff_size; - unsigned long fmt_in_buff_size; - unsigned long raw_in_buff_size; - - struct delayed_work delayed_tx; - struct sk_buff *delayed_skb; - u8 delayed_count; -}; - -/* converts from struct link_device* to struct xxx_link_device* */ -#define to_c2c_link_device(linkdev) \ - container_of(linkdev, struct c2c_link_device, ld) +#define CP_WAKEUP_HOLD_TIME 500 /* 500 ms */ #endif + diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c index a650ed9..a87aff2 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.c +++ b/drivers/misc/modem_if/modem_link_device_dpram.c @@ -25,560 +25,385 @@ #include #include #include -#include +#include +#include +#include +#include "modem.h" #include "modem_prj.h" -#include "modem_link_device_dpram.h" #include "modem_utils.h" +#include "modem_link_device_dpram.h" -/* -** Function prototypes for basic DPRAM operations -*/ -static inline void clear_intr(struct dpram_link_device *dpld); -static inline u16 recv_intr(struct dpram_link_device *dpld); -static inline void send_intr(struct dpram_link_device *dpld, u16 mask); - -static inline u16 get_magic(struct dpram_link_device *dpld); -static inline void set_magic(struct dpram_link_device *dpld, u16 val); -static inline u16 get_access(struct dpram_link_device *dpld); -static inline void set_access(struct dpram_link_device *dpld, u16 val); - -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); - -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); - -static void handle_cp_crash(struct dpram_link_device *dpld); -static int trigger_force_cp_crash(struct dpram_link_device *dpld); -static void dpram_dump_memory(struct link_device *ld, char *buff); - -/* -** Functions for debugging -*/ -static inline void log_dpram_status(struct dpram_link_device *dpld) -{ - pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " - "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", - dpld->ld.mc->name, - get_magic(dpld), get_access(dpld), - get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), - get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), - get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), - get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), - recv_intr(dpld)); -} - -static void set_dpram_map(struct dpram_link_device *dpld, - struct mif_irq_map *map) -{ - map->magic = get_magic(dpld); - map->access = get_access(dpld); - - map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); - map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); - map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); - map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); - map->raw_tx_in = get_tx_head(dpld, IPC_RAW); - map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); - map->raw_rx_in = get_rx_head(dpld, IPC_RAW); - map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); - - map->cp2ap = recv_intr(dpld); -} - -/* -** RXB (DPRAM RX buffer) functions -*/ -static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) -{ - struct dpram_rxb *rxb; - u8 *buff; - int i; - - rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); - if (!rxb) { - mif_info("ERR! kzalloc rxb fail\n"); - return NULL; - } - - buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); - if (!buff) { - mif_info("ERR! kzalloc buff fail\n"); - kfree(rxb); - return NULL; - } - - for (i = 0; i < count; i++) { - rxb[i].buff = buff; - rxb[i].size = size; - buff += size; - } - - return rxb; -} - -static inline unsigned rxbq_get_page_size(unsigned len) -{ - return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; -} - -static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) -{ - return (rxbq->in == rxbq->out) ? true : false; -} - -static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in < out) ? (out - in - 1) : (qsize + out - in - 1); -} +static void trigger_forced_cp_crash(struct dpram_link_device *dpld); -static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) +/** + * set_circ_pointer + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @dir: direction of communication (TX or RX) + * @ptr: type of the queue pointer (HEAD or TAIL) + * @addr: address of the queue pointer + * @val: value to be written to the queue pointer + * + * Writes a value to a pointer in a circular queue with verification. + */ +static inline void set_circ_pointer(struct dpram_link_device *dpld, int id, + int dir, int ptr, void __iomem *addr, u16 val) { - struct dpram_rxb *rxb = NULL; + struct link_device *ld = &dpld->ld; + int cnt = 0; + u16 saved = 0; - if (likely(rxbq_free_size(rxbq) > 0)) { - rxb = &rxbq->rxb[rxbq->in]; - rxbq->in++; - if (rxbq->in >= rxbq->size) - rxbq->in -= rxbq->size; - rxb->data = rxb->buff; - } + iowrite16(val, addr); - return rxb; -} + while (1) { + /* Check the value written to the address */ + saved = ioread16(addr); + if (likely(saved == val)) + break; -static inline int rxbq_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in >= out) ? (in - out) : (qsize - out + in); -} + cnt++; + mif_err("%s: ERR! %s_%s.%s saved(%d) != val(%d), count %d\n", + ld->name, get_dev_name(id), circ_dir(dir), + circ_ptr(ptr), saved, val, cnt); + if (cnt >= MAX_RETRY_CNT) { + trigger_forced_cp_crash(dpld); + break; + } -static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; + udelay(100); - if (likely(!rxbq_empty(rxbq))) { - rxb = &rxbq->rxb[rxbq->out]; - rxbq->out++; - if (rxbq->out >= rxbq->size) - rxbq->out -= rxbq->size; + /* Write the value again */ + iowrite16(val, addr); } - - return rxb; -} - -static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) -{ - rxb->len = len; - return rxb->data; } -static inline void rxb_clear(struct dpram_rxb *rxb) +/** + * recv_int2ap + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the CP-to-AP interrupt register in a DPRAM. + */ +static inline u16 recv_int2ap(struct dpram_link_device *dpld) { - rxb->data = NULL; - rxb->len = 0; + return ioread16(dpld->mbx2ap); } -/* -** DPRAM operations -*/ -static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), - unsigned long flag, const char *name, - struct dpram_link_device *dpld) +/** + * send_int2cp + * @dpld: pointer to an instance of dpram_link_device structure + * @mask: value to be written to the AP-to-CP interrupt register in a DPRAM + */ +static inline void send_int2cp(struct dpram_link_device *dpld, u16 mask) { - int ret; + struct idpram_pm_op *pm_op = dpld->pm_op; - ret = request_irq(irq, isr, flag, name, dpld); - if (ret) { - mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); - return ret; + if (pm_op && pm_op->int2cp_possible) { + if (!pm_op->int2cp_possible(dpld)) + return; } - ret = enable_irq_wake(irq); - if (ret) - mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); - - mif_info("%s (#%d) handler registered\n", name, irq); - - return 0; -} - -static inline void clear_intr(struct dpram_link_device *dpld) -{ - if (likely(dpld->dpctl->clear_intr)) - dpld->dpctl->clear_intr(); -} - -static inline u16 recv_intr(struct dpram_link_device *dpld) -{ - if (likely(dpld->dpctl->recv_intr)) - return dpld->dpctl->recv_intr(); - else - return ioread16(dpld->mbx2ap); + iowrite16(mask, dpld->mbx2cp); } -static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +/** + * read_int2cp + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the AP-to-CP interrupt register in a DPRAM. + */ +static inline u16 read_int2cp(struct dpram_link_device *dpld) { - if (likely(dpld->dpctl->send_intr)) - dpld->dpctl->send_intr(mask); - else - iowrite16(mask, dpld->mbx2cp); + return ioread16(dpld->mbx2cp); } +/** + * get_magic + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the "magic code" field in a DPRAM. + */ static inline u16 get_magic(struct dpram_link_device *dpld) { return ioread16(dpld->magic); } +/** + * set_magic + * @dpld: pointer to an instance of dpram_link_device structure + * @val: value to be written to the "magic code" field in a DPRAM + */ static inline void set_magic(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->magic); } +/** + * get_access + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the "access enable" field in a DPRAM. + */ static inline u16 get_access(struct dpram_link_device *dpld) { return ioread16(dpld->access); } +/** + * set_access + * @dpld: pointer to an instance of dpram_link_device structure + * @val: value to be written to the "access enable" field in a DPRAM + */ static inline void set_access(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->access); } -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +/** + * get_txq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_head(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->txq.head); } -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +/** + * get_txq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (out) pointer in a TX queue. + * + * It is useless for an AP to read a tail pointer in a TX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_txq_tail(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->txq.tail); } -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)in, dpld->dev[id]->txq.head); - - do { - /* Check head value written */ - val = ioread16(dpld->dev[id]->txq.head); - if (likely(val == in)) - return; - - mif_err("ERR: %s txq.head(%d) != in(%d)\n", - get_dev_name(id), val, in); - udelay(100); - - /* Write head value again */ - iowrite16((u16)in, dpld->dev[id]->txq.head); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)out, dpld->dev[id]->txq.tail); - - do { - /* Check tail value written */ - val = ioread16(dpld->dev[id]->txq.tail); - if (likely(val == out)) - return; - - mif_err("ERR: %s txq.tail(%d) != out(%d)\n", - get_dev_name(id), val, out); - udelay(100); - - /* Write tail value again */ - iowrite16((u16)out, dpld->dev[id]->txq.tail); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +/** + * get_txq_buff + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_buff(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->txq.buff; } -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +/** + * get_txq_buff_size + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_buff_size(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->txq.size; } -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +/** + * get_rxq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in an RX queue. + * + * It is useless for an AP to read a head pointer in an RX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_rxq_head(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->rxq.head); } -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +/** + * get_rxq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_tail(struct dpram_link_device *dpld, int id) { return ioread16(dpld->dev[id]->rxq.tail); } -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)in, dpld->dev[id]->rxq.head); - - do { - /* Check head value written */ - val = ioread16(dpld->dev[id]->rxq.head); - if (val == in) - return; - - mif_err("ERR: %s rxq.head(%d) != in(%d)\n", - get_dev_name(id), val, in); - udelay(100); - - /* Write head value again */ - iowrite16((u16)in, dpld->dev[id]->rxq.head); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) -{ - int cnt = 3; - u32 val = 0; - - iowrite16((u16)out, dpld->dev[id]->rxq.tail); - - do { - /* Check tail value written */ - val = ioread16(dpld->dev[id]->rxq.tail); - if (val == out) - return; - - mif_err("ERR: %s rxq.tail(%d) != out(%d)\n", - get_dev_name(id), val, out); - udelay(100); - - /* Write tail value again */ - iowrite16((u16)out, dpld->dev[id]->rxq.tail); - } while (cnt--); - - trigger_force_cp_crash(dpld); -} - -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +/** + * get_rxq_buff + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_buff(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->rxq.buff; } -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +/** + * get_rxq_buff_size + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_buff_size(struct dpram_link_device *dpld, int id) { return dpld->dev[id]->rxq.size; } -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) -{ - return dpld->dev[id]->mask_req_ack; -} - -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +/** + * set_txq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct dpram_link_device *dpld, int id, u32 in) { - return dpld->dev[id]->mask_res_ack; + set_circ_pointer(dpld, id, TX, HEAD, dpld->dev[id]->txq.head, in); } -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +/** + * set_txq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct dpram_link_device *dpld, int id, u32 out) { - return dpld->dev[id]->mask_send; + set_circ_pointer(dpld, id, TX, TAIL, dpld->dev[id]->txq.tail, out); } -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +/** + * set_rxq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct dpram_link_device *dpld, int id, u32 in) { - if (in >= size) - return false; - - if (out >= size) - return false; - - return true; + set_circ_pointer(dpld, id, RX, HEAD, dpld->dev[id]->rxq.head, in); } -/* Get free space in the TXQ as well as in & out pointers */ -static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +/** + * set_rxq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct dpram_link_device *dpld, int id, u32 out) { - struct link_device *ld = &dpld->ld; - int cnt = 3; - u32 head; - u32 tail; - int space; - - do { - head = get_tx_head(dpld, dev); - tail = get_tx_tail(dpld, dev); - - space = (head < tail) ? (tail - head - 1) : - (qsize + tail - head - 1); - mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", - ld->name, get_dev_name(dev), qsize, head, tail, space); - - if (dpram_circ_valid(qsize, head, tail)) { - *in = head; - *out = tail; - return space; - } - - mif_info("%s: CAUTION! <%pf> " - "%s_TXQ invalid (size:%d in:%d out:%d)\n", - ld->name, __builtin_return_address(0), - get_dev_name(dev), qsize, head, tail); - - udelay(100); - } while (cnt--); - - *in = 0; - *out = 0; - return -EINVAL; + set_circ_pointer(dpld, id, RX, TAIL, dpld->dev[id]->rxq.tail, out); } -static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +/** + * get_mask_req_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) { - set_tx_head(dpld, dev, 0); - set_tx_tail(dpld, dev, 0); - if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + return dpld->dev[id]->mask_req_ack; } -/* Get data size in the RXQ as well as in & out pointers */ -static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +/** + * get_mask_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) { - struct link_device *ld = &dpld->ld; - int cnt = 3; - u32 head; - u32 tail; - u32 rcvd; - - do { - head = get_rx_head(dpld, dev); - tail = get_rx_tail(dpld, dev); - if (head == tail) { - *in = head; - *out = tail; - return 0; - } - - rcvd = (head > tail) ? (head - tail) : (qsize - tail + head); - mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", - ld->name, get_dev_name(dev), qsize, head, tail, rcvd); - - if (dpram_circ_valid(qsize, head, tail)) { - *in = head; - *out = tail; - return rcvd; - } - - mif_info("%s: CAUTION! <%pf> " - "%s_RXQ invalid (size:%d in:%d out:%d)\n", - ld->name, __builtin_return_address(0), - get_dev_name(dev), qsize, head, tail); - - udelay(100); - } while (cnt--); - - *in = 0; - *out = 0; - return -EINVAL; + return dpld->dev[id]->mask_res_ack; } -static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +/** + * get_mask_send + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) { - set_rx_head(dpld, dev, 0); - set_rx_tail(dpld, dev, 0); - if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + return dpld->dev[id]->mask_send; } -/* -** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success -*/ -static int dpram_wake_up(struct dpram_link_device *dpld) +/** + * reset_txq_circ + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties a TXQ by resetting the head (in) pointer with the value in the tail + * (out) pointer. + */ +static inline void reset_txq_circ(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + u32 head = get_txq_head(dpld, dev); + u32 tail = get_txq_tail(dpld, dev); - if (!dpld->dpctl->wakeup) - return 0; - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n", - ld->name, __builtin_return_address(0)); - - if (dpld->dpctl->sleep) - dpld->dpctl->sleep(); - - udelay(10); - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail twice\n", - ld->name, __builtin_return_address(0)); - return -EACCES; - } - } + mif_info("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); - atomic_inc(&dpld->accessing); - return 0; + set_txq_head(dpld, dev, tail); } -static void dpram_allow_sleep(struct dpram_link_device *dpld) +/** + * reset_rxq_circ + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties an RXQ by resetting the tail (out) pointer with the value in the head + * (in) pointer. + */ +static inline void reset_rxq_circ(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + u32 head = get_rxq_head(dpld, dev); + u32 tail = get_rxq_tail(dpld, dev); - if (!dpld->dpctl->sleep) - return; + mif_info("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); - if (atomic_dec_return(&dpld->accessing) <= 0) { - dpld->dpctl->sleep(); - atomic_set(&dpld->accessing, 0); - mif_debug("%s: DPRAM sleep possible\n", ld->name); - } + set_rxq_tail(dpld, dev, head); } -static int dpram_check_access(struct dpram_link_device *dpld) +/** + * check_magic_access + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns 0 if the "magic code" and "access enable" values are valid, otherwise + * returns -EACCES. + */ +static int check_magic_access(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; int i; u16 magic = get_magic(dpld); u16 access = get_access(dpld); + /* Returns 0 if the "magic code" and "access enable" are valid */ if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; + /* Retry up to 100 times with 100 us delay per each retry */ for (i = 1; i <= 100; i++) { - mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n", + mif_info("%s: magic:%X access:%X -> retry:%d\n", ld->name, magic, access, i); udelay(100); @@ -592,357 +417,313 @@ static int dpram_check_access(struct dpram_link_device *dpld) return -EACCES; } -static bool dpram_ipc_active(struct dpram_link_device *dpld) +/** + * ipc_active + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns whether or not IPC via the dpram_link_device instance is possible. + */ +static bool ipc_active(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { - mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n", - ld->name, __builtin_return_address(0)); + mif_err("%s: ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); return false; } - if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pf> dpram_check_access fail\n", - ld->name, __builtin_return_address(0)); + /* Check "magic code" and "access enable" values */ + if (check_magic_access(dpld) < 0) { + mif_err("%s: ERR! check_magic_access fail\n", + ld->name, CALLER); return false; } return true; } -static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 in, u32 out, struct sk_buff *skb) -{ - struct link_device *ld = &dpld->ld; - u8 __iomem *buff = get_tx_buff(dpld, dev); - u8 *src = skb->data; - u32 len = skb->len; - u32 inp; - struct mif_irq_map map; - - if (in < out) { - /* +++++++++ in ---------- out ++++++++++ */ - memcpy((buff + in), src, len); - } else { - /* ------ out +++++++++++ in ------------ */ - u32 space = qsize - in; +/** + * get_dpram_status + * @dpld: pointer to an instance of dpram_link_device structure + * @dir: direction of communication (TX or RX) + * @stat: pointer to an instance of mem_status structure + * + * Takes a snapshot of the current status of a DPRAM. + */ +static void get_dpram_status(struct dpram_link_device *dpld, + enum circ_dir_type dir, struct mem_status *stat) +{ +#ifdef DEBUG_MODEM_IF + getnstimeofday(&stat->ts); +#endif + + stat->dir = dir; + stat->magic = get_magic(dpld); + stat->access = get_access(dpld); + stat->head[IPC_FMT][TX] = get_txq_head(dpld, IPC_FMT); + stat->tail[IPC_FMT][TX] = get_txq_tail(dpld, IPC_FMT); + stat->head[IPC_FMT][RX] = get_rxq_head(dpld, IPC_FMT); + stat->tail[IPC_FMT][RX] = get_rxq_tail(dpld, IPC_FMT); + stat->head[IPC_RAW][TX] = get_txq_head(dpld, IPC_RAW); + stat->tail[IPC_RAW][TX] = get_txq_tail(dpld, IPC_RAW); + stat->head[IPC_RAW][RX] = get_rxq_head(dpld, IPC_RAW); + stat->tail[IPC_RAW][RX] = get_rxq_tail(dpld, IPC_RAW); + stat->int2ap = recv_int2ap(dpld); + stat->int2cp = read_int2cp(dpld); +} + +#if 0 +/** + * save_ipc_trace_work + * @work: pointer to an instance of work_struct structure + * + * Performs actual file operation for saving RX IPC trace. + */ +static void save_ipc_trace_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct trace_data_queue *trq; + struct trace_data *trd; + struct circ_status *stat; + struct file *fp; + struct timespec *ts; + int dev; + u8 *dump; + int rcvd; + u8 *buff; + char *path; + struct utc_time utc; - /* 1) in -> buffer end */ - memcpy((buff + in), src, ((len > space) ? space : len)); + dpld = container_of(work, struct dpram_link_device, trace_dwork.work); + ld = &dpld->ld; + trq = &dpld->trace_list; + path = dpld->trace_path; - /* 2) buffer start -> out */ - if (len > space) - memcpy(buff, (src + space), (len - space)); + buff = kzalloc(dpld->size << 3, GFP_KERNEL); + if (!buff) { + while (1) { + trd = trq_get_data_slot(trq); + if (!trd) + break; + + ts = &trd->ts; + dev = trd->dev; + stat = &trd->circ_stat; + dump = trd->data; + rcvd = trd->size; + print_ipc_trace(ld, dev, stat, ts, dump, rcvd); + + kfree(dump); + } + return; } - /* update new in pointer */ - inp = in + len; - if (inp >= qsize) - inp -= qsize; - set_tx_head(dpld, dev, inp); + while (1) { + trd = trq_get_data_slot(trq); + if (!trd) + break; + + ts = &trd->ts; + dev = trd->dev; + stat = &trd->circ_stat; + dump = trd->data; + rcvd = trd->size; + + ts2utc(ts, &utc); + snprintf(path, MIF_MAX_PATH_LEN, + "%s/%s_%s_%d%02d%02d-%02d%02d%02d.lst", + MIF_LOG_DIR, ld->name, get_dev_name(dev), + utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec); + + fp = mif_open_file(path); + if (fp) { + int len; + + snprintf(buff, MIF_MAX_PATH_LEN, + "[%d-%02d-%02d %02d:%02d:%02d.%03d] " + "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", + utc.year, utc.mon, utc.day, utc.hour, utc.min, + utc.sec, utc.msec, ld->name, get_dev_name(dev), + stat->in, stat->out, stat->size); + len = strlen(buff); + mif_dump2format4(dump, rcvd, (buff + len), NULL); + strcat(buff, "\n"); + len = strlen(buff); + + mif_save_file(fp, buff, len); + + memset(buff, 0, len); + mif_close_file(fp); + } else { + mif_err("%s: %s open fail\n", ld->name, path); + print_ipc_trace(ld, dev, stat, ts, dump, rcvd); + } - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); - mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); + kfree(dump); } - if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { - mif_err("%s: memcmp16_to_io fail\n", ld->name); - trigger_force_cp_crash(dpld); - } + kfree(buff); } +#endif -static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) +/** + * set_dpram_map + * @dpld: pointer to an instance of dpram_link_device structure + * @map: pointer to an instance of mif_irq_map structure + * + * Sets variables in an mif_irq_map instance as current DPRAM status for IPC + * logging. + */ +static void set_dpram_map(struct dpram_link_device *dpld, + struct mif_irq_map *map) { - struct link_device *ld = &dpld->ld; - struct sk_buff_head *txq = ld->skb_txq[dev]; - struct sk_buff *skb; - unsigned long int flags; - u32 qsize = get_tx_buff_size(dpld, dev); - u32 in; - u32 out; - int space; - int copied = 0; - - spin_lock_irqsave(&dpld->tx_lock[dev], flags); - - while (1) { - space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); - if (unlikely(space < 0)) { - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - dpram_reset_tx_circ(dpld, dev); - return space; - } - - skb = skb_dequeue(txq); - if (unlikely(!skb)) - break; - - if (unlikely(space < skb->len)) { - atomic_set(&dpld->res_required[dev], 1); - skb_queue_head(txq, skb); - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - mif_info("%s: %s " - "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", - ld->name, get_dev_name(dev), - qsize, in, out, space, skb->len); - return -ENOSPC; - } - - /* TX if there is enough room in the queue */ - dpram_ipc_write(dpld, dev, qsize, in, out, skb); - copied += skb->len; - dev_kfree_skb_any(skb); - } + map->magic = get_magic(dpld); + map->access = get_access(dpld); - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + map->fmt_tx_in = get_txq_head(dpld, IPC_FMT); + map->fmt_tx_out = get_txq_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rxq_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rxq_tail(dpld, IPC_FMT); + map->raw_tx_in = get_txq_head(dpld, IPC_RAW); + map->raw_tx_out = get_txq_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rxq_head(dpld, IPC_RAW); + map->raw_rx_out = get_rxq_tail(dpld, IPC_RAW); - return copied; + map->cp2ap = recv_int2ap(dpld); } -static void dpram_ipc_rx_task(unsigned long data) +/** + * dpram_wake_up + * @dpld: pointer to an instance of dpram_link_device structure + * + * Wakes up a DPRAM if it can sleep and increases the "accessing" counter in the + * dpram_link_device instance. + * + * CAUTION!!! dpram_allow_sleep() MUST be invoked after dpram_wake_up() success + * to decrease the "accessing" counter. + */ +static int dpram_wake_up(struct dpram_link_device *dpld) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; struct link_device *ld = &dpld->ld; - struct io_device *iod; - struct dpram_rxb *rxb; - unsigned qlen; - int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { - iod = dpld->iod[i]; - qlen = rxbq_size(&dpld->rxbq[i]); - while (qlen > 0) { - rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); - iod->recv(iod, ld, rxb->data, rxb->len); - rxb_clear(rxb); - qlen--; - } - } -} + if (unlikely(!dpld->need_wake_up)) + return 0; -static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, - u8 __iomem *src, u32 out, u32 len, u32 qsize) -{ - if ((out + len) <= qsize) { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - memcpy(dst, (src + out), len); - } else { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - unsigned len1 = qsize - out; + if (dpld->ext_op->wakeup(dpld) < 0) { + mif_err("%s: ERR! wakeup fail\n", + ld->name, CALLER); + return -EACCES; + } - /* 1) out -> buffer end */ - memcpy(dst, (src + out), len1); + atomic_inc(&dpld->accessing); - /* 2) buffer start -> in */ - dst += len1; - memcpy(dst, src, (len - len1)); - } + return 0; } -/* - ret < 0 : error - ret == 0 : no data - ret > 0 : valid data -*/ -static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) +/** + * dpram_allow_sleep + * @dpld: pointer to an instance of dpram_link_device structure + * + * Decreases the "accessing" counter in the dpram_link_device instance if it can + * sleep and allows the DPRAM to sleep only if the value of "accessing" counter + * is less than or equal to 0. + * + * MUST be invoked after dpram_wake_up() success to decrease the "accessing" + * counter. + */ +static void dpram_allow_sleep(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - struct dpram_rxb *rxb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); - u32 in; - u32 out; - u32 rcvd; - struct mif_irq_map map; - - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); - if (rcvd <= 0) - return rcvd; - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } + if (unlikely(!dpld->need_wake_up)) + return; - /* Allocate an rxb */ - rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); - if (!rxb) { - mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", - ld->name, get_dev_name(dev)); - return -ENOMEM; + if (atomic_dec_return(&dpld->accessing) <= 0) { + dpld->ext_op->sleep(dpld); + atomic_set(&dpld->accessing, 0); + mif_debug("%s: DPRAM sleep possible\n", ld->name); } - - /* Read data from each DPRAM buffer */ - dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); - - /* Calculate and set new out */ - out += rcvd; - if (out >= qsize) - out -= qsize; - set_rx_tail(dpld, dev, out); - - return rcvd; } -/* - ret < 0 : error - ret == 0 : no data - ret > 0 : valid data -*/ -static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) +static int capture_dpram_snapshot(struct link_device *ld, struct io_device *iod) { - struct link_device *ld = &dpld->ld; - struct io_device *iod = dpld->iod[dev]; + struct dpram_link_device *dpld = to_dpram_link_device(ld); struct sk_buff *skb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); - u32 in; - u32 out; - u32 rcvd; - int rest; - u8 *frm; - u8 *dst; - unsigned int len; - unsigned int pad; - unsigned int tot; - struct mif_irq_map map; - - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); - if (rcvd <= 0) - return rcvd; - - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } - - rest = rcvd; - while (rest > 0) { - frm = src + out; - if (unlikely(!sipc5_start_valid(frm[0]))) { - mif_err("%s: ERR! %s invalid start 0x%02X\n", - ld->name, get_dev_name(dev), frm[0]); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } + u32 size = dpld->size; + u32 copied = 0; + u8 *dump; - len = sipc5_get_frame_sz16(frm); - if (unlikely(len > rest)) { - mif_err("%s: ERR! %s len %d > rest %d\n", - ld->name, get_dev_name(dev), len, rest); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } + dpram_wake_up(dpld); + dump = capture_mem_dump(ld, dpld->base, dpld->size); + dpram_allow_sleep(dpld); - pad = sipc5_calc_padding_size(len); - tot = len + pad; + if (!dump) + return -ENOMEM; - /* Allocate an skb */ - skb = dev_alloc_skb(tot); + while (copied < size) { + skb = alloc_skb(MAX_DUMP_SKB_SIZE, GFP_ATOMIC); if (!skb) { - mif_err("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); + mif_err("ERR! alloc_skb fail\n"); + kfree(dump); return -ENOMEM; } - /* Read data from each DPRAM buffer */ - dst = skb_put(skb, tot); - dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); - skb_trim(skb, len); - iod->recv_skb(iod, ld, skb); - - /* Calculate and set new out */ - rest -= tot; - out += tot; - if (out >= qsize) - out -= qsize; - } - - set_rx_tail(dpld, dev, out); - - return rcvd; -} - -static void non_command_handler(struct dpram_link_device *dpld, u16 intr) -{ - struct link_device *ld = &dpld->ld; - int i = 0; - int ret = 0; - u16 tx_mask = 0; - - if (!dpram_ipc_active(dpld)) - return; - - /* Read data from DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (dpld->use_skb) - ret = dpram_ipc_recv_data_with_skb(dpld, i); - else - ret = dpram_ipc_recv_data_with_rxb(dpld, i); - if (ret < 0) - dpram_reset_rx_circ(dpld, i); - - /* Check and process REQ_ACK (at this time, in == out) */ - if (intr & get_mask_req_ack(dpld, i)) { - mif_debug("%s: send %s_RES_ACK\n", - ld->name, get_dev_name(i)); - tx_mask |= get_mask_res_ack(dpld, i); - } - } - - if (!dpld->use_skb) { - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); - } + skb_put(skb, MAX_DUMP_SKB_SIZE); + memcpy(skb->data, (dump + copied), MAX_DUMP_SKB_SIZE); + copied += MAX_DUMP_SKB_SIZE; - /* Try TX via DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (atomic_read(&dpld->res_required[i]) > 0) { - ret = dpram_try_ipc_tx(dpld, i); - if (ret > 0) { - atomic_set(&dpld->res_required[i], 0); - tx_mask |= get_mask_send(dpld, i); - } else if (ret == -ENOSPC) { - tx_mask |= get_mask_req_ack(dpld, i); - } - } + skb_queue_tail(&iod->sk_rx_q, skb); + wake_up(&iod->wq); } - if (tx_mask) { - send_intr(dpld, INT_NON_CMD(tx_mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); - } + kfree(dump); + return 0; } +/** + * handle_cp_crash + * @dpld: pointer to an instance of dpram_link_device structure + * + * Actual handler for the CRASH_EXIT command from a CP. + */ static void handle_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { - mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); + if (dpld->forced_cp_crash) + dpld->forced_cp_crash = false; + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < ld->max_ipc_dev; i++) skb_queue_purge(ld->skb_txq[i]); - } + /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); - - iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); if (iod) - iodevs_for_each(iod->msd, iodev_netif_stop, 0); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); } -static void handle_no_crash_ack(unsigned long arg) +/** + * handle_no_cp_crash_ack + * @arg: pointer to an instance of dpram_link_device structure + * + * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no + * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. + */ +static void handle_no_cp_crash_ack(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; struct link_device *ld = &dpld->ld; @@ -955,187 +736,363 @@ static void handle_no_crash_ack(unsigned long arg) handle_cp_crash(dpld); } -static int trigger_force_cp_crash(struct dpram_link_device *dpld) +/** + * trigger_forced_cp_crash + * @dpld: pointer to an instance of dpram_link_device structure + * + * Triggers an enforced CP crash. + */ +static void trigger_forced_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; +#ifdef DEBUG_MODEM_IF + struct trace_data *trd; + u8 *dump; + struct timespec ts; + getnstimeofday(&ts); +#endif if (ld->mode == LINK_MODE_ULOAD) { - mif_err("%s: CP crash is already in progress\n", ld->mc->name); - return 0; + mif_err("%s: ALREADY in progress\n", + ld->name, CALLER); + return; } ld->mode = LINK_MODE_ULOAD; - mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); + dpld->forced_cp_crash = true; + + disable_irq_nosync(dpld->irq); + + dpram_wake_up(dpld); + +#ifdef DEBUG_MODEM_IF + dump = capture_mem_dump(ld, dpld->base, dpld->size); + if (dump) { + trd = trq_get_free_slot(&dpld->trace_list); + memcpy(&trd->ts, &ts, sizeof(struct timespec)); + trd->dev = IPC_DEBUG; + trd->data = dump; + trd->size = dpld->size; + } +#endif + + enable_irq(dpld->irq); - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + mif_err("%s: \n", ld->name, CALLER); - send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + /* Send CRASH_EXIT command to a CP */ + send_int2cp(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack() will be executed. */ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)dpld); + handle_no_cp_crash_ack, (unsigned long)dpld); - return 0; + return; } -static int dpram_init_ipc(struct dpram_link_device *dpld) +/** + * ext_command_handler + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: extended DPRAM command from a CP + * + * Processes an extended command from a CP. + */ +static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) { struct link_device *ld = &dpld->ld; - int i; - - if (ld->mode == LINK_MODE_IPC && - get_magic(dpld) == DPRAM_MAGIC_CODE && - get_access(dpld) == 1) - mif_info("%s: IPC already initialized\n", ld->name); + u16 resp; - /* Clear pointers in every circular queue */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - set_tx_head(dpld, i, 0); - set_tx_tail(dpld, i, 0); - set_rx_head(dpld, i, 0); - set_rx_tail(dpld, i, 0); - } + switch (EXT_CMD_MASK(cmd)) { + case EXT_CMD_SET_SPEED_LOW: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_LOW); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); + send_int2cp(dpld, resp); + } + break; - /* Initialize variables for efficient TX/RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->iod[i] = link_get_iod_with_format(ld, i); - dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + case EXT_CMD_SET_SPEED_MID: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_MID); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); + send_int2cp(dpld, resp); + } + break; - if (dpld->iod[IPC_RAW]->recv_skb) - dpld->use_skb = true; + case EXT_CMD_SET_SPEED_HIGH: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); + send_int2cp(dpld, resp); + } + break; - for (i = 0; i < dpld->max_ipc_dev; i++) { - spin_lock_init(&dpld->tx_lock[i]); - atomic_set(&dpld->res_required[i], 0); - skb_queue_purge(&dpld->skb_rxq[i]); + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + break; } +} - /* Enable IPC */ - atomic_set(&dpld->accessing, 0); - - set_magic(dpld, DPRAM_MAGIC_CODE); - set_access(dpld, 1); - if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) - return -EACCES; +/** + * udl_command_handler + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: DPRAM upload/download command from a CP + * + * Processes a command for upload/download from a CP. + */ +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; - ld->mode = LINK_MODE_IPC; + if (cmd & UDL_RESULT_FAIL) { + mif_err("%s: ERR! command fail (0x%04X)\n", ld->name, cmd); + return; + } - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_err("%s: [CP->AP] CMD_DL_READY (0x%04X)\n", ld->name, cmd); +#ifdef CONFIG_CDMA_MODEM_CBP72 + mif_err("%s: [AP->CP] CMD_DL_START_REQ (0x%04X)\n", + ld->name, CMD_DL_START_REQ); + send_int2cp(dpld, CMD_DL_START_REQ); +#else + complete(&dpld->udl_cmpl); +#endif + break; - return 0; + default: + complete(&dpld->udl_cmpl); + } } +/** + * cmd_req_active_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the REQ_ACTIVE command from a CP. + */ static void cmd_req_active_handler(struct dpram_link_device *dpld) { - send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_int2cp(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); } +/** + * cmd_crash_reset_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the CRASH_RESET command from a CP. + */ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; + int i; ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < ld->max_ipc_dev; i++) + skb_queue_purge(ld->skb_txq[i]); + mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); + /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); iod->modem_state_changed(iod, STATE_CRASH_RESET); + /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_RESET); } +/** + * cmd_crash_exit_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the CRASH_EXIT command from a CP. + */ static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - u32 size = dpld->dpctl->dp_size; - char *dpram_buff = NULL; +#ifdef DEBUG_MODEM_IF + struct trace_data *trd; + u8 *dump; + struct timespec ts; + getnstimeofday(&ts); +#endif ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); - mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + del_timer(&dpld->crash_ack_timer); - dpram_buff = kzalloc(size + (MAX_MIF_SEPA_SIZE * 2), GFP_ATOMIC); - if (!dpram_buff) { - mif_err("DPRAM dump failed!!\n"); - } else { - memset(dpram_buff, 0, size + (MAX_MIF_SEPA_SIZE * 2)); - memcpy(dpram_buff, MIF_SEPARATOR_DPRAM, MAX_MIF_SEPA_SIZE); - memcpy(dpram_buff + MAX_MIF_SEPA_SIZE, &size, sizeof(u32)); - dpram_buff += (MAX_MIF_SEPA_SIZE * 2); - dpram_dump_memory(ld, dpram_buff); + dpram_wake_up(dpld); + +#ifdef DEBUG_MODEM_IF + if (!dpld->forced_cp_crash) { + dump = capture_mem_dump(ld, dpld->base, dpld->size); + if (dump) { + trd = trq_get_free_slot(&dpld->trace_list); + memcpy(&trd->ts, &ts, sizeof(struct timespec)); + trd->dev = IPC_DEBUG; + trd->data = dump; + trd->size = dpld->size; + } } - - del_timer(&dpld->crash_ack_timer); +#endif if (dpld->ext_op && dpld->ext_op->crash_log) dpld->ext_op->crash_log(dpld); + mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); + handle_cp_crash(dpld); } -static void cmd_phone_start_handler(struct dpram_link_device *dpld) +/** + * init_dpram_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * + * Initializes IPC via DPRAM. + */ +static int init_dpram_ipc(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - struct io_device *iod = NULL; - - mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); + int i; - dpram_init_ipc(dpld); + if (ld->mode == LINK_MODE_IPC && + get_magic(dpld) == DPRAM_MAGIC_CODE && + get_access(dpld) == 1) + mif_info("%s: IPC already initialized\n", ld->name); - iod = link_get_iod_with_format(ld, IPC_FMT); - if (!iod) { - mif_info("%s: ERR! no iod\n", ld->name); - return; + /* Clear pointers in every circular queue */ + for (i = 0; i < ld->max_ipc_dev; i++) { + set_txq_head(dpld, i, 0); + set_txq_tail(dpld, i, 0); + set_rxq_head(dpld, i, 0); + set_rxq_tail(dpld, i, 0); } - if (dpld->ext_op && dpld->ext_op->cp_start_handler) - dpld->ext_op->cp_start_handler(dpld); + /* Initialize variables for efficient TX/RX processing */ + for (i = 0; i < ld->max_ipc_dev; i++) + dpld->iod[i] = link_get_iod_with_format(ld, i); + dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - if (ld->mc->phone_state != STATE_ONLINE) { - mif_info("%s: phone_state: %d -> ONLINE\n", - ld->name, ld->mc->phone_state); - iod->modem_state_changed(iod, STATE_ONLINE); - } + /* Initialize variables for TX flow control */ + for (i = 0; i < ld->max_ipc_dev; i++) + atomic_set(&dpld->res_required[i], 0); + + /* Enable IPC */ + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); + + atomic_set(&dpld->accessing, 0); + + set_magic(dpld, DPRAM_MAGIC_CODE); + set_access(dpld, 1); + if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + return -EACCES; - mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); + ld->mode = LINK_MODE_IPC; + + return 0; } -static void command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * reset_dpram_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * + * Reset DPRAM with IPC map. + */ +static void reset_dpram_ipc(struct dpram_link_device *dpld) { + int i; struct link_device *ld = &dpld->ld; - switch (INT_CMD_MASK(cmd)) { - case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(dpld); - break; + dpld->set_access(dpld, 0); - case INT_CMD_CRASH_RESET: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_reset_handler(dpld); + /* Clear pointers in every circular queue */ + for (i = 0; i < ld->max_ipc_dev; i++) { + dpld->set_txq_head(dpld, i, 0); + dpld->set_txq_tail(dpld, i, 0); + dpld->set_rxq_head(dpld, i, 0); + dpld->set_rxq_tail(dpld, i, 0); + } + + dpld->set_magic(dpld, DPRAM_MAGIC_CODE); + dpld->set_access(dpld, 1); +} + +/** + * cmd_phone_start_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the PHONE_START command from a CP. + */ +static void cmd_phone_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("%s: ERR! no iod\n", ld->name); + return; + } + + init_dpram_ipc(dpld); + + iod->modem_state_changed(iod, STATE_ONLINE); + + if (dpld->ext_op && dpld->ext_op->cp_start_handler) { + dpld->ext_op->cp_start_handler(dpld); + } else { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(dpld, INT_CMD(INT_CMD_INIT_END)); + } +} + +/** + * cmd_handler: processes a DPRAM command from a CP + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: DPRAM command from a CP + */ +static void cmd_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; + + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + cmd_req_active_handler(dpld); + break; + + case INT_CMD_CRASH_RESET: + dpld->init_status = DPRAM_INIT_STATE_NONE; + cmd_crash_reset_handler(dpld); break; case INT_CMD_CRASH_EXIT: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + dpld->init_status = DPRAM_INIT_STATE_NONE; cmd_crash_exit_handler(dpld); break; case INT_CMD_PHONE_START: - dpld->dpram_init_status = DPRAM_INIT_STATE_READY; + dpld->init_status = DPRAM_INIT_STATE_READY; cmd_phone_start_handler(dpld); - complete_all(&dpld->dpram_init_cmd); + complete_all(&ld->init_cmpl); break; case INT_CMD_NV_REBUILDING: @@ -1143,14 +1100,14 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&dpld->modem_pif_init_done); + complete_all(&ld->pif_cmpl); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_PWR_OFF: + case INT_CMD_NORMAL_POWER_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1165,213 +1122,1143 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) } } -static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * ipc_rx_work + * @work: pointer to an instance of the work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void ipc_rx_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct io_device *iod; + struct sk_buff *skb; + int i; + + dpld = container_of(work, struct dpram_link_device, rx_dwork.work); + ld = &dpld->ld; + + for (i = 0; i < ld->max_ipc_dev; i++) { + iod = dpld->iod[i]; + while (1) { + skb = skb_dequeue(ld->skb_rxq[i]); + if (!skb) + break; + + if (iod->recv_skb) { + iod->recv_skb(iod, ld, skb); + } else { + iod->recv(iod, ld, skb->data, skb->len); + dev_kfree_skb_any(skb); + } + } + } +} + +/** + * get_rxq_rcvd + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @dcst: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a RXQ, size of the buffer, in & out + * pointer values, size of received data} into the 'stat' instance. + * + * Returns an error code. + */ +static int get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + struct mem_status *mst, struct circ_status *dcst) { struct link_device *ld = &dpld->ld; - u16 resp; - switch (EXT_CMD_MASK(cmd)) { - case EXT_CMD_SET_SPEED_LOW: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); - send_intr(dpld, resp); + dcst->buff = get_rxq_buff(dpld, dev); + dcst->qsize = get_rxq_buff_size(dpld, dev); + dcst->in = mst->head[dev][RX]; + dcst->out = mst->tail[dev][RX]; + dcst->size = circ_get_usage(dcst->qsize, dcst->in, dcst->out); + + if (circ_valid(dcst->qsize, dcst->in, dcst->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), dcst->qsize, dcst->in, + dcst->out, dcst->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), dcst->qsize, dcst->in, + dcst->out); + return -EIO; + } +} + +/** + * rx_sipc4_frames + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a bottom half (e.g. ipc_rx_task) that will invoke the recv method in + * the io_device instance. + */ +static int rx_sipc4_frames(struct dpram_link_device *dpld, int dev, + struct mem_status *mst) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff *skb; + u8 *dst; + struct circ_status dcst; + int rcvd; + + rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); + if (unlikely(rcvd < 0)) { +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(dpld); +#endif + goto exit; + } + rcvd = dcst.size; + + /* Allocate an skb */ + skb = dev_alloc_skb(rcvd); + if (!skb) { + mif_info("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + rcvd = -ENOMEM; + goto exit; + } + + /* Read data from the RXQ */ + dst = skb_put(skb, rcvd); + circ_read16_from_io(dst, dcst.buff, dcst.qsize, dcst.out, rcvd); + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + +exit: + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(dpld, dev, dcst.in); + + return rcvd; +} + +/** + * rx_sipc5_frames + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_sipc5_frames(struct dpram_link_device *dpld, int dev, + struct mem_status *mst) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff *skb; + /** + * variables for the status of the circular queue + */ + u8 __iomem *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + struct circ_status dcst; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int idx; /* index to the start of current frame */ + u8 *frm; /* pointer to current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + /** + * variables for debug logging + */ + struct mif_irq_map map; + + /* Get data size in the RXQ and in/out pointer values */ + rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); + if (unlikely(rcvd < 0)) { + mif_err("%s: ERR! rcvd %d < 0\n", ld->name, rcvd); + goto exit; + } + + rcvd = dcst.size; + src = dcst.buff; + qsize = dcst.qsize; + idx = dcst.out; + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + +#if 0 + skb = dev_alloc_skb(rcvd); + + /* + ** If there is enough free space for an skb to store received + ** data at once, + */ + if (skb) { + /* Read all data from the RXQ to the skb */ + dst = skb_put(skb, rcvd); + if (unlikely(dpld->strict_io_access)) + circ_read16_from_io(dst, src, qsize, idx, rcvd); + else + circ_read(dst, src, qsize, idx, rcvd); + +#ifdef DEBUG_MODEM_IF + /* Verify data copied to the skb */ + if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + rcvd = -EIO; + goto exit; } - break; +#endif - case EXT_CMD_SET_SPEED_MID: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_MID); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); - send_intr(dpld, resp); + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + + goto exit; + } + + /* + ** If there was no enough space to store received data at once, + */ +#endif + + rest = rcvd; + while (rest > 0) { + /* Calculate the start of an SIPC5 frame */ + frm = src + idx; + + /* Copy the header in the frame to the header buffer */ + if (unlikely(dpld->strict_io_access)) + memcpy16_from_io(hdr, frm, SIPC5_MIN_HEADER_SIZE); + else + memcpy(hdr, frm, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: BAD CONFIG", + ld->mc->name); + mif_err("%s: ERR! %s INVALID config 0x%02X\n", + ld->name, get_dev_name(dev), hdr[0]); + pr_ipc(1, str, hdr, 4); + rcvd = -EBADMSG; + goto exit; } - break; - case EXT_CMD_SET_SPEED_HIGH: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); - send_intr(dpld, resp); + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: BAD LENGTH", + ld->mc->name); + mif_err("%s: ERR! %s tot %d > rest %d\n", + ld->name, get_dev_name(dev), tot, rest); + pr_ipc(1, str, hdr, 4); + rcvd = -EBADMSG; +#if defined(CONFIG_MACH_C1_KOR_SKT) || defined(CONFIG_MACH_C1_KOR_KT) || defined(CONFIG_MACH_C1_KOR_LGT) + return rcvd; +#else + goto exit; +#endif } - break; - default: - mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); - break; + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + rcvd = -ENOMEM; + goto exit; + } + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + if (unlikely(dpld->strict_io_access)) + circ_read16_from_io(dst, src, qsize, idx, tot); + else + circ_read(dst, src, qsize, idx, tot); + +#ifdef DEBUG_MODEM_IF + /* Take a log for debugging */ + if (unlikely(dev == IPC_FMT)) { + size_t len = (skb->len > 32) ? 32 : skb->len; + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: CP2MIF", + ld->mc->name); + pr_ipc(0, str, skb->data, len); + } +#endif + +#ifdef DEBUG_MODEM_IF + /* Verify data copied to the skb */ + if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + rcvd = -EIO; + goto exit; + } +#endif + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + + /* Calculate new idx value */ + rest -= tot; + idx += tot; + if (unlikely(idx >= qsize)) + idx -= qsize; } + +exit: +#ifdef DEBUG_MODEM_IF + if (rcvd < 0) + trigger_forced_cp_crash(dpld); +#endif + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(dpld, dev, dcst.in); + + return rcvd; } -static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * msg_handler: receives IPC messages from every RXQ + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every DPRAM RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void msg_handler(struct dpram_link_device *dpld, struct mem_status *stat) { struct link_device *ld = &dpld->ld; + int i = 0; + int ret = 0; + u16 mask = 0; + u16 intr = stat->int2ap; - if (cmd & UDL_RESULT_FAIL) { - mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); + if (!ipc_active(dpld)) return; + + /* Read data from DPRAM */ + for (i = 0; i < ld->max_ipc_dev; i++) { + /* Invoke an RX function only when there is data in the RXQ */ + if (unlikely(stat->head[i][RX] == stat->tail[i][RX])) { + mif_debug("%s: %s_RXQ is empty\n", + ld->name, get_dev_name(i)); + } else { + if (unlikely(ld->ipc_version < SIPC_VER_50)) + ret = rx_sipc4_frames(dpld, i, stat); + else + ret = rx_sipc5_frames(dpld, i, stat); + if (ret < 0) + reset_rxq_circ(dpld, i); + } } - switch (UDL_CMD_MASK(cmd)) { - case UDL_CMD_RECV_READY: - mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); - send_intr(dpld, CMD_IMG_START_REQ); - break; - default: - complete_all(&dpld->udl_cmd_complete); + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &dpld->rx_dwork, 0); + + /* Check and process REQ_ACK (at this time, in == out) */ + if (unlikely(intr & INT_MASK_REQ_ACK_SET)) { + for (i = 0; i < ld->max_ipc_dev; i++) { + if (intr & get_mask_req_ack(dpld, i)) { + mif_debug("%s: set %s_RES_ACK\n", + ld->name, get_dev_name(i)); + mask |= get_mask_res_ack(dpld, i); + } + } + + send_int2cp(dpld, INT_NON_CMD(mask)); + } + + /* Check and process RES_ACK */ + if (unlikely(intr & INT_MASK_RES_ACK_SET)) { + for (i = 0; i < ld->max_ipc_dev; i++) { + if (intr & get_mask_res_ack(dpld, i)) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv %s_RES_ACK\n", + ld->name, get_dev_name(i)); + print_circ_status(ld, i, stat); +#endif + complete(&dpld->req_ack_cmpl[i]); + } + } + } +} + +/** + * cmd_msg_handler: processes a DPRAM command or receives IPC messages + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * Invokes cmd_handler for a DPRAM command or msg_handler for IPC messages. + */ +static inline void cmd_msg_handler(struct dpram_link_device *dpld, + struct mem_status *stat) +{ + struct dpram_ext_op *ext_op = dpld->ext_op; + struct mem_status *mst = msq_get_free_slot(&dpld->stat_list); + u16 intr = stat->int2ap; + + memcpy(mst, stat, sizeof(struct mem_status)); + + if (unlikely(INT_CMD_VALID(intr))) { + if (ext_op && ext_op->cmd_handler) + ext_op->cmd_handler(dpld, intr); + else + cmd_handler(dpld, intr); + } else { + msg_handler(dpld, stat); } } -static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr) +/** + * intr_handler: processes an interrupt from a CP + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * Call flow for normal interrupt handling: + * cmd_msg_handler -> cmd_handler -> cmd_xxx_handler + * cmd_msg_handler -> msg_handler -> rx_sipc5_frames -> ... + */ +static inline void intr_handler(struct dpram_link_device *dpld, + struct mem_status *stat) { - if (unlikely(INT_CMD_VALID(intr))) - command_handler(dpld, intr); + char *name = dpld->ld.name; + u16 intr = stat->int2ap; + + if (unlikely(intr == INT_POWERSAFE_FAIL)) { + mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); + return; + } + + if (unlikely(EXT_UDL_CMD(intr))) { + if (likely(EXT_INT_VALID(intr))) { + if (UDL_CMD_VALID(intr)) + udl_command_handler(dpld, intr); + else if (EXT_CMD_VALID(intr)) + ext_command_handler(dpld, intr); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", + name, intr); + } else { + mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); + } + + return; + } + + if (likely(INT_VALID(intr))) + cmd_msg_handler(dpld, stat); else - non_command_handler(dpld, intr); + mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); +} + +/** + * ap_idpram_irq_handler: interrupt handler for an internal DPRAM in an AP + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ap_idpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + struct modemlink_dpram_data *dpram = dpld->dpram; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; + + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + if (likely(dpram->clear_int2ap)) + dpram->clear_int2ap(); + + return IRQ_HANDLED; } -static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr) -{ - char *name = dpld->ld.name; +/** + * cp_idpram_irq_handler: interrupt handler for an internal DPRAM in a CP + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Wakes up the DPRAM + * 2) Reads the interrupt value + * 3) Performs interrupt handling + * 4) Clears the interrupt port (port = memory or register) + * 5) Allows the DPRAM to sleep + */ +static irqreturn_t cp_idpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + struct dpram_ext_op *ext_op = dpld->ext_op; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { + mif_err("%s: ERR! ld->mode == LINK_MODE_OFFLINE\n", ld->name); + get_dpram_status(dpld, RX, &stat); +#ifdef DEBUG_MODEM_IF + print_mem_status(ld, &stat); +#endif + return IRQ_HANDLED; + } + + if (dpram_wake_up(dpld) < 0) { + trigger_forced_cp_crash(dpld); + return IRQ_HANDLED; + } + + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + if (likely(ext_op && ext_op->clear_int2ap)) + ext_op->clear_int2ap(dpld); + + dpram_allow_sleep(dpld); + + return IRQ_HANDLED; +} + +/** + * ext_dpram_irq_handler: interrupt handler for a normal external DPRAM + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ext_dpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; + + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + return IRQ_HANDLED; +} + +/** + * get_txq_space + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * OUT @stat: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of free space} into the 'stat' instance. + * + * Returns the size of free space in the buffer or an error code. + */ +static int get_txq_space(struct dpram_link_device *dpld, int dev, + struct circ_status *stat) +{ + struct link_device *ld = &dpld->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int space; + + while (1) { + qsize = get_txq_buff_size(dpld, dev); + head = get_txq_head(dpld, dev); + tail = get_txq_tail(dpld, dev); + space = circ_get_space(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + stat->buff = get_txq_buff(dpld, dev); + stat->qsize = qsize; + stat->in = head; + stat->out = tail; + stat->size = space; + + return space; +} + +/** + * write_ipc_to_txq + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @stat: pointer to an instance of circ_status structure + * @skb: pointer to an instance of sk_buff structure + * + * Must be invoked only when there is enough space in the TXQ. + */ +static void write_ipc_to_txq(struct dpram_link_device *dpld, int dev, + struct circ_status *stat, struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *buff = stat->buff; + u32 qsize = stat->qsize; + u32 in = stat->in; + u8 *src = skb->data; + u32 len = skb->len; + struct mif_irq_map map; + + /* Write data to the TXQ */ + if (unlikely(dpld->strict_io_access)) + circ_write16_to_io(buff, src, qsize, in, len); + else + circ_write(buff, src, qsize, in, len); + + /* Update new head (in) pointer */ + set_txq_head(dpld, dev, circ_new_pointer(qsize, in, len)); + + /* Take a log for debugging */ + if (dev == IPC_FMT) { +#ifdef DEBUG_MODEM_IF + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2CP", ld->mc->name); + pr_ipc(0, tag, src, (len > 32 ? 32 : len)); +#endif + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); + mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); + } + +#ifdef DEBUG_MODEM_IF + /* Verify data written to the TXQ */ + if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + trigger_forced_cp_crash(dpld); + } +#endif +} + +/** + * xmit_ipc_msg + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. + * + * Returns total length of IPC messages transmitted or an error code. + */ +static int xmit_ipc_msg(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long flags; + struct circ_status stat; + int space; + int copied = 0; + + /* Acquire the spin lock for a TXQ */ + spin_lock_irqsave(&dpld->tx_lock[dev], flags); + + while (1) { + /* Get the size of free space in the TXQ */ + space = get_txq_space(dpld, dev, &stat); + if (unlikely(space < 0)) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(dpld); +#endif + /* Empty out the TXQ */ + reset_txq_circ(dpld, dev); + copied = -EIO; + break; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + /* Check the free space size comparing with skb->len */ + if (unlikely(space < skb->len)) { +#ifdef DEBUG_MODEM_IF + struct mem_status mst; +#endif + /* Set res_required flag for the "dev" */ + atomic_set(&dpld->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_info("%s: NOSPC in %s_TXQ" + "{qsize:%u in:%u out:%u}, free:%u < len:%u\n", + ld->name, CALLER, get_dev_name(dev), + stat.qsize, stat.in, stat.out, space, skb->len); +#ifdef DEBUG_MODEM_IF + get_dpram_status(dpld, TX, &mst); + print_circ_status(ld, dev, &mst); +#endif + copied = -ENOSPC; + break; + } + + /* TX only when there is enough space in the TXQ */ + write_ipc_to_txq(dpld, dev, &stat, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + /* Release the spin lock */ + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + + return copied; +} + +/** + * wait_for_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Sends an REQ_ACK interrupt for @dev to CP. + * 2) Waits for the corresponding RES_ACK for @dev from CP. + * + * Returns the return value from wait_for_completion_interruptible_timeout(). + */ +static int wait_for_res_ack(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct completion *cmpl = &dpld->req_ack_cmpl[dev]; + unsigned long timeout = msecs_to_jiffies(dpld->res_ack_wait_timeout); + int ret; + u16 mask; + +#ifdef DEBUG_MODEM_IF + mif_info("%s: send %s_REQ_ACK\n", ld->name, get_dev_name(dev)); +#endif + + mask = get_mask_req_ack(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + + ret = wait_for_completion_interruptible_timeout(cmpl, timeout); + /* ret == 0 on timeout, ret < 0 if interrupted */ + if (ret < 0) { + mif_info("%s: %s: wait_for_completion interrupted! (ret %d)\n", + ld->name, get_dev_name(dev), ret); + goto exit; + } + + if (ret == 0) { + struct mem_status mst; + get_dpram_status(dpld, TX, &mst); + + mif_info("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", + ld->name, get_dev_name(dev)); + + /* + ** The TXQ must be checked whether or not it is empty, because + ** an interrupt mask can be overwritten by the next interrupt. + */ + if (mst.head[dev][TX] == mst.tail[dev][TX]) { + ret = get_txq_buff_size(dpld, dev); +#ifdef DEBUG_MODEM_IF + mif_info("%s: %s_TXQ has been emptied\n", + ld->name, get_dev_name(dev)); + print_circ_status(ld, dev, &mst); +#endif + } + } + +exit: + return ret; +} + +/** + * process_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Restarts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + * + * Returns the return value from xmit_ipc_msg(). + */ +static int process_res_ack(struct dpram_link_device *dpld, int dev) +{ + int ret; + u16 mask; + + ret = xmit_ipc_msg(dpld, dev); + if (ret > 0) { + mask = get_mask_send(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + } + + if (ret >= 0) + atomic_set(&dpld->res_required[dev], 0); + + return ret; +} + +/** + * fmt_tx_work: performs TX for FMT IPC device under DPRAM flow control + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of FMT IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. + */ +static void fmt_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, fmt_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_FMT); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); + return; + } + + ret = process_res_ack(dpld, IPC_FMT); + if (ret >= 0) { + dpram_allow_sleep(dpld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], delay); +} + +/** + * raw_tx_work: performs TX for RAW IPC device under DPRAM flow control. + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RAW IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. + */ +static void raw_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, raw_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_RAW); + /* ret < 0 if interrupted */ + if (ret < 0) + return; - if (unlikely(intr == INT_POWERSAFE_FAIL)) { - mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); return; } - if (unlikely(EXT_UDL_CMD(intr))) { - if (likely(EXT_INT_VALID(intr))) { - if (UDL_CMD_VALID(intr)) - udl_command_handler(dpld, intr); - else if (EXT_CMD_VALID(intr)) - ext_command_handler(dpld, intr); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", - name, intr); - } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); - } + ret = process_res_ack(dpld, IPC_RAW); + if (ret >= 0) { + dpram_allow_sleep(dpld); + mif_netif_wake(ld); return; } - if (likely(INT_VALID(intr))) - dpram_ipc_rx(dpld, intr); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], delay); } -static irqreturn_t ap_idpram_irq_handler(int irq, void *data) +/** + * rfs_tx_work: performs TX for RFS IPC device under DPRAM flow control + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RFS IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RFS IPC device. + */ +static void rfs_tx_work(struct work_struct *work) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - u16 int2ap = recv_intr(dpld); + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; + ld = container_of(work, struct link_device, rfs_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_RFS); + /* ret < 0 if interrupted */ + if (ret < 0) + return; - dpram_intr_handler(dpld, int2ap); + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], 0); + return; + } - return IRQ_HANDLED; + ret = process_res_ack(dpld, IPC_RFS); + if (ret >= 0) { + dpram_allow_sleep(dpld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], delay); } -static irqreturn_t cp_idpram_irq_handler(int irq, void *data) +/** + * dpram_send_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static int dpram_send_ipc(struct dpram_link_device *dpld, int dev) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - u16 int2ap; + struct link_device *ld = &dpld->ld; + int ret; + u16 mask; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; + if (atomic_read(&dpld->res_required[dev]) > 0) { + mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + return 0; + } if (dpram_wake_up(dpld) < 0) { - log_dpram_status(dpld); - trigger_force_cp_crash(dpld); - return IRQ_HANDLED; + trigger_forced_cp_crash(dpld); + return -EIO; + } + + if (!ipc_active(dpld)) { + mif_info("%s: IPC is NOT active\n", ld->name); + ret = -EIO; + goto exit; + } + + ret = xmit_ipc_msg(dpld, dev); + if (likely(ret > 0)) { + mask = get_mask_send(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + goto exit; } - int2ap = recv_intr(dpld); + /* If there was no TX, just exit */ + if (ret == 0) + goto exit; + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) { + /* Prohibit DPRAM from sleeping until the TXQ buffer is empty */ + if (dpram_wake_up(dpld) < 0) { + trigger_forced_cp_crash(dpld); + goto exit; + } - dpram_intr_handler(dpld, int2ap); + /*----------------------------------------------------*/ + /* dpld->res_required[dev] was set in xmit_ipc_msg(). */ + /*----------------------------------------------------*/ - clear_intr(dpld); + if (dev == IPC_RAW) + mif_netif_stop(ld); - dpram_allow_sleep(dpld); + queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], 0); + } - return IRQ_HANDLED; +exit: + dpram_allow_sleep(dpld); + return ret; } -static irqreturn_t ext_dpram_irq_handler(int irq, void *data) +/** + * pm_tx_work: performs TX while DPRAM PM is locked + * @work: pointer to an instance of the work_struct structure + */ +static void pm_tx_work(struct work_struct *work) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; - u16 int2ap = recv_intr(dpld); + struct idpram_pm_data *pm_data; + struct idpram_pm_op *pm_op; + struct dpram_link_device *dpld; + struct link_device *ld; + struct workqueue_struct *pm_wq = system_nrt_wq; + int i; + int ret; + unsigned long delay = 0; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) - return IRQ_HANDLED; + pm_data = container_of(work, struct idpram_pm_data, tx_dwork.work); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + ld = &dpld->ld; + pm_op = dpld->pm_op; - dpram_intr_handler(dpld, int2ap); + if (pm_op->locked(dpld)) { + queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); + return; + } - return IRQ_HANDLED; + /* Here, PM is not locked. */ + for (i = 0; i < ld->max_ipc_dev; i++) { + ret = dpram_send_ipc(dpld, i); + if (ret < 0) { + struct io_device *iod = dpld->iod[i]; + mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } + } } -static void dpram_send_ipc(struct link_device *ld, int dev, +/** + * dpram_try_send_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 4) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static void dpram_try_send_ipc(struct dpram_link_device *dpld, int dev, struct io_device *iod, struct sk_buff *skb) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct link_device *ld = &dpld->ld; + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct idpram_pm_op *pm_op = dpld->pm_op; + struct workqueue_struct *pm_wq = system_nrt_wq; + unsigned long delay = msecs_to_jiffies(10); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; - u16 mask; - skb_queue_tail(txq, skb); - if (txq->qlen > 1024) { - mif_debug("%s: %s txq->qlen %d > 1024\n", - ld->name, get_dev_name(dev), txq->qlen); + if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { + mif_info("%s: %s txq->qlen %d >= %d\n", ld->name, + get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); + dev_kfree_skb_any(skb); + return; } - if (dpld->dp_type == CP_IDPRAM) { - if (dpram_wake_up(dpld) < 0) { - trigger_force_cp_crash(dpld); + skb_queue_tail(txq, skb); + + if (pm_op && pm_op->locked) { + if (pm_op->locked(dpld)) { + queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); return; } - } - if (!dpram_ipc_active(dpld)) - goto exit; - - if (atomic_read(&dpld->res_required[dev]) > 0) { - mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); - goto exit; + /* Here, PM is not locked. */ + if (work_pending(&pm_data->tx_dwork.work)) + cancel_delayed_work_sync(&pm_data->tx_dwork); } - ret = dpram_try_ipc_tx(dpld, dev); - if (ret > 0) { - mask = get_mask_send(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); - } else if (ret == -ENOSPC) { - mask = get_mask_req_ack(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); - mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); - } else { - mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); + ret = dpram_send_ipc(dpld, dev); + if (ret < 0) { + mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); } - -exit: - if (dpld->dp_type == CP_IDPRAM) - dpram_allow_sleep(dpld); } static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - if (dpld->ext_op && dpld->ext_op->download_binary) - return dpld->ext_op->download_binary(dpld, skb); + if (dpld->ext_op && dpld->ext_op->xmit_binary) + return dpld->ext_op->xmit_binary(dpld, skb); else return -ENODEV; } +/** + * dpram_send + * @ld: pointer to an instance of the link_device structure + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * Returns the length of data transmitted or an error code. + * + * Normal call flow for an IPC message: + * dpram_try_send_ipc -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on PM lock in a DPRAM IPC TXQ: + * dpram_try_send_ipc ,,, queue_delayed_work + * => pm_tx_work -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on congestion in a DPRAM IPC TXQ: + * dpram_try_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work + * => xxx_tx_work -> wait_for_res_ack + * => msg_handler + * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) + */ static int dpram_send(struct link_device *ld, struct io_device *iod, - struct sk_buff *skb) + struct sk_buff *skb) { - enum dev_format dev = iod->format; + struct dpram_link_device *dpld = to_dpram_link_device(ld); + int dev = iod->format; int len = skb->len; switch (dev) { @@ -1379,7 +2266,7 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_send_ipc(ld, dev, iod, skb); + dpram_try_send_ipc(dpld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); @@ -1396,23 +2283,45 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static int dpram_xmit_boot(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->xmit_boot) + return dpld->ext_op->xmit_boot(dpld, arg); + else + return -ENODEV; +} + +static int dpram_set_dload_magic(struct link_device *ld, struct io_device *iod) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - trigger_force_cp_crash(dpld); + + ld->mode = LINK_MODE_DLOAD; + + mif_err("%s: magic = 0x%08X\n", ld->name, DP_MAGIC_DMDL); + iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); + return 0; } -static void dpram_dump_memory(struct link_device *ld, char *buff) +static int dpram_dload_firmware(struct link_device *ld, struct io_device *iod, + unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *base = dpld->dpctl->dp_base; - u32 size = dpld->dpctl->dp_size; - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + if (dpld->ext_op && dpld->ext_op->firm_update) + return dpld->ext_op->firm_update(dpld, arg); + else + return -ENODEV; +} - memcpy(buff, base, size); +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + trigger_forced_cp_crash(dpld); + return 0; } static int dpram_dump_start(struct link_device *ld, struct io_device *iod) @@ -1431,13 +2340,24 @@ static int dpram_dump_update(struct link_device *ld, struct io_device *iod, struct dpram_link_device *dpld = to_dpram_link_device(ld); if (dpld->ext_op && dpld->ext_op->dump_update) - return dpld->ext_op->dump_update(dpld, (void *)arg); + return dpld->ext_op->dump_update(dpld, arg); + else + return -ENODEV; +} + +static int dpram_dump_finish(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dump_finish) + return dpld->ext_op->dump_finish(dpld, arg); else return -ENODEV; } static int dpram_ioctl(struct link_device *ld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); int err = 0; @@ -1447,17 +2367,21 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + return dpld->init_status; - default: - if (dpld->ext_ioctl) { - err = dpld->ext_ioctl(dpld, iod, cmd, arg); - } else { - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - } + case IOCTL_MIF_DPRAM_DUMP: + if (copy_to_user((void __user *)arg, &dpld->size, sizeof(u32))) + return -EFAULT; + capture_dpram_snapshot(ld, iod); break; + + default: + if (dpld->ext_ioctl) + return dpld->ext_ioctl(dpld, iod, cmd, arg); + + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + return -EINVAL; } return err; @@ -1468,9 +2392,9 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) struct dpram_ipc_16k_map *dpram_map; struct dpram_ipc_device *dev; - dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base; + dpram_map = (struct dpram_ipc_16k_map *)dpld->base; - /* magic code and access enable fields */ + /* "magic code" and "access enable" fields */ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; @@ -1519,167 +2443,201 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp; } -static int dpram_table_init(struct dpram_link_device *dpld) +static int dpram_init_boot_map(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - u8 __iomem *dp_base; - int i; - - if (!dpld->dp_base) { - mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); - return -EINVAL; - } - dp_base = dpld->dp_base; - - /* Map for IPC */ - if (dpld->dpctl->ipc_map) { - memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, - sizeof(struct dpram_ipc_map)); - } else { - if (dpld->dp_size == DPRAM_SIZE_16KB) - dpram_remap_std_16k_region(dpld); - else - return -EINVAL; - } - - dpld->magic = dpld->ipc_map.magic; - dpld->access = dpld->ipc_map.access; - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->dev[i] = &dpld->ipc_map.dev[i]; - dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; - dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; - /* Map for booting */ if (dpld->ext_op && dpld->ext_op->init_boot_map) { dpld->ext_op->init_boot_map(dpld); } else { dpld->bt_map.magic = (u32 *)(dp_base); - dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); - dpld->bt_map.size = dpld->dp_size - 8; + dpld->bt_map.buff = (u8 *)(dp_base + magic_size); + dpld->bt_map.space = dpld->size - (magic_size + mbx_size); } - /* Map for download (FOTA, UDL, etc.) */ + return 0; +} + +static int dpram_init_dload_map(struct dpram_link_device *dpld) +{ + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; + if (dpld->ext_op && dpld->ext_op->init_dl_map) { dpld->ext_op->init_dl_map(dpld); } else { dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.buff = (u8 *)(dp_base + magic_size); + dpld->dl_map.space = dpld->size - (magic_size + mbx_size); } - /* Map for upload mode */ + return 0; +} + +static int dpram_init_uload_map(struct dpram_link_device *dpld) +{ + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; + if (dpld->ext_op && dpld->ext_op->init_ul_map) { dpld->ext_op->init_ul_map(dpld); } else { dpld->ul_map.magic = (u32 *)(dp_base); dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + dpld->ul_map.space = dpld->size - (magic_size + mbx_size); + } + + return 0; +} + +static int dpram_init_ipc_map(struct dpram_link_device *dpld) +{ + int i; + struct link_device *ld = &dpld->ld; + + if (dpld->ext_op && dpld->ext_op->init_ipc_map) { + dpld->ext_op->init_ipc_map(dpld); + } else if (dpld->dpram->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpram->ipc_map, + sizeof(struct dpram_ipc_map)); + } else { + if (dpld->size == DPRAM_SIZE_16KB) + dpram_remap_std_16k_region(dpld); + else + return -EINVAL; } + dpld->magic = dpld->ipc_map.magic; + dpld->access = dpld->ipc_map.access; + for (i = 0; i < ld->max_ipc_dev; i++) + dpld->dev[i] = &dpld->ipc_map.dev[i]; + dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; + dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + return 0; } static void dpram_setup_common_op(struct dpram_link_device *dpld) { - dpld->clear_intr = clear_intr; - dpld->recv_intr = recv_intr; - dpld->send_intr = send_intr; + dpld->recv_intr = recv_int2ap; + dpld->send_intr = send_int2cp; dpld->get_magic = get_magic; dpld->set_magic = set_magic; dpld->get_access = get_access; dpld->set_access = set_access; - dpld->get_tx_head = get_tx_head; - dpld->get_tx_tail = get_tx_tail; - dpld->set_tx_head = set_tx_head; - dpld->set_tx_tail = set_tx_tail; - dpld->get_tx_buff = get_tx_buff; - dpld->get_tx_buff_size = get_tx_buff_size; - dpld->get_rx_head = get_rx_head; - dpld->get_rx_tail = get_rx_tail; - dpld->set_rx_head = set_rx_head; - dpld->set_rx_tail = set_rx_tail; - dpld->get_rx_buff = get_rx_buff; - dpld->get_rx_buff_size = get_rx_buff_size; + dpld->get_txq_head = get_txq_head; + dpld->get_txq_tail = get_txq_tail; + dpld->set_txq_head = set_txq_head; + dpld->set_txq_tail = set_txq_tail; + dpld->get_txq_buff = get_txq_buff; + dpld->get_txq_buff_size = get_txq_buff_size; + dpld->get_rxq_head = get_rxq_head; + dpld->get_rxq_tail = get_rxq_tail; + dpld->set_rxq_head = set_rxq_head; + dpld->set_rxq_tail = set_rxq_tail; + dpld->get_rxq_buff = get_rxq_buff; + dpld->get_rxq_buff_size = get_rxq_buff_size; dpld->get_mask_req_ack = get_mask_req_ack; dpld->get_mask_res_ack = get_mask_res_ack; dpld->get_mask_send = get_mask_send; - dpld->ipc_rx_handler = dpram_ipc_rx; -} - -static int dpram_link_init(struct link_device *ld, struct io_device *iod) -{ - return 0; + dpld->get_dpram_status = get_dpram_status; + dpld->ipc_rx_handler = cmd_msg_handler; + dpld->reset_dpram_ipc = reset_dpram_ipc; } static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) { + if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { + if (!atomic_read(&iod->opened)) { + ld->mode = LINK_MODE_OFFLINE; + mif_err("%s: %s: link mode is changed: IPC->OFFLINE\n", + iod->name, ld->name); + } + } + return; } struct link_device *dpram_create_link_device(struct platform_device *pdev) { - struct modem_data *mdm_data = NULL; struct dpram_link_device *dpld = NULL; struct link_device *ld = NULL; + struct modem_data *modem = NULL; + struct modemlink_dpram_data *dpram = NULL; struct resource *res = NULL; resource_size_t res_size; - struct modemlink_dpram_control *dpctl = NULL; - unsigned long task_data; int ret = 0; int i = 0; - int bsize; - int qsize; - /* Get the platform data */ - mdm_data = (struct modem_data *)pdev->dev.platform_data; - if (!mdm_data) { - mif_info("ERR! mdm_data == NULL\n"); + /* + ** Alloc an instance of dpram_link_device structure + */ + dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); + if (!dpld) { + mif_err("ERR! kzalloc dpld fail\n"); goto err; } - mif_info("modem = %s\n", mdm_data->name); - mif_info("link device = %s\n", mdm_data->link_name); + ld = &dpld->ld; - if (!mdm_data->dpram_ctl) { - mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); + /* + ** Get the modem (platform) data + */ + modem = (struct modem_data *)pdev->dev.platform_data; + if (!modem) { + mif_err("ERR! modem == NULL\n"); goto err; } - dpctl = mdm_data->dpram_ctl; - - /* Alloc DPRAM link device structure */ - dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); - if (!dpld) { - mif_info("ERR! kzalloc dpld fail\n"); + mif_info("modem = %s\n", modem->name); + mif_info("link device = %s\n", modem->link_name); + + /* + ** Retrieve modem data and DPRAM control data from the modem data + */ + ld->mdm_data = modem; + ld->name = modem->link_name; + ld->ipc_version = modem->ipc_version; + + if (!modem->dpram) { + mif_err("ERR! no modem->dpram\n"); goto err; } - ld = &dpld->ld; - - /* Retrieve modem data and DPRAM control data from the modem data */ - ld->mdm_data = mdm_data; - ld->name = mdm_data->link_name; - ld->ipc_version = mdm_data->ipc_version; + dpram = modem->dpram; - /* Retrieve the most basic data for IPC from the modem data */ - dpld->dpctl = dpctl; - dpld->dp_type = dpctl->dp_type; + dpld->dpram = dpram; + dpld->type = dpram->type; + dpld->ap = dpram->ap; + dpld->strict_io_access = dpram->strict_io_access; - if (mdm_data->ipc_version < SIPC_VER_50) { - if (!dpctl->max_ipc_dev) { - mif_info("ERR! no max_ipc_dev\n"); + if (ld->ipc_version < SIPC_VER_50) { + if (!modem->max_ipc_dev) { + mif_err("%s: ERR! no max_ipc_dev\n", ld->name); goto err; } - ld->aligned = dpctl->aligned; - dpld->max_ipc_dev = dpctl->max_ipc_dev; + ld->aligned = dpram->aligned; + ld->max_ipc_dev = modem->max_ipc_dev; } else { ld->aligned = 1; - dpld->max_ipc_dev = MAX_SIPC5_DEV; + ld->max_ipc_dev = MAX_SIPC5_DEV; } - /* Set attributes as a link device */ - ld->init_comm = dpram_link_init; + /* + ** Set attributes as a link device + */ ld->terminate_comm = dpram_link_terminate; ld->send = dpram_send; + ld->xmit_boot = dpram_xmit_boot; + ld->dload_start = dpram_set_dload_magic; + ld->firm_update = dpram_dload_firmware; ld->force_dump = dpram_force_dump; ld->dump_start = dpram_dump_start; ld->dump_update = dpram_dump_update; + ld->dump_finish = dpram_dump_finish; + /* IOCTL extension */ ld->ioctl = dpram_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1691,129 +2649,217 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; - /* Set up function pointers */ + skb_queue_head_init(&ld->sk_fmt_rx_q); + skb_queue_head_init(&ld->sk_raw_rx_q); + skb_queue_head_init(&ld->sk_rfs_rx_q); + ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; + ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; + ld->skb_rxq[IPC_RFS] = &ld->sk_rfs_rx_q; + + init_completion(&ld->init_cmpl); + init_completion(&ld->pif_cmpl); + + /* + ** Set up function pointers + */ dpram_setup_common_op(dpld); - dpld->dpram_dump = dpram_dump_memory; - dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); - if (dpld->ext_op && dpld->ext_op->ioctl) - dpld->ext_ioctl = dpld->ext_op->ioctl; - - /* Retrieve DPRAM resource */ - if (!dpctl->dp_base) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dpld->ext_op = dpram_get_ext_op(modem->modem_type); + if (dpld->ext_op) { + if (dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; + + if (dpld->ext_op->wakeup && dpld->ext_op->sleep) + dpld->need_wake_up = true; + } + + /* + ** Retrieve DPRAM resource + */ + if (!dpram->base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + STR_DPRAM_BASE); if (!res) { - mif_info("%s: ERR! platform_get_resource fail\n", + mif_err("%s: ERR! no DPRAM resource\n", ld->name); + goto err; + } + res_size = resource_size(res); + + dpram->base = ioremap_nocache(res->start, res_size); + if (!dpram->base) { + mif_err("%s: ERR! ioremap_nocache for BASE fail\n", ld->name); goto err; } + dpram->size = res_size; + } + dpld->base = dpram->base; + dpld->size = dpram->size; + + mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", + ld->name, dpld->type, ld->aligned, (int)dpld->base, dpld->size); + + /* + ** Retrieve DPRAM SFR resource if exists + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + STR_DPRAM_SFR_BASE); + if (res) { res_size = resource_size(res); + dpld->sfr_base = ioremap_nocache(res->start, res_size); + if (!dpld->sfr_base) { + mif_err("%s: ERR! ioremap_nocache for SFR fail\n", + ld->name); + goto err; + } + } + + /* + ** Initialize DPRAM maps (physical map -> logical map) + */ + ret = dpram_init_boot_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_boot_map fail (err %d)\n", + ld->name, ret); + goto err; + } - dpctl->dp_base = ioremap_nocache(res->start, res_size); - dpctl->dp_size = res_size; + ret = dpram_init_dload_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_dload_map fail (err %d)\n", + ld->name, ret); + goto err; } - dpld->dp_base = dpctl->dp_base; - dpld->dp_size = dpctl->dp_size; - mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", - ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, - dpld->dp_size); + ret = dpram_init_uload_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_uload_map fail (err %d)\n", + ld->name, ret); + goto err; + } - /* Initialize DPRAM map (physical map -> logical map) */ - ret = dpram_table_init(dpld); + ret = dpram_init_ipc_map(dpld); if (ret < 0) { - mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + mif_err("%s: ERR! dpram_init_ipc_map fail (err %d)\n", ld->name, ret); goto err; } + if (dpram->res_ack_wait_timeout > 0) + dpld->res_ack_wait_timeout = dpram->res_ack_wait_timeout; + else + dpld->res_ack_wait_timeout = RES_ACK_WAIT_TIMEOUT; + /* Disable IPC */ - set_magic(dpld, 0); - set_access(dpld, 0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + if (!dpram->disabled) { + set_magic(dpld, 0); + set_access(dpld, 0); + } + dpld->init_status = DPRAM_INIT_STATE_NONE; - /* Initialize locks, completions, and bottom halves */ - snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); + /* + ** Initialize locks, completions, and bottom halves + */ + snprintf(dpld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); - init_completion(&dpld->dpram_init_cmd); - init_completion(&dpld->modem_pif_init_done); - init_completion(&dpld->udl_start_complete); - init_completion(&dpld->udl_cmd_complete); - init_completion(&dpld->dump_start_complete); - init_completion(&dpld->dump_recv_done); - - task_data = (unsigned long)dpld; - tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); - - /* Prepare SKB queue head for RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - skb_queue_head_init(&dpld->skb_rxq[i]); - - /* Prepare RXB queue */ - qsize = DPRAM_MAX_RXBQ_SIZE; - for (i = 0; i < dpld->max_ipc_dev; i++) { - bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); - dpld->rxbq[i].size = qsize; - dpld->rxbq[i].in = 0; - dpld->rxbq[i].out = 0; - dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); - if (!dpld->rxbq[i].rxb) { - mif_info("%s: ERR! %s rxbq_create_pool fail\n", - ld->name, get_dev_name(i)); - goto err; - } - mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n", - ld->name, get_dev_name(i), bsize, qsize); + init_completion(&dpld->udl_cmpl); + init_completion(&dpld->crash_cmpl); + + for (i = 0; i < ld->max_ipc_dev; i++) + init_completion(&dpld->req_ack_cmpl[i]); + + INIT_DELAYED_WORK(&dpld->rx_dwork, ipc_rx_work); + + for (i = 0; i < ld->max_ipc_dev; i++) { + spin_lock_init(&dpld->tx_lock[i]); + atomic_set(&dpld->res_required[i], 0); + } + + ld->tx_wq = create_singlethread_workqueue("dpram_tx_wq"); + if (!ld->tx_wq) { + mif_err("%s: ERR! fail to create tx_wq\n", ld->name); + goto err; } + INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); + INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); + INIT_DELAYED_WORK(&ld->rfs_tx_dwork, rfs_tx_work); + ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; + ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; + ld->tx_dwork[IPC_RFS] = &ld->rfs_tx_dwork; + +#ifdef DEBUG_MODEM_IF + spin_lock_init(&dpld->stat_list.lock); + spin_lock_init(&dpld->trace_list.lock); +#endif /* Prepare a multi-purpose miscellaneous buffer */ - dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); + dpld->buff = kzalloc(dpld->size, GFP_KERNEL); if (!dpld->buff) { - mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + mif_err("%s: ERR! kzalloc dpld->buff fail\n", ld->name); goto err; } - /* Retrieve DPRAM IRQ GPIO# */ - dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; - - /* Retrieve DPRAM IRQ# */ - if (!dpctl->dpram_irq) { - dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); - if (dpctl->dpram_irq < 0) { - mif_info("%s: ERR! platform_get_irq_byname fail\n", - ld->name); + /* + ** Retrieve DPRAM IRQ GPIO#, IRQ#, and IRQ flags + */ + dpld->gpio_int2ap = modem->gpio_ipc_int2ap; + dpld->gpio_cp_status = modem->gpio_cp_status; + dpld->gpio_cp_wakeup = modem->gpio_cp_wakeup; + if (dpram->type == AP_IDPRAM) { + if (!modem->gpio_ipc_int2cp) { + mif_err("%s: ERR! no gpio_ipc_int2cp\n", ld->name); goto err; } + dpld->gpio_int2cp = modem->gpio_ipc_int2cp; } - dpld->irq = dpctl->dpram_irq; - /* Retrieve DPRAM IRQ flags */ - if (!dpctl->dpram_irq_flags) - dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); - dpld->irq_flags = dpctl->dpram_irq_flags; + dpld->irq = modem->irq_ipc_int2ap; + + if (modem->irqf_ipc_int2ap) + dpld->irq_flags = modem->irqf_ipc_int2ap; + else + dpld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + + /* + ** Initialize power management (PM) for AP_IDPRAM + */ + if (dpld->type == AP_IDPRAM) { + dpld->pm_op = idpram_get_pm_op(dpld->ap); + if (!dpld->pm_op) { + mif_err("%s: no pm_op for AP_IDPRAM\n", ld->name); + goto err; + } + + ret = dpld->pm_op->pm_init(dpld, modem, pm_tx_work); + if (ret) { + mif_err("%s: pm_init fail (err %d)\n", ld->name, ret); + goto err; + } + } - /* Register DPRAM interrupt handler */ - snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); + /* + ** Register DPRAM interrupt handler + */ + snprintf(dpld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); if (dpld->ext_op && dpld->ext_op->irq_handler) dpld->irq_handler = dpld->ext_op->irq_handler; - else if (dpld->dp_type == CP_IDPRAM) + else if (dpld->type == CP_IDPRAM) dpld->irq_handler = cp_idpram_irq_handler; - else if (dpld->dp_type == AP_IDPRAM) + else if (dpld->type == AP_IDPRAM) dpld->irq_handler = ap_idpram_irq_handler; else dpld->irq_handler = ext_dpram_irq_handler; - ret = dpram_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, + ret = mif_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, dpld->irq_name, dpld); if (ret) goto err; - else - return ld; + + return ld; err: if (dpld) { - if (dpld->buff) - kfree(dpld->buff); + kfree(dpld->buff); kfree(dpld); } diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h index c651e51..d9ef251 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.h +++ b/drivers/misc/modem_if/modem_link_device_dpram.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,227 +11,11 @@ * GNU General Public License for more details. * */ + #ifndef __MODEM_LINK_DEVICE_DPRAM_H__ #define __MODEM_LINK_DEVICE_DPRAM_H__ -#include -#include -#include -#include -#include - -#include "modem_prj.h" - -#define DPRAM_MAGIC_CODE 0xAA - -/* interrupt masks.*/ -#define INT_MASK_VALID 0x0080 -#define INT_MASK_CMD 0x0040 -#define INT_VALID(x) ((x) & INT_MASK_VALID) -#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) -#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) -#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) - -#define EXT_UDL_MASK 0xF000 -#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) -#define EXT_INT_VALID_MASK 0x8000 -#define EXT_CMD_VALID_MASK 0x4000 -#define UDL_CMD_VALID_MASK 0x2000 -#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) -#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) -#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) -#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) - -#define EXT_CMD_MASK(x) ((x) & 0x0FFF) -#define EXT_CMD_SET_SPEED_LOW 0x0011 -#define EXT_CMD_SET_SPEED_MID 0x0012 -#define EXT_CMD_SET_SPEED_HIGH 0x0013 - -#define UDL_RESULT_SUCCESS 0x1 -#define UDL_RESULT_FAIL 0x2 - -#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) -#define UDL_CMD_RECV_READY 0x1 -#define UDL_CMD_DL_START_REQ 0x2 -#define UDL_CMD_DL_START_RESP 0x3 -#define UDL_CMD_IMAGE_SEND_REQ 0x4 -#define UDL_CMD_SEND_DONE_RESP 0x5 -#define UDL_CMD_SEND_DONE_REQ 0x6 -#define UDL_CMD_UPDATE_DONE 0x7 -#define UDL_CMD_STATUS_UPDATE 0x8 -#define UDL_CMD_IMAGE_SEND_RESP 0x9 -#define UDL_CMD_EFS_CLEAR_RESP 0xB -#define UDL_CMD_ALARM_BOOT_OK 0xC -#define UDL_CMD_ALARM_BOOT_FAIL 0xD - -#define CMD_IMG_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 -#define CMD_UL_RECV_RESP 0x9601 -#define CMD_UL_RECV_DONE_RESP 0x9801 - -/* special interrupt cmd indicating modem boot failure. */ -#define INT_POWERSAFE_FAIL 0xDEAD - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_REQ_ACK_F 0x0020 -#define INT_MASK_REQ_ACK_R 0x0010 -#define INT_MASK_RES_ACK_F 0x0008 -#define INT_MASK_RES_ACK_R 0x0004 -#define INT_MASK_SEND_F 0x0002 -#define INT_MASK_SEND_R 0x0001 - -#define INT_MASK_RES_ACK_SET \ - (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) - -#define INT_MASK_SEND_SET \ - (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) - -#define INT_CMD_MASK(x) ((x) & 0xF) -#define INT_CMD_INIT_START 0x1 -#define INT_CMD_INIT_END 0x2 -#define INT_CMD_REQ_ACTIVE 0x3 -#define INT_CMD_RES_ACTIVE 0x4 -#define INT_CMD_REQ_TIME_SYNC 0x5 -#define INT_CMD_CRASH_RESET 0x7 -#define INT_CMD_PHONE_START 0x8 -#define INT_CMD_ERR_DISPLAY 0x9 -#define INT_CMD_CRASH_EXIT 0x9 -#define INT_CMD_CP_DEEP_SLEEP 0xA -#define INT_CMD_NV_REBUILDING 0xB -#define INT_CMD_EMER_DOWN 0xC -#define INT_CMD_PIF_INIT_DONE 0xD -#define INT_CMD_SILENT_NV_REBUILDING 0xE -#define INT_CMD_NORMAL_PWR_OFF 0xF - -#define START_FLAG 0x7F -#define END_FLAG 0x7E - -#define DP_MAGIC_DMDL 0x4445444C -#define DP_MAGIC_UMDL 0x4445444D -#define DP_DPRAM_SIZE 0x4000 -#define DP_DEFAULT_WRITE_LEN 8168 -#define DP_DEFAULT_DUMP_LEN 16128 -#define DP_DUMP_HEADER_SIZE 7 - -#define UDL_TIMEOUT (50 * HZ) -#define UDL_SEND_TIMEOUT (200 * HZ) -#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) -#define DUMP_TIMEOUT (30 * HZ) -#define DUMP_START_TIMEOUT (100 * HZ) -#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ - -enum host_boot_mode { - HOST_BOOT_MODE_NORMAL, - HOST_BOOT_MODE_DUMP, -}; - -enum dpram_init_status { - DPRAM_INIT_STATE_NONE, - DPRAM_INIT_STATE_READY, -}; - -struct dpram_boot_img { - char *addr; - int size; - enum host_boot_mode mode; - unsigned req; - unsigned resp; -}; - -#define MAX_PAYLOAD_SIZE 0x2000 -struct dpram_boot_frame { - unsigned req; /* AP->CP request */ - unsigned resp; /* response expected by AP */ - ssize_t len; /* data size in the buffer */ - unsigned offset; /* offset to write into DPRAM */ - char data[MAX_PAYLOAD_SIZE]; -}; - -/* buffer type for modem image */ -struct dpram_dump_arg { - char *buff; /* pointer to the buffer */ - int buff_size; /* buffer size */ - unsigned req; /* AP->CP request */ - unsigned resp; /* CP->AP response */ - bool cmd; /* AP->CP command */ -}; - -struct dpram_boot_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 __iomem *req; - u32 __iomem *resp; - u32 size; -}; - -struct qc_dpram_boot_map { - u8 __iomem *buff; - u16 __iomem *frame_size; - u16 __iomem *tag; - u16 __iomem *count; -}; - -struct dpram_dload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct dpram_uload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct ul_header { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -} __packed; - -struct dpram_udl_param { - unsigned char *addr; - unsigned int size; - unsigned int count; - unsigned int tag; -}; - -struct dpram_udl_check { - unsigned int total_size; - unsigned int rest_size; - unsigned int send_size; - unsigned int copy_start; - unsigned int copy_complete; - unsigned int boot_complete; -}; - -#define DP_BOOT_BUFF_OFFSET 4 -#define DP_DLOAD_BUFF_OFFSET 4 -#define DP_ULOAD_BUFF_OFFSET 4 -#define DP_BOOT_REQ_OFFSET 0 -#define DP_BOOT_RESP_OFFSET 8 - -#define MAX_WQ_NAME_LENGTH 64 - -#define DPRAM_MAX_RXBQ_SIZE 256 - -struct dpram_rxb { - u8 *buff; - unsigned size; - - u8 *data; - unsigned len; -}; - -struct dpram_rxb_queue { - int size; - int in; - int out; - struct dpram_rxb *rxb; -}; +#include "modem_link_device_memory.h" /* magic_code + @@ -282,38 +65,110 @@ struct dpram_ipc_16k_map { u16 mbx_ap2cp; }; -#define DP_MAX_NAME_LEN 32 +enum dpram_init_status { + DPRAM_INIT_STATE_NONE, + DPRAM_INIT_STATE_READY, +}; + +struct dpram_boot_frame { + unsigned req; /* AP->CP request */ + unsigned resp; /* response expected by AP */ + ssize_t len; /* data size in the buffer */ + unsigned offset; /* offset to write into DPRAM */ + char data[DP_MAX_PAYLOAD_SIZE]; +}; + +struct dpram_dump_arg { + char *buff; /* pointer to the buffer */ + int buff_size; /* buffer size */ + unsigned req; /* AP->CP request */ + unsigned resp; /* CP->AP response */ + int cmd; /* AP->CP command */ +}; + +/* DPRAM upload/download header */ +struct dpram_udl_header { + u8 bop; + u16 num_frames; + u16 curr_frame; + u16 len; +#ifdef CONFIG_CDMA_MODEM_CBP82 + u8 pad; +#endif +} __packed; + +#define MAX_DUMP_SKB_SIZE 4096 + +enum idpram_link_pm_states { + IDPRAM_PM_SUSPEND_PREPARE, + IDPRAM_PM_DPRAM_POWER_DOWN, + IDPRAM_PM_SUSPEND_START, + IDPRAM_PM_RESUME_START, + IDPRAM_PM_ACTIVE, +}; + +struct idpram_pm_data { + atomic_t pm_lock; + + enum idpram_link_pm_states pm_state; + + struct completion down_cmpl; + + struct wake_lock ap_wlock; + struct wake_lock hold_wlock; + struct delayed_work tx_dwork; + struct delayed_work resume_dwork; + + struct notifier_block pm_noti; + + unsigned resume_try_cnt; + + /* the last value in the mbx_cp2ap */ + unsigned last_msg; +}; + +struct dpram_link_device; struct dpram_ext_op; +struct idpram_pm_op; struct dpram_link_device { struct link_device ld; + enum dpram_type type; /* DPRAM type */ + enum ap_type ap; /* AP type for AP_IDPRAM */ + + /* Stirct I/O access (e.g. ioread16(), etc.) is required */ + bool strict_io_access; + /* DPRAM address and size */ - u8 __iomem *dp_base; /* DPRAM base virtual address */ - u32 dp_size; /* DPRAM size */ - enum dpram_type dp_type; /* DPRAM type */ + u8 __iomem *base; /* Virtual address of DPRAM */ + u32 size; /* DPRAM size */ + + /* DPRAM SFR */ + u8 __iomem *sfr_base; /* Virtual address of SFR */ + + /* Whether or not this DPRAM can go asleep */ + bool need_wake_up; /* DPRAM IRQ GPIO# */ - unsigned gpio_dpram_int; + unsigned gpio_int2ap; + unsigned gpio_cp_status; + unsigned gpio_cp_wakeup; + unsigned gpio_int2cp; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[DP_MAX_NAME_LEN]; + char irq_name[MIF_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - int max_ipc_dev; - struct modemlink_dpram_control *dpctl; + struct modemlink_dpram_data *dpram; /* Physical configuration -> logical configuration */ - union { - struct dpram_boot_map bt_map; - struct qc_dpram_boot_map qc_bt_map; - }; - - struct dpram_dload_map dl_map; - struct dpram_uload_map ul_map; + struct memif_boot_map bt_map; + struct memif_dload_map dl_map; + struct memif_uload_map ul_map; /* IPC device map */ struct dpram_ipc_map ipc_map; @@ -327,40 +182,40 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[DP_MAX_NAME_LEN]; + char wlock_name[MIF_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; - struct completion dpram_init_cmd; - struct completion modem_pif_init_done; /* For UDL */ - struct tasklet_struct ul_tsk; struct tasklet_struct dl_tsk; - struct completion udl_start_complete; - struct completion udl_cmd_complete; - struct dpram_udl_check udl_check; - struct dpram_udl_param udl_param; + struct completion udl_cmpl; - /* For CP RAM dump */ + /* + ** For CP crash dump + */ + bool forced_cp_crash; struct timer_list crash_ack_timer; - struct completion dump_start_complete; - struct completion dump_recv_done; - struct timer_list dump_timer; - int dump_rcvd; /* Count of dump packets received */ + struct timer_list crash_timer; + struct completion crash_cmpl; + /* If this field is wanted to be used, it must be initialized only in + * the "ld->dump_start" method. + */ + struct delayed_work crash_dwork; + /* Count of CP crash dump packets received */ + int crash_rcvd; /* For locking TX process */ spinlock_t tx_lock[MAX_IPC_DEV]; + /* For retransmission under DPRAM flow control after TXQ full state */ + unsigned long res_ack_wait_timeout; + atomic_t res_required[MAX_IPC_DEV]; + struct completion req_ack_cmpl[MAX_IPC_DEV]; + /* For efficient RX process */ - struct tasklet_struct rx_tsk; - struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct delayed_work rx_dwork; struct io_device *iod[MAX_IPC_DEV]; - bool use_skb; - struct sk_buff_head skb_rxq[MAX_IPC_DEV]; - - /* For retransmission after buffer full state */ - atomic_t res_required[MAX_IPC_DEV]; /* For wake-up/sleep control */ atomic_t accessing; @@ -369,45 +224,53 @@ struct dpram_link_device { u8 *buff; /* DPRAM IPC initialization status */ - int dpram_init_status; + int init_status; /* Alias to device-specific IOCTL function */ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg); /* Alias to DPRAM IRQ handler */ - irqreturn_t (*irq_handler)(int irq, void *data); + irq_handler_t irq_handler; - /* For DPRAM dump */ - void (*dpram_dump)(struct link_device *ld, char *buff); + /* For DPRAM logging */ + struct mem_status_queue stat_list; + struct trace_data_queue trace_list; /* Common operations for each DPRAM */ - void (*clear_intr)(struct dpram_link_device *dpld); u16 (*recv_intr)(struct dpram_link_device *dpld); void (*send_intr)(struct dpram_link_device *dpld, u16 mask); u16 (*get_magic)(struct dpram_link_device *dpld); void (*set_magic)(struct dpram_link_device *dpld, u16 value); u16 (*get_access)(struct dpram_link_device *dpld); void (*set_access)(struct dpram_link_device *dpld, u16 value); - u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); - void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); - void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_head)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_tail)(struct dpram_link_device *dpld, int id); + void (*set_txq_head)(struct dpram_link_device *dpld, int id, u32 in); + void (*set_txq_tail)(struct dpram_link_device *dpld, int id, u32 out); + u8 *(*get_txq_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_tail)(struct dpram_link_device *dpld, int id); + void (*set_rxq_head)(struct dpram_link_device *dpld, int id, u32 in); + void (*set_rxq_tail)(struct dpram_link_device *dpld, int id, u32 out); + u8 *(*get_rxq_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_buff_size)(struct dpram_link_device *dpld, int id); u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); - void (*ipc_rx_handler)(struct dpram_link_device *dpld, u16 int2ap); + void (*get_dpram_status)(struct dpram_link_device *dpld, + enum circ_dir_type, struct mem_status *stat); + void (*ipc_rx_handler)(struct dpram_link_device *dpld, + struct mem_status *stat); + void (*reset_dpram_ipc)(struct dpram_link_device *dpld); /* Extended operations for various modems */ struct dpram_ext_op *ext_op; + + /* Power management (PM) for AP_IDPRAM */ + struct idpram_pm_data pm_data; + struct idpram_pm_op *pm_op; }; /* converts from struct link_device* to struct xxx_link_device* */ @@ -415,27 +278,65 @@ struct dpram_link_device { container_of(linkdev, struct dpram_link_device, ld) struct dpram_ext_op { + /* flag for checking whether or not a dpram_ext_op instance exists */ int exist; + /* methods for setting up DPRAM maps */ void (*init_boot_map)(struct dpram_link_device *dpld); void (*init_dl_map)(struct dpram_link_device *dpld); void (*init_ul_map)(struct dpram_link_device *dpld); + void (*init_ipc_map)(struct dpram_link_device *dpld); - int (*download_binary)(struct dpram_link_device *dpld, - struct sk_buff *skb); + /* methods for CP booting */ + int (*xmit_boot)(struct dpram_link_device *dpld, unsigned long arg); + int (*xmit_binary)(struct dpram_link_device *dpld, struct sk_buff *skb); + /* methods for DPRAM command handling */ + void (*cmd_handler)(struct dpram_link_device *dpld, u16 cmd); void (*cp_start_handler)(struct dpram_link_device *dpld); + /* method for CP firmware upgrade */ + int (*firm_update)(struct dpram_link_device *dpld, unsigned long arg); + + /* methods for CP crash dump */ void (*crash_log)(struct dpram_link_device *dpld); int (*dump_start)(struct dpram_link_device *dpld); - int (*dump_update)(struct dpram_link_device *dpld, void *arg); + int (*dump_update)(struct dpram_link_device *dpld, unsigned long arg); + int (*dump_finish)(struct dpram_link_device *dpld, unsigned long arg); + /* IOCTL extension */ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); - irqreturn_t (*irq_handler)(int irq, void *data); + /* methods for interrupt handling */ + irq_handler_t irq_handler; + void (*clear_int2ap)(struct dpram_link_device *dpld); + + /* methods for power management */ + int (*wakeup)(struct dpram_link_device *dpld); + void (*sleep)(struct dpram_link_device *dpld); }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +struct idpram_pm_op { + /* flag for checking whether or not a idpram_pm_op instance exists */ + int exist; + int (*pm_init)(struct dpram_link_device *dpld, struct modem_data *modem, + void (*pm_tx_func)(struct work_struct *work)); + void (*power_down)(struct dpram_link_device *dpld); + void (*power_up)(struct dpram_link_device *dpld); + void (*halt_suspend)(struct dpram_link_device *dpld); + bool (*locked)(struct dpram_link_device *dpld); + bool (*int2cp_possible)(struct dpram_link_device *dpld); +}; + +struct idpram_pm_op *idpram_get_pm_op(enum ap_type id); + +#if 1 +#endif + +extern void set_sromc_access(bool access); + #endif + diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c index 1475a46..edaf6e5 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c @@ -25,103 +25,82 @@ #include #include #include -#include +#include +#include +#include +#include "modem.h" #include "modem_prj.h" -#include "modem_link_device_dpram.h" #include "modem_utils.h" +#include "modem_link_device_dpram.h" -#if defined(CONFIG_LTE_MODEM_CMC221) -/* -** For host (flashless) booting via DPRAM -*/ -#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 -#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 -#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A -#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD - -#define CMC22x_HOST_DOWN_START 0x1234 -#define CMC22x_HOST_DOWN_END 0x4321 -#define CMC22x_REG_NV_DOWN_END 0xABCD -#define CMC22x_CAL_NV_DOWN_END 0xDCBA - -#define CMC22x_1ST_BUFF_READY 0xAAAA -#define CMC22x_2ND_BUFF_READY 0xBBBB -#define CMC22x_1ST_BUFF_FULL 0x1111 -#define CMC22x_2ND_BUFF_FULL 0x2222 - -#define CMC22x_CP_RECV_NV_END 0x8888 -#define CMC22x_CP_CAL_OK 0x4F4B -#define CMC22x_CP_CAL_BAD 0x4552 -#define CMC22x_CP_DUMP_END 0xFADE - -#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */ -#endif - -#if defined(CONFIG_CDMA_MODEM_CBP72) -static void cbp72_init_boot_map(struct dpram_link_device *dpld) +#if defined(CONFIG_CDMA_MODEM_CBP72) || defined(CONFIG_CDMA_MODEM_CBP82) +static void cbp_init_boot_map(struct dpram_link_device *dpld) { - struct dpram_boot_map *bt_map = &dpld->bt_map; + struct memif_boot_map *bt_map = &dpld->bt_map; - bt_map->magic = (u32 *)dpld->dp_base; - bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET); - bt_map->size = dpld->dp_size - 4; + bt_map->magic = (u32 *)dpld->base; + bt_map->buff = (u8 *)(dpld->base + DP_BOOT_BUFF_OFFSET); + bt_map->space = dpld->size - 4; } -static void cbp72_init_dl_map(struct dpram_link_device *dpld) +static void cbp_init_dl_map(struct dpram_link_device *dpld) { - dpld->dl_map.magic = (u32 *)dpld->dp_base; - dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); } -static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int cbp_udl_wait_resp(struct dpram_link_device *dpld, u32 resp) { - struct link_device *ld = &dpld->ld; int ret; - int int2cp; + int int2ap; - ret = wait_for_completion_interruptible_timeout( - &dpld->udl_cmd_complete, UDL_TIMEOUT); + mif_debug("wait for 0x%04X\n", resp); + ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); if (!ret) { - mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name); - return -ENXIO; + mif_info("ERR! No UDL_CMD_RESP!!!\n"); + return -EIO; } - int2cp = dpld->recv_intr(dpld); - mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp); - if (resp == int2cp || int2cp == 0xA700) - return int2cp; - else + int2ap = dpld->recv_intr(dpld); + if (resp == int2ap || int2ap == CMD_UL_RECV_DONE_REQ) { + mif_debug("int2ap = 0x%04X\n", int2ap); + return int2ap; + } else { + mif_err("ERR! int2ap 0x%04X != resp 0x%04X\n", int2ap, resp); return -EINVAL; + } } -static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld, +static int cbp_xmit_binary(struct dpram_link_device *dpld, struct sk_buff *skb) { - struct link_device *ld = &dpld->ld; struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; u8 __iomem *buff = dpld->bt_map.buff; int err = 0; - if (bf->len > dpld->bt_map.size) { - mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); - err = -EINVAL; + if (bf->len > dpld->bt_map.space) { + mif_info("ERR! Out of DPRAM boundary\n"); + err = -ERANGE; goto exit; } if (bf->len) - memcpy(buff, bf->data, bf->len); + memcpy16_to_io(buff, bf->data, bf->len); - init_completion(&dpld->udl_cmd_complete); +#ifdef CONFIG_CDMA_MODEM_CBP72 + init_completion(&dpld->udl_cmpl); +#endif - if (bf->req) + if (bf->req) { dpld->send_intr(dpld, (u16)bf->req); + mif_debug("send intr 0x%04X\n", (u16)bf->req); + } if (bf->resp) { - err = _cbp72_edpram_wait_resp(dpld, bf->resp); + err = cbp_udl_wait_resp(dpld, bf->resp); if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); + mif_err("ERR! cbp_udl_wait_resp fail (err %d)\n", err); goto exit; } else if (err == bf->resp) { err = skb->len; @@ -133,24 +112,14 @@ exit: return err; } -static int cbp72_download_binary(struct dpram_link_device *dpld, - struct sk_buff *skb) -{ - if (dpld->dp_type == EXT_DPRAM) - return _cbp72_edpram_download_bin(dpld, skb); - else - return -ENODEV; -} - -static int cbp72_dump_start(struct dpram_link_device *dpld) +static int cbp_dump_start(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; u8 *dest = dpld->ul_map.buff; int ret; - ld->mode = LINK_MODE_ULOAD; + dpld->ld.mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->dump_timer); + ret = del_timer(&dpld->crash_timer); wake_lock(&dpld->wlock); iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic); @@ -161,53 +130,65 @@ static int cbp72_dump_start(struct dpram_link_device *dpld) iowrite8((u8)0x0, dest + 3); iowrite8((u8)END_FLAG, dest + 4); - init_completion(&dpld->dump_start_complete); + init_completion(&dpld->crash_cmpl); return 0; } -static int _cbp72_edpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dump, unsigned char __user *target) +static int cbp_dump_update(struct dpram_link_device *dpld, unsigned long arg) { - struct link_device *ld = &dpld->ld; - struct ul_header header; + struct dpram_dump_arg dump; + struct dpram_udl_header header; + unsigned char __user *target = (unsigned char __user *)arg; + int err = 0; + int resp = 0; u8 *dest = NULL; u8 *buff = NULL; - u16 plen = 0; - int err = 0; - int ret = 0; + u8 *header_buff = NULL; int buff_size = 0; + u16 plen = 0; - mif_debug("\n"); - - init_completion(&dpld->udl_cmd_complete); - - mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp); + err = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); + if (err < 0) { + mif_err("ERR! ARG copy_from_user fail (err %d)\n", err); + goto exit; + } + mif_debug("req %x, resp %x", dump.req, dump.resp); - if (dump->req) - dpld->send_intr(dpld, (u16)dump->req); + if (dump.req) + dpld->send_intr(dpld, (u16)dump.req); - if (dump->resp) { - err = _cbp72_edpram_wait_resp(dpld, dump->resp); + if (dump.resp) { + resp = err = cbp_udl_wait_resp(dpld, dump.resp); if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); + mif_info("ERR! wait_response fail (err %d)\n", err); goto exit; } } - if (dump->cmd) - return err; + if (dump.cmd) + goto exit; dest = (u8 *)dpld->ul_map.buff; - header.bop = *(u8 *)(dest); - header.total_frame = *(u16 *)(dest + 1); - header.curr_frame = *(u16 *)(dest + 3); - header.len = *(u16 *)(dest + 5); + header_buff = vmalloc(sizeof(struct dpram_udl_header)); + if (!header_buff) { + err = -ENOMEM; + goto exit; + } + + memcpy16_from_io(header_buff, dest, sizeof(struct dpram_udl_header)); + + header.bop = *(u8 *)(header_buff); + header.num_frames = *(u16 *)(header_buff + 1); + header.curr_frame = *(u16 *)(header_buff + 3); + header.len = *(u16 *)(header_buff + 5); +#ifdef CONFIG_CDMA_MODEM_CBP82 + header.pad = *(u8 *)(header_buff + 7); +#endif - mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n", - ld->name, header.total_frame, header.curr_frame, header.len); + mif_debug("total frames:%d, current frame:%d, data len:%d\n", + header.num_frames, header.curr_frame, header.len); plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN); @@ -217,22 +198,26 @@ static int _cbp72_edpram_upload(struct dpram_link_device *dpld, goto exit; } - memcpy(buff, dest + sizeof(struct ul_header), plen); - ret = copy_to_user(dump->buff, buff, plen); - if (ret < 0) { - mif_info("%s: ERR! dump copy_to_user fail\n", ld->name); + memcpy16_from_io(buff, dest + sizeof(struct dpram_udl_header), plen); + err = copy_to_user(dump.buff, buff, plen); + if (err) { + mif_info("ERR! DUMP copy_to_user fail\n"); err = -EIO; goto exit; } - buff_size = plen; - ret = copy_to_user(target + 4, &buff_size, sizeof(int)); - if (ret < 0) { - mif_info("%s: ERR! size copy_to_user fail\n", ld->name); + buff_size = plen; + err = copy_to_user(target + 4, &buff_size, sizeof(int)); + if (err) { + mif_info("ERR! SIZE copy_to_user fail\n"); err = -EIO; goto exit; } + /* Return response value */ + if (err == 0) + err = resp; + vfree(buff); return err; @@ -243,82 +228,169 @@ exit: wake_unlock(&dpld->wlock); return err; } +#endif -static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg) -{ - struct link_device *ld = &dpld->ld; - struct dpram_dump_arg dump; - int ret; +#if defined(CONFIG_LTE_MODEM_CMC221) +/* +** For CMC221 SFR for IDPRAM +*/ +#define CMC_INT2CP_REG 0x10 /* Interrupt to CP */ +#define CMC_INT2AP_REG 0x50 +#define CMC_CLR_INT_REG 0x28 /* Clear Interrupt to AP */ +#define CMC_RESET_REG 0x3C +#define CMC_PUT_REG 0x40 /* AP->CP reg for hostbooting */ +#define CMC_GET_REG 0x50 /* CP->AP reg for hostbooting */ - ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); - if (ret < 0) { - mif_info("%s: ERR! copy_from_user fail\n", ld->name); - return ret; - } +/* +** For host (flashless) booting via DPRAM +*/ +#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 +#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 +#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A +#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD - return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg); -} +#define CMC22x_HOST_DOWN_START 0x1234 +#define CMC22x_HOST_DOWN_END 0x4321 +#define CMC22x_REG_NV_DOWN_END 0xABCD +#define CMC22x_CAL_NV_DOWN_END 0xDCBA -static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); +#define CMC22x_1ST_BUFF_READY 0xAAAA +#define CMC22x_2ND_BUFF_READY 0xBBBB +#define CMC22x_1ST_BUFF_FULL 0x1111 +#define CMC22x_2ND_BUFF_FULL 0x2222 + +#define CMC22x_CP_RECV_NV_END 0x8888 +#define CMC22x_CP_CAL_OK 0x4F4B +#define CMC22x_CP_CAL_BAD 0x4552 +#define CMC22x_CP_DUMP_END 0xFADE - ld->mode = LINK_MODE_DLOAD; +#define CMC22x_DUMP_BUFF_SIZE 8192 - iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); +/* CMC221 IDPRAM SFR */ +struct cmc221_idpram_sfr { + u16 __iomem *int2cp; + u16 __iomem *int2ap; + u16 __iomem *clr_int2ap; + u16 __iomem *reset; + u16 __iomem *msg2cp; + u16 __iomem *msg2ap; +}; - return 0; -} +struct cmc221_boot_img { + char *addr; + int size; + enum cp_boot_mode mode; + unsigned req; + unsigned resp; +}; -static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) +static struct cmc221_idpram_sfr cmc_sfr; + +static void cmc221_init_boot_map(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int err = 0; + struct memif_boot_map *bt_map = &dpld->bt_map; - switch (cmd) { - case IOCTL_MODEM_DL_START: - err = cbp72_set_dl_magic(ld, iod); - if (err < 0) - mif_err("%s: ERR! set_dl_magic fail\n", ld->name); - break; + bt_map->buff = dpld->base; + bt_map->space = dpld->size; + bt_map->req = (u32 *)(dpld->base + DP_BOOT_REQ_OFFSET); + bt_map->resp = (u32 *)(dpld->base + DP_BOOT_RESP_OFFSET); +} - default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - break; - } +static void cmc221_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)dpld->base; +} - return err; +static void cmc221_init_ul_map(struct dpram_link_device *dpld) +{ + dpld->ul_map.magic = (u32 *)dpld->base; + dpld->ul_map.buff = (u8 *)dpld->base; } -#endif -#if defined(CONFIG_LTE_MODEM_CMC221) -static void cmc221_init_boot_map(struct dpram_link_device *dpld) +static void cmc221_init_ipc_map(struct dpram_link_device *dpld) { - struct dpram_boot_map *bt_map = &dpld->bt_map; + struct dpram_ipc_16k_map *dpram_map; + struct dpram_ipc_device *dev; + u8 __iomem *sfr_base = dpld->sfr_base; + + dpram_map = (struct dpram_ipc_16k_map *)dpld->base; + + /* Magic code and access enable fields */ + dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; + dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; + + /* FMT */ + dev = &dpld->ipc_map.dev[IPC_FMT]; + + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; + + dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head; + dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail; + dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0]; + dev->txq.size = DP_16K_FMT_TX_BUFF_SZ; + + dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head; + dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail; + dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0]; + dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_F; + dev->mask_res_ack = INT_MASK_RES_ACK_F; + dev->mask_send = INT_MASK_SEND_F; + + /* RAW */ + dev = &dpld->ipc_map.dev[IPC_RAW]; + + strcpy(dev->name, "RAW"); + dev->id = IPC_RAW; + + dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head; + dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail; + dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0]; + dev->txq.size = DP_16K_RAW_TX_BUFF_SZ; + + dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head; + dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail; + dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0]; + dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_R; + dev->mask_res_ack = INT_MASK_RES_ACK_R; + dev->mask_send = INT_MASK_SEND_R; + + /* SFR */ + cmc_sfr.int2cp = (u16 __iomem *)(sfr_base + CMC_INT2CP_REG); + cmc_sfr.int2ap = (u16 __iomem *)(sfr_base + CMC_INT2AP_REG); + cmc_sfr.clr_int2ap = (u16 __iomem *)(sfr_base + CMC_CLR_INT_REG); + cmc_sfr.reset = (u16 __iomem *)(sfr_base + CMC_RESET_REG); + cmc_sfr.msg2cp = (u16 __iomem *)(sfr_base + CMC_PUT_REG); + cmc_sfr.msg2ap = (u16 __iomem *)(sfr_base + CMC_GET_REG); + + /* Interrupt ports */ + dpld->ipc_map.mbx_cp2ap = cmc_sfr.int2ap; + dpld->ipc_map.mbx_ap2cp = cmc_sfr.int2cp; +} - bt_map->buff = dpld->dp_base; - bt_map->size = dpld->dp_size; - bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET); - bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET); +static inline void cmc221_idpram_reset(struct dpram_link_device *dpld) +{ + iowrite16(1, cmc_sfr.reset); } -static void cmc221_init_dl_map(struct dpram_link_device *dpld) +static inline u16 cmc221_idpram_recv_msg(struct dpram_link_device *dpld) { - dpld->dl_map.magic = (u32 *)dpld->dp_base; - dpld->dl_map.buff = (u8 *)dpld->dp_base; + return ioread16(cmc_sfr.msg2ap); } -static void cmc221_init_ul_map(struct dpram_link_device *dpld) +static inline void cmc221_idpram_send_msg(struct dpram_link_device *dpld, + u16 msg) { - dpld->ul_map.magic = (u32 *)dpld->dp_base; - dpld->ul_map.buff = (u8 *)dpld->dp_base; + iowrite16(msg, cmc_sfr.msg2cp); } -static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) { - struct link_device *ld = &dpld->ld; int count = 50000; u32 rcvd = 0; @@ -328,16 +400,14 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) if (rcvd == resp) break; - rcvd = dpld->dpctl->recv_msg(); + rcvd = cmc221_idpram_recv_msg(dpld); if (rcvd == 0x9999) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%04X\n", rcvd); panic("CP Crash ... BAD CRC in CP"); } if (count-- < 0) { - mif_info("%s: Invalid resp 0x%08X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%08X\n", rcvd); return -EAGAIN; } @@ -345,20 +415,19 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) } } else { while (1) { - rcvd = dpld->dpctl->recv_msg(); + rcvd = cmc221_idpram_recv_msg(dpld); if (rcvd == resp) break; if (resp == CMC22x_CP_RECV_NV_END && rcvd == CMC22x_CP_CAL_BAD) { - mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name); + mif_info("invalid resp CMC22x_CP_CAL_BAD\n"); break; } if (count-- < 0) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%04X\n", rcvd); return -EAGAIN; } @@ -369,110 +438,104 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) return rcvd; } -static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg) +static int cmc221_xmit_boot(struct dpram_link_device *dpld, unsigned long arg) { struct link_device *ld = &dpld->ld; u8 __iomem *bt_buff = dpld->bt_map.buff; - struct dpram_boot_img cp_img; + struct cmc221_boot_img cp_img; u8 *img_buff = NULL; int err = 0; int cnt = 0; + mif_info("+++\n"); ld->mode = LINK_MODE_BOOT; - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + dpld->dpram->setup_speed(DPRAM_SPEED_LOW); /* Test memory... After testing, memory is cleared. */ - if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) { - mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name); + if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.space) < 0) { + mif_info("ERR! mif_test_dpram fail!\n"); ld->mode = LINK_MODE_OFFLINE; return -EIO; } - memset(&cp_img, 0, sizeof(struct dpram_boot_img)); + memset(&cp_img, 0, sizeof(struct cmc221_boot_img)); /* Get information about the boot image */ - err = copy_from_user(&cp_img, arg, sizeof(cp_img)); - mif_info("%s: CP image addr = 0x%08X, size = %d\n", - ld->name, (int)cp_img.addr, cp_img.size); + err = copy_from_user(&cp_img, (void __user *)arg, sizeof(cp_img)); + mif_info("CP image addr = 0x%08X, size = %d\n", + (int)cp_img.addr, cp_img.size); /* Alloc a buffer for the boot image */ - img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL); + img_buff = kzalloc(dpld->bt_map.space, GFP_KERNEL); if (!img_buff) { - mif_info("%s: ERR! kzalloc fail\n", ld->name); + mif_info("ERR! kzalloc fail\n"); ld->mode = LINK_MODE_OFFLINE; return -ENOMEM; } /* Copy boot image from the user space to the image buffer */ - err = copy_from_user(img_buff, cp_img.addr, cp_img.size); + err = copy_from_user(img_buff, (void __user *)cp_img.addr, cp_img.size); /* Copy boot image to DPRAM and verify it */ memcpy(bt_buff, img_buff, cp_img.size); if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) { - mif_info("%s: ERR! Boot may be broken!!!\n", ld->name); + mif_info("ERR! Boot may be broken!!!\n"); goto err; } - dpld->dpctl->reset(); + cmc221_idpram_reset(dpld); usleep_range(1000, 2000); - if (cp_img.mode == HOST_BOOT_MODE_NORMAL) { - mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name); - mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req); + if (cp_img.mode == CP_BOOT_MODE_NORMAL) { + mif_info("CP_BOOT_MODE_NORMAL\n"); + mif_info("send req 0x%08X\n", cp_img.req); iowrite32(cp_img.req, dpld->bt_map.req); /* Wait for cp_img.resp for up to 2 seconds */ - mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp); + mif_info("wait resp 0x%08X\n", cp_img.resp); while (ioread32(dpld->bt_map.resp) != cp_img.resp) { cnt++; usleep_range(1000, 2000); if (cnt > 1000) { - mif_info("%s: ERR! Invalid resp 0x%08X\n", - ld->name, ioread32(dpld->bt_map.resp)); + mif_info("ERR! invalid resp 0x%08X\n", + ioread32(dpld->bt_map.resp)); goto err; } } } else { - mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name); + mif_info("CP_BOOT_MODE_DUMP\n"); } kfree(img_buff); - mif_info("%s: Send BOOT done\n", ld->name); + mif_info("send BOOT done\n"); - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + mif_info("---\n"); return 0; err: ld->mode = LINK_MODE_OFFLINE; kfree(img_buff); - mif_info("%s: ERR! Boot send fail!!!\n", ld->name); + mif_err("FAIL!!!\n"); + mif_info("---\n"); return -EIO; } -static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg) -{ - if (dpld->dp_type == CP_IDPRAM) - return _cmc221_idpram_send_boot(dpld, arg); - else - return -ENODEV; -} - -static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, +static int cmc221_idpram_download_bin(struct dpram_link_device *dpld, struct sk_buff *skb) { int err = 0; int ret = 0; - struct link_device *ld = &dpld->ld; struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; u8 __iomem *buff = (dpld->bt_map.buff + bf->offset); - if ((bf->offset + bf->len) > dpld->bt_map.size) { - mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); + if ((bf->offset + bf->len) > dpld->bt_map.space) { + mif_info("ERR! out of DPRAM boundary\n"); err = -EINVAL; goto exit; } @@ -481,17 +544,16 @@ static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, memcpy(buff, bf->data, bf->len); if (bf->req) - dpld->dpctl->send_msg((u16)bf->req); + cmc221_idpram_send_msg(dpld, (u16)bf->req); if (bf->resp) { - err = _cmc221_idpram_wait_resp(dpld, bf->resp); + err = cmc221_idpram_wait_resp(dpld, bf->resp); if (err < 0) - mif_info("%s: ERR! wait_response fail (err %d)\n", - ld->name, err); + mif_info("ERR! wait_resp fail (err %d)\n", err); } if (bf->req == CMC22x_CAL_NV_DOWN_END) - mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name); + mif_info("request CMC22x_CAL_NV_DOWN_END\n"); exit: if (err < 0) @@ -504,89 +566,84 @@ exit: return ret; } -static int cmc221_download_binary(struct dpram_link_device *dpld, +static int cmc221_xmit_binary(struct dpram_link_device *dpld, struct sk_buff *skb) { - if (dpld->dp_type == CP_IDPRAM) - return _cmc221_idpram_download_bin(dpld, skb); + if (dpld->type == CP_IDPRAM) + return cmc221_idpram_download_bin(dpld, skb); else return -ENODEV; } static int cmc221_dump_start(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int ret; - - ld->mode = LINK_MODE_ULOAD; + dpld->ld.mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->dump_timer); + del_timer(&dpld->crash_timer); wake_lock(&dpld->wlock); - dpld->dump_rcvd = 0; + dpld->crash_rcvd = 0; iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic); - init_completion(&dpld->dump_start_complete); + init_completion(&dpld->crash_cmpl); return 0; } -static void _cmc221_idpram_wait_dump(unsigned long arg) +static void cmc221_idpram_wait_dump(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; u16 msg; - msg = dpld->dpctl->recv_msg(); + msg = cmc221_idpram_recv_msg(dpld); if (msg == CMC22x_CP_DUMP_END) { - complete_all(&dpld->dump_recv_done); + complete_all(&dpld->crash_cmpl); return; } - if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (((dpld->crash_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { + complete_all(&dpld->crash_cmpl); return; } - if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (((dpld->crash_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { + complete_all(&dpld->crash_cmpl); return; } - mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, - _cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, + cmc221_idpram_wait_dump, (unsigned long)dpld); } -static int _cmc221_idpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dumparg) +static int cmc221_idpram_upload(struct dpram_link_device *dpld, + struct dpram_dump_arg *dumparg) { - struct link_device *ld = &dpld->ld; int ret; u8 __iomem *src; int buff_size = CMC22x_DUMP_BUFF_SIZE; - if ((dpld->dump_rcvd & 0x1) == 0) - dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY); + if ((dpld->crash_rcvd & 0x1) == 0) + cmc221_idpram_send_msg(dpld, CMC22x_1ST_BUFF_READY); else - dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY); + cmc221_idpram_send_msg(dpld, CMC22x_2ND_BUFF_READY); - init_completion(&dpld->dump_recv_done); + init_completion(&dpld->crash_cmpl); - mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, - _cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, + cmc221_idpram_wait_dump, (unsigned long)dpld); - ret = wait_for_completion_interruptible_timeout( - &dpld->dump_recv_done, DUMP_TIMEOUT); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, DUMP_TIMEOUT); if (!ret) { - mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name); + mif_info("ERR! no dump from CP!!!\n"); goto err_out; } - if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) { - mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name); + if (cmc221_idpram_recv_msg(dpld) == CMC22x_CP_DUMP_END) { + mif_info("recv CMC22x_CP_DUMP_END\n"); return 0; } - if ((dpld->dump_rcvd & 0x1) == 0) + if ((dpld->crash_rcvd & 0x1) == 0) src = dpld->ul_map.buff; else src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE; @@ -595,52 +652,64 @@ static int _cmc221_idpram_upload(struct dpram_link_device *dpld, ret = copy_to_user(dumparg->buff, dpld->buff, buff_size); if (ret < 0) { - mif_info("%s: ERR! copy_to_user fail\n", ld->name); + mif_info("ERR! copy_to_user fail\n"); goto err_out; } - dpld->dump_rcvd++; + dpld->crash_rcvd++; return buff_size; err_out: return -EIO; } -static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg) +static int cmc221_dump_update(struct dpram_link_device *dpld, unsigned long arg) { - struct link_device *ld = &dpld->ld; struct dpram_dump_arg dump; int ret; ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); if (ret < 0) { - mif_info("%s: ERR! copy_from_user fail\n", ld->name); + mif_info("ERR! copy_from_user fail\n"); return ret; } - return _cmc221_idpram_upload(dpld, &dump); + return cmc221_idpram_upload(dpld, &dump); } -static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) +static void cmc221_idpram_clr_int2ap(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int err = 0; + iowrite16(0xFFFF, cmc_sfr.clr_int2ap); +} - switch (cmd) { - case IOCTL_DPRAM_SEND_BOOT: - err = cmc221_download_boot(dpld, (void *)arg); - if (err < 0) - mif_info("%s: ERR! download_boot fail\n", ld->name); - break; +static int cmc221_idpram_wakeup(struct dpram_link_device *dpld) +{ + int cnt = 0; - default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - break; + gpio_set_value(dpld->gpio_cp_wakeup, 1); + + while (!gpio_get_value(dpld->gpio_cp_status)) { + if (cnt++ > 10) { + if (in_irq()) + mif_err("ERR! gpio_cp_status == 0 in IRQ\n"); + else + mif_err("ERR! gpio_cp_status == 0\n"); + return -EACCES; + } + + mif_info("gpio_cp_status == 0 (cnt %d)\n", cnt); + if (in_interrupt()) + udelay(1000); + else + usleep_range(1000, 2000); } - return err; + return 0; +} + +static void cmc221_idpram_sleep(struct dpram_link_device *dpld) +{ + gpio_set_value(dpld->gpio_cp_wakeup, 0); } #endif @@ -652,17 +721,44 @@ enum qc_dload_tag { QC_DLOAD_TAG_MAX }; +struct qc_dpram_boot_map { + u8 __iomem *buff; + u16 __iomem *frame_size; + u16 __iomem *tag; + u16 __iomem *count; +}; + +struct qc_dpram_udl_param { + unsigned char *addr; + unsigned int size; + unsigned int count; + unsigned int tag; +}; + +struct qc_dpram_udl_check { + unsigned int total_size; + unsigned int rest_size; + unsigned int send_size; + unsigned int copy_start; + unsigned int copy_complete; + unsigned int boot_complete; +}; + +static struct qc_dpram_boot_map qc_bt_map; +static struct qc_dpram_udl_param qc_udl_param; +static struct qc_dpram_udl_check qc_udl_check; + static void qc_dload_task(unsigned long data); static void qc_init_boot_map(struct dpram_link_device *dpld) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; + struct modemlink_dpram_data *dpram = dpld->dpram; - bt_map->buff = dpld->dp_base; - bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); - bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); - bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); + qbt_map->buff = dpld->base; + qbt_map->frame_size = (u16 *)(dpld->base + dpram->boot_size_offset); + qbt_map->tag = (u16 *)(dpld->base + dpram->boot_tag_offset); + qbt_map->count = (u16 *)(dpld->base + dpram->boot_count_offset); tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); } @@ -673,15 +769,15 @@ static int qc_prepare_download(struct dpram_link_device *dpld) int count = 0; while (1) { - if (dpld->udl_check.copy_start) { - dpld->udl_check.copy_start = 0; + if (qc_udl_check.copy_start) { + qc_udl_check.copy_start = 0; break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; - if (count > 200) { + if (count > 300) { mif_err("ERR! count %d\n", count); return -1; } @@ -690,32 +786,33 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void _qc_do_download(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static void qc_do_download(struct dpram_link_device *dpld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; - if (param->size <= dpld->dpctl->max_boot_frame_size) { - memcpy(bt_map->buff, param->addr, param->size); - iowrite16(param->size, bt_map->frame_size); - iowrite16(param->tag, bt_map->tag); - iowrite16(param->count, bt_map->count); + if (param->size <= dpld->dpram->max_boot_frame_size) { + memcpy(qbt_map->buff, param->addr, param->size); + iowrite16(param->size, qbt_map->frame_size); + iowrite16(param->tag, qbt_map->tag); + iowrite16(param->count, qbt_map->count); dpld->send_intr(dpld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int _qc_download(struct dpram_link_device *dpld, void *arg, +static int qc_download(struct dpram_link_device *dpld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + retval = copy_from_user((void *)¶m, (void __user *)arg, + sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail\n"); return -1; @@ -729,24 +826,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, memset(img, 0, param.size); memcpy(img, param.addr, param.size); - dpld->udl_check.total_size = param.size; - dpld->udl_check.rest_size = param.size; - dpld->udl_check.send_size = 0; - dpld->udl_check.copy_complete = 0; + qc_udl_check.total_size = param.size; + qc_udl_check.rest_size = param.size; + qc_udl_check.send_size = 0; + qc_udl_check.copy_complete = 0; - dpld->udl_param.addr = img; - dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + qc_udl_param.addr = img; + qc_udl_param.size = dpld->dpram->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - dpld->udl_param.count = 1; + qc_udl_param.count = 1; else - dpld->udl_param.count = param.count; - dpld->udl_param.tag = tag; + qc_udl_param.count = param.count; + qc_udl_param.tag = tag; - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) + qc_udl_param.size = qc_udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(dpld, &dpld->udl_param); + qc_do_download(dpld, &qc_udl_param); /* Wait for completion */ @@ -756,18 +853,18 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (dpld->udl_check.copy_complete) { - dpld->udl_check.copy_complete = 0; + if (qc_udl_check.copy_complete) { + qc_udl_check.copy_complete = 0; retval = 0; break; } - msleep(10); + usleep_range(10000, 11000); count++; if (count > cnt_limit) { - dpld->udl_check.total_size = 0; - dpld->udl_check.rest_size = 0; + qc_udl_check.total_size = 0; + qc_udl_check.rest_size = 0; mif_err("ERR! count %d\n", count); retval = -1; break; @@ -781,51 +878,51 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, static int qc_download_binary(struct dpram_link_device *dpld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + return qc_download(dpld, arg, QC_DLOAD_TAG_BIN); } static int qc_download_nv(struct dpram_link_device *dpld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); + return qc_download(dpld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { struct dpram_link_device *dpld = (struct dpram_link_device *)data; - dpld->udl_check.send_size += dpld->udl_param.size; - dpld->udl_check.rest_size -= dpld->udl_param.size; + qc_udl_check.send_size += qc_udl_param.size; + qc_udl_check.rest_size -= qc_udl_param.size; - dpld->udl_param.addr += dpld->udl_param.size; + qc_udl_param.addr += qc_udl_param.size; - if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { - dpld->udl_check.copy_complete = 1; - dpld->udl_param.tag = 0; + if (qc_udl_check.send_size >= qc_udl_check.total_size) { + qc_udl_check.copy_complete = 1; + qc_udl_param.tag = 0; return; } - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) + qc_udl_param.size = qc_udl_check.rest_size; - dpld->udl_param.count += 1; + qc_udl_param.count += 1; - _qc_do_download(dpld, &dpld->udl_param); + qc_do_download(dpld, &qc_udl_param); } static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) { switch (cmd) { case 0x1234: - dpld->udl_check.copy_start = 1; + qc_udl_check.copy_start = 1; break; case 0xDBAB: - if (dpld->udl_check.total_size) + if (qc_udl_check.total_size) tasklet_schedule(&dpld->dl_tsk); break; case 0xABCD: - dpld->udl_check.boot_complete = 1; + qc_udl_check.boot_complete = 1; break; default: @@ -843,12 +940,12 @@ static int qc_boot_start(struct dpram_link_device *dpld) dpld->send_intr(dpld, mask); while (1) { - if (dpld->udl_check.boot_complete) { - dpld->udl_check.boot_complete = 0; + if (qc_udl_check.boot_complete) { + qc_udl_check.boot_complete = 0; break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -870,7 +967,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -900,27 +997,26 @@ static void qc_start_handler(struct dpram_link_device *dpld) static void qc_crash_log(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = dpld->get_rx_buff(dpld, IPC_FMT); + data = dpld->get_rxq_buff(dpld, IPC_FMT); memcpy(buf, data, (sizeof(buf) - 1)); - mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); + mif_info("PHONE ERR MSG\t| %s Crash\n", dpld->ld.mc->name); mif_info("PHONE ERR MSG\t| %s\n", buf); } -static int _qc_data_upload(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static int qc_data_upload(struct dpram_link_device *dpld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { + if (!gpio_get_value(dpld->gpio_int2ap)) { intval = dpld->recv_intr(dpld); if (intval == 0xDBAB) { break; @@ -930,7 +1026,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - msleep_interruptible(1); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -939,10 +1035,10 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - param->size = ioread16(bt_map->frame_size); - memcpy(param->addr, bt_map->buff, param->size); - param->tag = ioread16(bt_map->tag); - param->count = ioread16(bt_map->count); + param->size = ioread16(qbt_map->frame_size); + memcpy(param->addr, qbt_map->buff, param->size); + param->tag = ioread16(qbt_map->tag); + param->count = ioread16(qbt_map->count); dpld->send_intr(dpld, 0xDB12); @@ -961,7 +1057,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { + if (!gpio_get_value(dpld->gpio_int2ap)) { intval = dpld->recv_intr(dpld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { @@ -972,7 +1068,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - msleep_interruptible(1); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -993,17 +1089,18 @@ static int qc_uload_step1(struct dpram_link_device *dpld) static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) { int retval = 0; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + retval = copy_from_user((void *)¶m, (void __user *)arg, + sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail (err %d)\n", retval); return -1; } - retval = _qc_data_upload(dpld, ¶m); + retval = qc_data_upload(dpld, ¶m); if (retval < 0) { - mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); + mif_err("ERR! qc_data_upload fail (err %d)\n", retval); return -1; } @@ -1025,40 +1122,39 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) } static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { - struct link_device *ld = &dpld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: err = qc_prepare_download(dpld); if (err < 0) - mif_info("%s: ERR! prepare_download fail\n", ld->name); + mif_info("ERR! prepare_download fail\n"); break; case IOCTL_DPRAM_PHONEIMG_LOAD: err = qc_download_binary(dpld, (void *)arg); if (err < 0) - mif_info("%s: ERR! download_binary fail\n", ld->name); + mif_info("ERR! download_binary fail\n"); break; case IOCTL_DPRAM_NVDATA_LOAD: err = qc_download_nv(dpld, (void *)arg); if (err < 0) - mif_info("%s: ERR! download_nv fail\n", ld->name); + mif_info("ERR! download_nv fail\n"); break; case IOCTL_DPRAM_PHONE_BOOTSTART: err = qc_boot_start(dpld); if (err < 0) { - mif_info("%s: ERR! boot_start fail\n", ld->name); + mif_info("ERR! boot_start fail\n"); break; } err = qc_boot_post_process(dpld); if (err < 0) - mif_info("%s: ERR! boot_post_process fail\n", ld->name); + mif_info("ERR! boot_post_process fail\n"); break; @@ -1067,7 +1163,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step1(dpld); if (err < 0) { enable_irq(dpld->irq); - mif_info("%s: ERR! upload_step1 fail\n", ld->name); + mif_info("ERR! upload_step1 fail\n"); } break; @@ -1075,12 +1171,12 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step2(dpld, (void *)arg); if (err < 0) { enable_irq(dpld->irq); - mif_info("%s: ERR! upload_step2 fail\n", ld->name); + mif_info("ERR! upload_step2 fail\n"); } break; default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + mif_err("ERR! invalid cmd 0x%08X\n", cmd); err = -EINVAL; break; } @@ -1091,44 +1187,402 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, static irqreturn_t qc_dpram_irq_handler(int irq, void *data) { struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; + struct link_device *ld = &dpld->ld; + struct mem_status stat; u16 int2ap = 0; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + if (ld->mode == LINK_MODE_OFFLINE) { + int2ap = dpld->recv_intr(dpld); return IRQ_HANDLED; + } - int2ap = dpld->recv_intr(dpld); + dpld->get_dpram_status(dpld, RX, &stat); + int2ap = stat.int2ap; if (int2ap == INT_POWERSAFE_FAIL) { - mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); + mif_info("int2ap == INT_POWERSAFE_FAIL\n"); goto exit; } if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { qc_dload_cmd_handler(dpld, int2ap); goto exit; + } else if (int2ap == 0x4321 || int2ap == 0x5432) { + mif_err("ERR! CP error command (0x%04X)\n", int2ap); + goto exit; } if (likely(INT_VALID(int2ap))) - dpld->ipc_rx_handler(dpld, int2ap); + dpld->ipc_rx_handler(dpld, &stat); else - mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap); + mif_info("ERR! invalid intr 0x%04X\n", int2ap); exit: return IRQ_HANDLED; } #endif -static struct dpram_ext_op ext_op_set[] = { +#if defined(CONFIG_CDMA_MODEM_QSC6085) +#define CMD_CP_RAMDUMP_START_REQ 0x9200 +#define CMD_CP_RAMDUMP_SEND_REQ 0x9400 +#define CMD_CP_RAMDUMP_SEND_DONE_REQ 0x9600 + +#define CMD_CP_RAMDUMP_START_RESP 0x0300 +#define CMD_CP_RAMDUMP_SEND_RESP 0x0500 +#define CMD_CP_RAMDUMP_SEND_DONE_RESP 0x0700 + +#define QSC_UPLOAD_MODE (0x444D554C) +#define QSC_UPLOAD_MODE_COMPLETE (0xABCDEF90) + +#define RAMDUMP_CMD_TIMEOUT (5 * HZ) +#define QSC6085_RAM_SIZE (32 * 1024 * 1024) /* 32MB */ + +struct qsc6085_dump_command { + u32 addr; + u32 size; + u32 copyto_offset; +}; + +struct qsc6085_dump_status { + u32 dump_size; + u32 addr; + u32 rcvd; + u32 rest; +}; + +static struct qsc6085_dump_status qsc_dump_stat; + +static void qsc6085_dump_work(struct work_struct *work); + +static void qsc6085_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); +} + +static void qsc6085_init_ul_map(struct dpram_link_device *dpld) +{ + int magic_size = DP_ULOAD_MAGIC_SIZE; + int cmd_size = sizeof(struct qsc6085_dump_command); + int mbx_size = DP_MBX_SET_SIZE; + + dpld->ul_map.magic = (u32 *)dpld->base; + dpld->ul_map.cmd = dpld->base + magic_size; + dpld->ul_map.cmd_size = cmd_size; + dpld->ul_map.buff = dpld->base + magic_size + cmd_size; + dpld->ul_map.space = dpld->size - (magic_size + cmd_size + mbx_size); +} + +static void qsc6085_req_active_handler(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + mif_info("pda_active = %d\n", gpio_get_value(mc->gpio_pda_active)); + dpld->send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); +} + +static void qsc6085_error_display_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_err("recv 0xC9 (CRASH_EXIT)\n"); + mif_err("CP Crash: %s\n", dpld->get_rxq_buff(dpld, IPC_FMT)); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); +} + +static void qsc6085_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_info("recv 0xC8 (CP_START)\n"); + + mif_info("send 0xC1 (INIT_START)\n"); + dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_START)); + + dpld->reset_dpram_ipc(dpld); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("ERR! no iod\n"); + return; + } + iod->modem_state_changed(iod, STATE_ONLINE); + + mif_info("send 0xC2 (INIT_END)\n"); + dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); +} + +static void qsc6085_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + qsc6085_req_active_handler(dpld); + break; + + case INT_CMD_ERR_DISPLAY: +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + /* If modem crashes while PDA_SLEEP is in progres */ + dpld->pm_op->halt_suspend(dpld); +#endif + qsc6085_error_display_handler(dpld); + break; + + case INT_CMD_PHONE_START: + qsc6085_start_handler(dpld); + complete_all(&ld->init_cmpl); + break; + +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + case INT_CMD_IDPRAM_SUSPEND_ACK: + dpld->pm_op->power_down(dpld); + break; + + case INT_CMD_IDPRAM_WAKEUP_START: + dpld->pm_op->power_up(dpld); + break; +#endif + + case INT_CMD_NORMAL_POWER_OFF: + complete(&dpld->crash_cmpl); + qsc6085_error_display_handler(dpld); + break; + + default: + mif_err("unknown command 0x%04X\n", cmd); + break; + } +} + +static int qsc6085_download_firmware(struct dpram_link_device *dpld, + struct modem_firmware *fw) +{ + int ret = 0; + char __user *src = fw->binary; + int rest = fw->size; + char __iomem *dst = NULL; + unsigned long timeout; + u16 curr_frame = 0; + u16 len = 0; + struct dpram_udl_header header; + + header.bop = START_FLAG; + header.num_frames = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN); + mif_err("FW %d bytes = %d frames\n", fw->size, header.num_frames); + + while (rest > 0) { + curr_frame++; + len = min(rest, DP_DEFAULT_WRITE_LEN); + + header.curr_frame = curr_frame; + header.len = len; + mif_info(">>> frame# %u, len %u\n", curr_frame, len); + + dst = dpld->dl_map.buff; + memcpy(dst, &header, sizeof(header)); + + dst += sizeof(header); + ret = copy_from_user(dst, (void __user *)src, len); + if (ret < 0) { + mif_err("copy_from_user fail\n"); + return -EIO; + } + + dst += len; + src += len; + rest -= len; + + iowrite8(END_FLAG, (dst+3)); + + if (curr_frame == 1) { + dpld->send_intr(dpld, 0); + timeout = UDL_TIMEOUT; + } else { + dpld->send_intr(dpld, CMD_DL_SEND_REQ); + timeout = UDL_SEND_TIMEOUT; + } + + ret = wait_for_completion_timeout(&dpld->udl_cmpl, timeout); + if (!ret) { + mif_err("ERR! no response from CP\n"); + return -EIO; + } + } + + mif_err("send CMD_DL_DONE_REQ to CP\n"); + dpld->send_intr(dpld, CMD_DL_DONE_REQ); + + ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); + if (!ret) { + mif_err("ERR! no response from CP\n"); + return -EIO; + } + + return 0; +} + +static int qsc6085_dload_firmware(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct modem_firmware fw; + mif_err("+++\n"); + + ret = copy_from_user(&fw, (void __user *)arg, sizeof(fw)); + if (ret < 0) { + mif_err("ERR! copy_from_user fail!\n"); + return ret; + } + + ret = qsc6085_download_firmware(dpld, &fw); + + mif_err("---\n"); + return ret; +} + +static int qsc6085_dump_start(struct dpram_link_device *dpld) +{ + int ret; + struct link_device *ld = &dpld->ld; + struct modem_ctl *mc = ld->mc; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + mif_err("+++\n"); + + init_completion(&dpld->crash_cmpl); + INIT_DELAYED_WORK(&dpld->crash_dwork, qsc6085_dump_work); + + iowrite32(QSC_UPLOAD_MODE, &dpld->ul_map.magic); + + /* reset modem so that it goes to upload mode */ + /* ap does not need to reset cp during CRASH_EXIT case */ + if (gpio_get_value(mc->gpio_phone_active)) + mc->ops.modem_reset(mc); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_START_REQ); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, + RAMDUMP_CMD_TIMEOUT); + if (!ret) { + mif_err("ERR! no response to CP_RAMDUMP_START_REQ\n"); + dump_stat->dump_size = 0; + } else { + dump_stat->dump_size = QSC6085_RAM_SIZE; + dump_stat->addr = 0; + dump_stat->rcvd = 0; + dump_stat->rest = dump_stat->dump_size; + } + + queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_dump_update(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct link_device *ld = &dpld->ld; + struct io_device *iod = link_get_iod_with_format(ld, IPC_RAMDUMP); + struct memif_uload_map *ul_map = &dpld->ul_map; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + struct qsc6085_dump_command dump_cmd; + + while (iod->sk_rx_q.qlen > 0) + usleep_range(1000, 1100); + + memset(&dump_cmd, 0, sizeof(dump_cmd)); + dump_cmd.addr = dump_stat->addr; + dump_cmd.size = min(dump_stat->rest, ul_map->space); + dump_cmd.copyto_offset = 0x38000010; + + memcpy_toio(ul_map->cmd, &dump_cmd, ul_map->cmd_size); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_REQ); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, + RAMDUMP_CMD_TIMEOUT); + if (!ret) { + dump_stat->dump_size = 0; + mif_err("ERR! no response to CP_RAMDUMP_SEND_REQ\n"); + ret = -EIO; + goto exit; + } + + memcpy_fromio(dpld->buff, ul_map->buff, dump_cmd.size); + + ret = iod->recv(iod, ld, dpld->buff, dump_cmd.size); + if (ret < 0) + goto exit; + + dump_stat->addr += dump_cmd.size; + dump_stat->rcvd += dump_cmd.size; + dump_stat->rest -= dump_cmd.size; + mif_info("rest = %u bytes\n", dump_stat->rest); + + ret = dump_cmd.size; + +exit: + return ret; +} + +static void qsc6085_dump_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + int ret; + + dpld = container_of(work, struct dpram_link_device, crash_dwork.work); + ld = &dpld->ld; + + ret = qsc6085_dump_update(dpld, 0); + if (ret > 0 && dump_stat->rest > 0) + queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); +} + +static int qsc6085_dump_finish(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct completion *cmpl = &dpld->crash_cmpl; + mif_err("+++\n"); + + init_completion(cmpl); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_DONE_REQ); + + ret = wait_for_completion_timeout(cmpl, RAMDUMP_CMD_TIMEOUT); + if (!ret) { + mif_err("ERR! no response to CP_RAMDUMP_SEND_DONE_REQ\n"); + ret = -EIO; + } + + mif_err("---\n"); + return ret; +} +#endif + +static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = { #ifdef CONFIG_CDMA_MODEM_CBP72 [VIA_CBP72] = { .exist = 1, - .init_boot_map = cbp72_init_boot_map, - .init_dl_map = cbp72_init_dl_map, - .download_binary = cbp72_download_binary, - .dump_start = cbp72_dump_start, - .dump_update = cbp72_dump_update, - .ioctl = cbp72_ioctl, + .init_boot_map = cbp_init_boot_map, + .init_dl_map = cbp_init_dl_map, + .xmit_binary = cbp_xmit_binary, + .dump_start = cbp_dump_start, + .dump_update = cbp_dump_update, + }, +#endif +#ifdef CONFIG_CDMA_MODEM_CBP82 + [VIA_CBP82] = { + .exist = 1, + .init_boot_map = cbp_init_boot_map, + .init_dl_map = cbp_init_dl_map, + .xmit_binary = cbp_xmit_binary, + .dump_start = cbp_dump_start, + .dump_update = cbp_dump_update, }, #endif #ifdef CONFIG_LTE_MODEM_CMC221 @@ -1137,10 +1591,14 @@ static struct dpram_ext_op ext_op_set[] = { .init_boot_map = cmc221_init_boot_map, .init_dl_map = cmc221_init_dl_map, .init_ul_map = cmc221_init_ul_map, - .download_binary = cmc221_download_binary, + .init_ipc_map = cmc221_init_ipc_map, + .xmit_boot = cmc221_xmit_boot, + .xmit_binary = cmc221_xmit_binary, .dump_start = cmc221_dump_start, .dump_update = cmc221_dump_update, - .ioctl = cmc221_ioctl, + .clear_int2ap = cmc221_idpram_clr_int2ap, + .wakeup = cmc221_idpram_wakeup, + .sleep = cmc221_idpram_sleep, }, #endif #if defined(CONFIG_CDMA_MODEM_MDM6600) @@ -1163,6 +1621,18 @@ static struct dpram_ext_op ext_op_set[] = { .irq_handler = qc_dpram_irq_handler, }, #endif +#if defined(CONFIG_CDMA_MODEM_QSC6085) + [QC_QSC6085] = { + .exist = 1, + .init_dl_map = qsc6085_init_dl_map, + .init_ul_map = qsc6085_init_ul_map, + .cmd_handler = qsc6085_command_handler, + .firm_update = qsc6085_dload_firmware, + .dump_start = qsc6085_dump_start, + .dump_update = qsc6085_dump_update, + .dump_finish = qsc6085_dump_finish, + }, +#endif }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) @@ -1173,3 +1643,422 @@ struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) return NULL; } +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM +#define GPIO_IDPRAM_SFN S3C_GPIO_SFN(2) + +#define MAX_CHECK_RETRY_CNT 5 +#define MAX_RESUME_TRY_CNT 5 + +static bool s5p_idpram_is_pm_locked(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + struct idpram_pm_data *pm_data = &dpld->pm_data; + + /* If PM is in SUSPEND */ + if (atomic_read(&pm_data->pm_lock) > 0) { + mif_info("in SUSPEND\n"); + return true; + } + + /* If AP is in or into LPA */ + if (!gpio_get_value(mc->gpio_pda_active)) { + mif_info("in LPA\n"); + return true; + } + + return false; +} + +static void s5p_idpram_set_pm_lock(struct dpram_link_device *dpld, int lock) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + + /* 0 = unlock, 1 = lock */ + switch (lock) { + case 0: + if (atomic_read(&pm_data->pm_lock)) + atomic_set(&pm_data->pm_lock, lock); + break; + + case 1: + if (!atomic_read(&pm_data->pm_lock)) + atomic_set(&pm_data->pm_lock, lock); + break; + + default: + break; + } +} + +static void s5p_idpram_try_resume(struct work_struct *work) +{ + struct idpram_pm_data *pm_data; + struct dpram_link_device *dpld; + struct link_device *ld; + unsigned long delay; + u16 cmd; + mif_info("+++\n"); + + pm_data = container_of(work, struct idpram_pm_data, resume_dwork.work); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + ld = &dpld->ld; + + if (pm_data->last_msg == INT_CMD(INT_CMD_IDPRAM_RESUME_REQ)) { + pm_data->last_msg = 0; + + s5p_idpram_set_pm_lock(dpld, 0); + wake_unlock(&pm_data->hold_wlock); + + delay = msecs_to_jiffies(10); + schedule_delayed_work(&pm_data->tx_dwork, delay); + + mif_info("%s resumed\n", ld->name); + goto exit; + } + + if (pm_data->resume_try_cnt++ < MAX_RESUME_TRY_CNT) { + mif_info("%s not resumed yet\n", ld->name); + + cmd = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); + mif_info("send IDPRAM_RESUME_REQ (0x%X)\n", cmd); + dpld->send_intr(dpld, cmd); + + delay = msecs_to_jiffies(200); + schedule_delayed_work(&pm_data->resume_dwork, delay); + } else { + struct io_device *iod; + mif_err("ERR! %s resume T-I-M-E-O-U-T\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + wake_unlock(&pm_data->hold_wlock); + + /* hold wakelock until uevnet sent to rild */ + wake_lock_timeout(&pm_data->hold_wlock, HZ*7); + s5p_idpram_set_pm_lock(dpld, 0); + } + +exit: + mif_info("---\n"); +} + +static irqreturn_t s5p_cp_dump_irq_handler(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static irqreturn_t s5p_ap_wakeup_irq_handler(int irq, void *data) +{ + struct idpram_pm_data *pm_data = data; + wake_lock_timeout(&pm_data->ap_wlock, HZ*5); + return IRQ_HANDLED; +} + +static void s5p_idpram_power_down(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK); + complete(&pm_data->down_cmpl); + + mif_info("---\n"); +} + +static void s5p_idpram_power_up(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); + pm_data->pm_state = IDPRAM_PM_ACTIVE; + + mif_info("---\n"); +} + +static void s5p_idpram_halt_suspend(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + complete(&pm_data->down_cmpl); + + mif_info("---\n"); +} + +static int s5p_idpram_prepare_suspend(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct modem_ctl *mc = dpld->ld.mc; + struct completion *cmpl; + unsigned long timeout; + unsigned long rest; + int cnt = 0; + u16 cmd = INT_CMD(INT_CMD_IDPRAM_SUSPEND_REQ); + mif_info("+++\n"); + + pm_data->pm_state = IDPRAM_PM_SUSPEND_PREPARE; + pm_data->last_msg = 0; + s5p_idpram_set_pm_lock(dpld, 1); + + /* + * Because, if dpram was powered down, cp dpram random intr was + * ocurred. so, fixed by muxing cp dpram intr pin to GPIO output + * high,.. + */ + gpio_set_value(dpld->gpio_int2cp, 1); + s3c_gpio_cfgpin(dpld->gpio_int2cp, S3C_GPIO_OUTPUT); + + /* prevent PDA_ACTIVE status is low */ + gpio_set_value(mc->gpio_pda_active, 1); + + cmpl = &pm_data->down_cmpl; + timeout = IDPRAM_SUSPEND_REQ_TIMEOUT; + cnt = 0; + do { + init_completion(cmpl); + + mif_info("send IDPRAM_SUSPEND_REQ (0x%X)\n", cmd); + dpld->send_intr(dpld, cmd); + + rest = wait_for_completion_timeout(cmpl, timeout); + if (rest == 0) { + cnt++; + mif_err("timeout!!! (count = %d)\n", cnt); + if (cnt >= 3) { + mif_err("ERR! no response from CP\n"); + break; + } + } + } while (rest == 0); + + switch (pm_data->last_msg) { + case INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK): + mif_info("recv IDPRAM_SUSPEND_ACK (0x%X)\n", pm_data->last_msg); + pm_data->pm_state = IDPRAM_PM_DPRAM_POWER_DOWN; + break; + + default: + mif_err("ERR! %s down or not ready!!! (intr 0x%04X)\n", + ld->name, dpld->recv_intr(dpld)); + timeout = msecs_to_jiffies(500); + wake_lock_timeout(&pm_data->hold_wlock, timeout); + s5p_idpram_set_pm_lock(dpld, 0); + break; + } + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_resume_init(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->pm_state = IDPRAM_PM_RESUME_START; + pm_data->last_msg = 0; + + dpld->reset_dpram_ipc(dpld); + + /* re-initialize internal dpram gpios */ + s3c_gpio_cfgpin(dpld->gpio_int2cp, GPIO_IDPRAM_SFN); + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_start_resume(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct modem_ctl *mc = dpld->ld.mc; + unsigned long delay; + mif_info("+++ (pm_state = %d)\n", pm_data->pm_state); + + switch (pm_data->pm_state) { + /* schedule_work */ + case IDPRAM_PM_DPRAM_POWER_DOWN: + gpio_set_value(mc->gpio_pda_active, 0); + msleep(50); + + s5p_idpram_resume_init(dpld); + msleep(50); + + gpio_set_value(mc->gpio_pda_active, 1); + msleep(20); + + pm_data->resume_try_cnt = 0; + wake_lock(&pm_data->hold_wlock); + + delay = msecs_to_jiffies(20); + schedule_delayed_work(&pm_data->resume_dwork, delay); + break; + + case IDPRAM_PM_RESUME_START: + case IDPRAM_PM_SUSPEND_PREPARE: + default: + break; + } + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_notify_pm_event(struct notifier_block *this, + unsigned long event, void *v) +{ + struct idpram_pm_data *pm_data; + struct dpram_link_device *dpld; + int err; + mif_info("+++ (event 0x%08X)\n", (int)event); + + pm_data = container_of(this, struct idpram_pm_data, pm_noti); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + + switch (event) { + case PM_SUSPEND_PREPARE: + err = s5p_idpram_prepare_suspend(dpld); + break; + + case PM_POST_SUSPEND: + err = s5p_idpram_start_resume(dpld); + break; + + default: + break; + } + + mif_info("---\n"); + return NOTIFY_DONE; +} + +static int s5p_idpram_pm_init(struct dpram_link_device *dpld, + struct modem_data *modem, void (*pm_tx_func)(struct work_struct *work)) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + int err; + unsigned gpio; + unsigned irq; + mif_info("+++\n"); + + atomic_set(&pm_data->pm_lock, 0); + + init_completion(&pm_data->down_cmpl); + + wake_lock_init(&pm_data->ap_wlock, WAKE_LOCK_SUSPEND, "ap_wakeup"); + wake_lock_init(&pm_data->hold_wlock, WAKE_LOCK_SUSPEND, "dpram_hold"); + + INIT_DELAYED_WORK(&pm_data->tx_dwork, pm_tx_func); + INIT_DELAYED_WORK(&pm_data->resume_dwork, s5p_idpram_try_resume); + + pm_data->resume_try_cnt = 0; + + /* register PM notifier */ + pm_data->pm_noti.notifier_call = s5p_idpram_notify_pm_event; + register_pm_notifier(&pm_data->pm_noti); + + /* + ** Register gpio_ap_wakeup interrupt handler + */ + gpio = modem->gpio_ap_wakeup; + irq = gpio_to_irq(gpio); + mif_info("gpio_ap_wakeup: GPIO# %d, IRQ# %d\n", gpio, irq); + + err = request_irq(irq, s5p_ap_wakeup_irq_handler, IRQF_TRIGGER_RISING, + "idpram_ap_wakeup", (void *)pm_data); + if (err) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); + goto exit; + } + + err = enable_irq_wake(irq); + if (err) { + mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); + free_irq(irq, (void *)pm_data); + goto exit; + } + + /* + ** Register gpio_cp_dump_int interrupt handler for LPA mode + */ + gpio = modem->gpio_cp_dump_int; + irq = gpio_to_irq(gpio); + mif_info("gpio_cp_dump_int: GPIO# %d, IRQ# %d\n", gpio, irq); + + err = request_irq(irq, s5p_cp_dump_irq_handler, IRQF_TRIGGER_RISING, + "idpram_cp_dump", (void *)pm_data); + if (err) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); + free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); + goto exit; + } + + err = enable_irq_wake(irq); + if (err) { + mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); + free_irq(gpio_to_irq(modem->gpio_cp_dump_int), (void *)pm_data); + free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); + goto exit; + } + +exit: + mif_err("---\n"); + return err; +} + +static bool s5p_idpram_int2cp_possible(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + int i; + int level; + + for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { + level = gpio_get_value(dpld->gpio_int2cp); + if (level) + break; + + /* CP has not yet received previous command. */ + mif_info("gpio_ipc_int2cp == 0 (count %d)\n", i); + + usleep_range(1000, 1100); + } + + for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { + level = gpio_get_value(mc->gpio_pda_active); + if (level) + break; + + /* AP is in transition to LPA mode. */ + mif_info("gpio_pda_active == 0 (count %d)\n", i); + + usleep_range(1000, 1100); + } + + return true; +} +#endif + +static struct idpram_pm_op idpram_pm_op_set[MAX_AP_TYPE] = { +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + [S5P] = { + .pm_init = s5p_idpram_pm_init, + .power_down = s5p_idpram_power_down, + .power_up = s5p_idpram_power_up, + .halt_suspend = s5p_idpram_halt_suspend, + .locked = s5p_idpram_is_pm_locked, + .int2cp_possible = s5p_idpram_int2cp_possible, + }, +#endif +}; + +struct idpram_pm_op *idpram_get_pm_op(enum ap_type ap) +{ + if (idpram_pm_op_set[ap].exist) + return &idpram_pm_op_set[ap]; + else + return NULL; +} + diff --git a/drivers/misc/modem_if/modem_link_device_hsic.c b/drivers/misc/modem_if/modem_link_device_hsic.c index eb5dfc6..e50e4a8 100644 --- a/drivers/misc/modem_if/modem_link_device_hsic.c +++ b/drivers/misc/modem_if/modem_link_device_hsic.c @@ -31,7 +31,7 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_hsic.h" #include "modem_utils.h" @@ -269,7 +269,7 @@ static void usb_rx_complete(struct urb *urb) switch (pipe_data->format) { case IF_USB_FMT_EP: if (usb_ld->if_usb_is_main) { -// pr_urb("IPC-RX", urb); + pr_urb("IPC-RX", urb); iod_format = IPC_FMT; } else { iod_format = IPC_BOOT; @@ -477,13 +477,13 @@ static int _usb_tx_work(struct sk_buff *skb) if (!pipe_data) return -ENOENT; -/* + if (iod->format == IPC_FMT && usb_ld->if_usb_is_main) pr_skb("IPC-TX", skb); if (iod->format == IPC_RAW) mif_debug("TX[RAW]\n"); -*/ + return usb_tx_urb_with_skb(usb_ld->usbdev, skb, pipe_data); } @@ -741,11 +741,11 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data) != HOSTWAKE_TRIGLEVEL) { if (gpio_get_value(pm_data->gpio_link_slavewake)) { gpio_set_value(pm_data->gpio_link_slavewake, 0); - mif_debug("gpio [SWK] set [0]\n"); + mif_info("gpio [SWK] set [0]\n"); mdelay(5); } gpio_set_value(pm_data->gpio_link_slavewake, 1); - mif_debug("gpio [SWK] set [1]\n"); + mif_info("gpio [SWK] set [1]\n"); mdelay(5); /* wait host wake signal*/ @@ -860,7 +860,7 @@ static irqreturn_t link_pm_irq_handler(int irq, void *data) runtime pm status changes to ACTIVE */ value = gpio_get_value(pm_data->gpio_link_hostwake); - mif_debug("gpio [HWK] get [%d]\n", value); + mif_info("gpio [HWK] get [%d]\n", value); /* * igonore host wakeup interrupt at suspending kernel @@ -975,9 +975,7 @@ static int link_pm_notifier_event(struct notifier_block *this, { struct link_pm_data *pm_data = container_of(this, struct link_pm_data, pm_notifier); -#ifdef CONFIG_UMTS_MODEM_XMM6262 struct modem_ctl *mc = if_usb_get_modemctl(pm_data); -#endif switch (event) { case PM_SUSPEND_PREPARE: @@ -986,13 +984,11 @@ static int link_pm_notifier_event(struct notifier_block *this, case PM_RESTORE_PREPARE: #endif pm_data->dpm_suspending = true; -#ifdef CONFIG_UMTS_MODEM_XMM6262 /* set PDA Active High if previous state was LPA */ if (!gpio_get_value(pm_data->gpio_link_active)) { mif_info("PDA active High to LPA suspend spot\n"); gpio_set_value(mc->gpio_pda_active, 1); } -#endif mif_debug("dpm suspending set to true\n"); return NOTIFY_OK; case PM_POST_SUSPEND: @@ -1006,15 +1002,14 @@ static int link_pm_notifier_event(struct notifier_block *this, queue_delayed_work(pm_data->wq, &pm_data->link_pm_work, 0); mif_info("post resume\n"); - } -#ifdef CONFIG_UMTS_MODEM_XMM6262 + } else { /* LPA to Kernel suspend and User Freezing task fail resume, restore to LPA GPIO states. */ - if (!gpio_get_value(pm_data->gpio_link_active)) { - mif_info("PDA active low to LPA GPIO state\n"); - gpio_set_value(mc->gpio_pda_active, 0); + if (!gpio_get_value(pm_data->gpio_link_active)) { + mif_info("PDA active low to LPA GPIO state\n"); + gpio_set_value(mc->gpio_pda_active, 0); + } } -#endif mif_debug("dpm suspending set to false\n"); return NOTIFY_OK; } @@ -1458,8 +1453,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) struct modem_data *pdata = (struct modem_data *)pdev->dev.platform_data; struct modemlink_pm_data *pm_pdata; - struct link_pm_data *pm_data = - kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); + struct link_pm_data *pm_data; if (!pdata || !pdata->link_pm_data) { mif_err("platform data is NULL\n"); @@ -1467,6 +1461,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) } pm_pdata = pdata->link_pm_data; + pm_data = kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); if (!pm_data) { mif_err("link_pm_data is NULL\n"); return -ENOMEM; diff --git a/drivers/misc/modem_if/modem_link_device_memory.c b/drivers/misc/modem_if/modem_link_device_memory.c new file mode 100644 index 0000000..9e49e50 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_memory.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2011 Samsung Electronics. + * + * 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 "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" +#include "modem_link_device_memory.h" +#ifdef CONFIG_LINK_DEVICE_DPRAM +#include "modem_link_device_dpram.h" +#endif + +/** + * msq_get_free_slot + * @trq : pointer to an instance of mem_status_queue structure + * + * Succeeds always by dropping the oldest slot if a "msq" is full. + */ +struct mem_status *msq_get_free_slot(struct mem_status_queue *msq) +{ + int qsize = MAX_MEM_LOG_CNT; + int in; + int out; + unsigned long flags; + struct mem_status *stat; + + spin_lock_irqsave(&msq->lock, flags); + + in = msq->in; + out = msq->out; + + if (circ_get_space(qsize, in, out) < 1) { + /* Make the oldest slot empty */ + out++; + msq->out = (out == qsize) ? 0 : out; + } + + /* Get a free slot */ + stat = &msq->stat[in]; + + /* Make it as "data" slot */ + in++; + msq->in = (in == qsize) ? 0 : in; + + spin_unlock_irqrestore(&msq->lock, flags); + + memset(stat, 0, sizeof(struct mem_status)); + + return stat; +} + +struct mem_status *msq_get_data_slot(struct mem_status_queue *msq) +{ + int qsize = MAX_MEM_LOG_CNT; + int in; + int out; + unsigned long flags; + struct mem_status *stat; + + spin_lock_irqsave(&msq->lock, flags); + + in = msq->in; + out = msq->out; + + if (in == out) { + stat = NULL; + goto exit; + } + + /* Get a data slot */ + stat = &msq->stat[out]; + + /* Make it "free" slot */ + out++; + msq->out = (out == qsize) ? 0 : out; + +exit: + spin_unlock_irqrestore(&msq->lock, flags); + return stat; +} + +/** + * memcpy16_from_io + * @to: pointer to "real" memory + * @from: pointer to IO memory + * @count: data length in bytes to be copied + * + * Copies data from IO memory space to "real" memory space. + */ +void memcpy16_from_io(const void *to, const void __iomem *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + u32 words = count >> 1; + while (words--) + *d++ = ioread16(s++); +} + +/** + * memcpy16_to_io + * @to: pointer to IO memory + * @from: pointer to "real" memory + * @count: data length in bytes to be copied + * + * Copies data from "real" memory space to IO memory space. + */ +void memcpy16_to_io(const void __iomem *to, const void *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + u32 words = count >> 1; + while (words--) + iowrite16(*s++, d++); +} + +/** + * memcmp16_to_io + * @to: pointer to IO memory + * @from: pointer to "real" memory + * @count: data length in bytes to be compared + * + * Compares data from "real" memory space to IO memory space. + */ +int memcmp16_to_io(const void __iomem *to, const void *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + int words = count >> 1; + int diff = 0; + int i; + u16 d1; + u16 s1; + + for (i = 0; i < words; i++) { + d1 = ioread16(d); + s1 = *s; + if (d1 != s1) { + diff++; + mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); + } + d++; + s++; + } + + return diff; +} + +/** + * circ_read16_from_io + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy16_from_io(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) data start (out) ~ buffer end */ + memcpy16_from_io(dst, (src + out), len1); + + /* 2) buffer start ~ data end (in - 1) */ + memcpy16_from_io((dst + len1), src, (len - len1)); + } +} + +/** + * circ_write16_to_io + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + memcpy16_to_io((dst + in), src, len); + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + memcpy16_to_io((dst + in), src, ((len > space) ? space : len)); + + /* 2) buffer start ~ data end */ + if (len > space) + memcpy16_to_io(dst, (src + space), (len - space)); + } +} + +/** + * copy_circ_to_user + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + if (copy_to_user(dst, (src + out), len)) { + mif_err("ERR! copy_to_user fail\n", + CALLER); + return -EFAULT; + } + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) data start (out) ~ buffer end */ + if (copy_to_user(dst, (src + out), len1)) { + mif_err("ERR! copy_to_user fail\n", + CALLER); + return -EFAULT; + } + + /* 2) buffer start ~ data end (in?) */ + if (copy_to_user((dst + len1), src, (len - len1))) { + mif_err("ERR! copy_to_user fail\n", + CALLER); + return -EFAULT; + } + } + + return 0; +} + +/** + * copy_user_to_circ + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + u32 len1; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + if (copy_from_user((dst + in), src, len)) { + mif_err("ERR! copy_from_user fail\n", + CALLER); + return -EFAULT; + } + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + len1 = (len > space) ? space : len; + if (copy_from_user((dst + in), src, len1)) { + mif_err("ERR! copy_from_user fail\n", + CALLER); + return -EFAULT; + } + + /* 2) buffer start ~ data end */ + if (len > len1) { + if (copy_from_user(dst, (src + space), (len - len1))) { + mif_err("ERR! copy_from_user " + "fail\n", CALLER); + return -EFAULT; + } + } + } + + return 0; +} + +/** + * print_mem_status + * @ld: pointer to an instance of link_device structure + * @mst: pointer to an instance of mem_status structure + * + * Prints a snapshot of the status of a SHM. + */ +void print_mem_status(struct link_device *ld, struct mem_status *mst) +{ + struct utc_time utc; + int us = ns2us(mst->ts.tv_nsec); + + ts2utc(&mst->ts, &utc); + pr_info("%s: %s: [%02d:%02d:%02d.%06d] " + "[%s] ACC{%X %d} " + "FMT{TI:%u TO:%u RI:%u RO:%u} " + "RAW{TI:%u TO:%u RI:%u RO:%u} " + "INTR{RX:0x%X TX:0x%X}\n", + MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, + get_dir_str(mst->dir), mst->magic, mst->access, + mst->head[IPC_FMT][TX], mst->tail[IPC_FMT][TX], + mst->head[IPC_FMT][RX], mst->tail[IPC_FMT][RX], + mst->head[IPC_RAW][TX], mst->tail[IPC_RAW][TX], + mst->head[IPC_RAW][RX], mst->tail[IPC_RAW][RX], + mst->int2ap, mst->int2cp); +} + +/** + * print_circ_status + * @ld: pointer to an instance of link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Prints a snapshot of the status of a memory + */ +void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst) +{ + struct utc_time utc; + int us = ns2us(mst->ts.tv_nsec); + + if (dev > IPC_RAW) + return; + + ts2utc(&mst->ts, &utc); + pr_info("%s: %s: [%02d:%02d:%02d.%06d] " + "[%s] %s | TXQ{in:%u out:%u} RXQ{in:%u out:%u}\n", + MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, + get_dir_str(mst->dir), get_dev_name(dev), + mst->head[dev][TX], mst->tail[dev][TX], + mst->head[dev][RX], mst->tail[dev][RX]); +} + +/** + * print_ipc_trace + * @ld: pointer to an instance of link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @stat: pointer to an instance of circ_status structure + * @ts: pointer to an instance of timespec structure + * @buff: start address of a buffer into which RX IPC messages were copied + * @rcvd: size of data in the buffer + * + * Prints IPC messages in a local memory buffer to a kernel log. + */ +void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, + struct timespec *ts, u8 *buff, u32 rcvd) +{ + struct utc_time utc; + + ts2utc(ts, &utc); + + pr_info("%s: [%d-%02d-%02d %02d:%02d:%02d.%03d] " + "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", + MIF_TAG, utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec, + utc.msec, ld->name, get_dev_name(dev), stat->in, stat->out, + stat->size); + + mif_print_dump(buff, rcvd, 4); +} + +/** + * capture_mem_dump + * @ld: pointer to an instance of link_device structure + * @base: base virtual address to a memory interface medium + * @size: size of the memory interface medium + * + * Captures a dump for a memory interface medium. + * + * Returns the pointer to a memory dump buffer. + */ +u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size) +{ + u8 *buff = kzalloc(size, GFP_ATOMIC); + if (!buff) { + mif_err("%s: ERR! kzalloc(%d) fail\n", ld->name, size); + return NULL; + } else { + memcpy16_from_io(buff, base, size); + return buff; + } +} + +/** + * trq_get_free_slot + * @trq : pointer to an instance of trace_data_queue structure + * + * Succeeds always by dropping the oldest slot if a "trq" is full. + */ +struct trace_data *trq_get_free_slot(struct trace_data_queue *trq) +{ + int qsize = MAX_TRACE_SIZE; + int in; + int out; + unsigned long flags; + struct trace_data *trd; + + spin_lock_irqsave(&trq->lock, flags); + + in = trq->in; + out = trq->out; + + /* The oldest slot can be dropped. */ + if (circ_get_space(qsize, in, out) < 1) { + /* Free the data buffer in the oldest slot */ + trd = &trq->trd[out]; + kfree(trd->data); + + /* Make the oldest slot empty */ + out++; + trq->out = (out == qsize) ? 0 : out; + } + + /* Get a free slot and make it occupied */ + trd = &trq->trd[in++]; + trq->in = (in == qsize) ? 0 : in; + + spin_unlock_irqrestore(&trq->lock, flags); + + memset(trd, 0, sizeof(struct trace_data)); + + return trd; +} + +struct trace_data *trq_get_data_slot(struct trace_data_queue *trq) +{ + int qsize = MAX_TRACE_SIZE; + int in; + int out; + unsigned long flags; + struct trace_data *trd; + + spin_lock_irqsave(&trq->lock, flags); + + in = trq->in; + out = trq->out; + + if (circ_get_usage(qsize, in, out) < 1) { + spin_unlock_irqrestore(&trq->lock, flags); + return NULL; + } + + /* Get a data slot and make it empty */ + trd = &trq->trd[out++]; + trq->out = (out == qsize) ? 0 : out; + + spin_unlock_irqrestore(&trq->lock, flags); + + return trd; +} + diff --git a/drivers/misc/modem_if/modem_link_device_memory.h b/drivers/misc/modem_if/modem_link_device_memory.h new file mode 100644 index 0000000..29a84ac --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_memory.h @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 __MODEM_LINK_DEVICE_MEMORY_H__ +#define __MODEM_LINK_DEVICE_MEMORY_H__ + +#include +#include +#include +#include +#include +#if defined(CONFIG_HAS_EARLYSUSPEND) +#include +#elif defined(CONFIG_FB) +#include +#endif + +#include "modem.h" +#include "modem_prj.h" + +#define DPRAM_MAGIC_CODE 0xAA + +/* interrupt masks.*/ +#define INT_MASK_VALID 0x0080 +#define INT_MASK_CMD 0x0040 +#define INT_VALID(x) ((x) & INT_MASK_VALID) +#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) +#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) +#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) + +#define EXT_UDL_MASK 0xF000 +#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) +#define EXT_INT_VALID_MASK 0x8000 +#define EXT_CMD_VALID_MASK 0x4000 +#define UDL_CMD_VALID_MASK 0x2000 +#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) +#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) +#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) +#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) + +#define EXT_CMD_MASK(x) ((x) & 0x0FFF) +#define EXT_CMD_SET_SPEED_LOW 0x0011 +#define EXT_CMD_SET_SPEED_MID 0x0012 +#define EXT_CMD_SET_SPEED_HIGH 0x0013 + +#define UDL_RESULT_SUCCESS 0x1 +#define UDL_RESULT_FAIL 0x2 + +#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) +#define UDL_CMD_RECV_READY 0x1 +#define UDL_CMD_DL_START_REQ 0x2 +#define UDL_CMD_DL_START_RESP 0x3 +#define UDL_CMD_IMAGE_SEND_REQ 0x4 +#define UDL_CMD_SEND_DONE_RESP 0x5 +#define UDL_CMD_SEND_DONE_REQ 0x6 +#define UDL_CMD_UPDATE_DONE 0x7 +#define UDL_CMD_STATUS_UPDATE 0x8 +#define UDL_CMD_IMAGE_SEND_RESP 0x9 +#define UDL_CMD_EFS_CLEAR_RESP 0xB +#define UDL_CMD_ALARM_BOOT_OK 0xC +#define UDL_CMD_ALARM_BOOT_FAIL 0xD + +#define CMD_DL_READY 0xA100 +#define CMD_DL_START_REQ 0x9200 +#define CMD_DL_START_RESP 0xA301 +#define CMD_DL_SEND_REQ 0x9400 +#define CMD_DL_SEND_RESP 0xA501 +#define CMD_DL_DONE_REQ 0x9600 +#define CMD_DL_DONE_RESP 0xA701 + +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_REQ 0xA700 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#define INT_MASK_REQ_ACK_F 0x0020 +#define INT_MASK_REQ_ACK_R 0x0010 +#define INT_MASK_RES_ACK_F 0x0008 +#define INT_MASK_RES_ACK_R 0x0004 +#define INT_MASK_SEND_F 0x0002 +#define INT_MASK_SEND_R 0x0001 + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_REQ_ACK_SET \ + (INT_MASK_REQ_ACK_F | INT_MASK_REQ_ACK_R | INT_MASK_REQ_ACK_RFS) + +#define INT_MASK_RES_ACK_SET \ + (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) + +#define INT_CMD_MASK(x) ((x) & 0xF) +#define INT_CMD_INIT_START 0x1 +#define INT_CMD_INIT_END 0x2 +#define INT_CMD_REQ_ACTIVE 0x3 +#define INT_CMD_RES_ACTIVE 0x4 +#define INT_CMD_REQ_TIME_SYNC 0x5 +#define INT_CMD_CRASH_RESET 0x7 +#define INT_CMD_PHONE_START 0x8 +#define INT_CMD_ERR_DISPLAY 0x9 +#define INT_CMD_CRASH_EXIT 0x9 +#define INT_CMD_CP_DEEP_SLEEP 0xA +#define INT_CMD_NV_REBUILDING 0xB +#define INT_CMD_EMER_DOWN 0xC +#define INT_CMD_PIF_INIT_DONE 0xD +#define INT_CMD_SILENT_NV_REBUILDING 0xE +#define INT_CMD_NORMAL_POWER_OFF 0xF + +/* AP_IDPRAM PM control command with QSC6085 */ +#define INT_CMD_IDPRAM_SUSPEND_REQ 0xD +#define INT_CMD_IDPRAM_SUSPEND_ACK 0xB +#define INT_CMD_IDPRAM_WAKEUP_START 0xE +#define INT_CMD_IDPRAM_RESUME_REQ 0xC + +#define START_FLAG 0x7F +#define END_FLAG 0x7E + +#define DP_MAGIC_DMDL 0x4445444C +#define DP_MAGIC_UMDL 0x4445444D +#define DP_DPRAM_SIZE 0x4000 +#define DP_DEFAULT_WRITE_LEN 8168 +#define DP_DEFAULT_DUMP_LEN 16128 +#define DP_DUMP_HEADER_SIZE 7 + +#define UDL_TIMEOUT (50 * HZ) +#define UDL_SEND_TIMEOUT (200 * HZ) +#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) +#define DUMP_TIMEOUT (30 * HZ) +#define DUMP_START_TIMEOUT (100 * HZ) +#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ + +#define IDPRAM_SUSPEND_REQ_TIMEOUT (50 * HZ) + +#define RES_ACK_WAIT_TIMEOUT 10 /* 10 ms */ +#define REQ_ACK_DELAY 10 /* 10 ms */ + +#ifdef DEBUG_MODEM_IF +#define MAX_RETRY_CNT 1 +#else +#define MAX_RETRY_CNT 3 +#endif + +#define MAX_SKB_TXQ_DEPTH 1024 + +struct memif_boot_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 __iomem *req; + u32 __iomem *resp; + u32 space; +}; + +struct memif_dload_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 space; +}; + +struct memif_uload_map { + u32 __iomem *magic; + u8 __iomem *cmd; + u32 cmd_size; + u8 __iomem *buff; + u32 space; +}; + +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_MAGIC_SIZE 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_MAGIC_SIZE 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 +#define DP_MBX_SET_SIZE 4 +#define DP_MAX_PAYLOAD_SIZE 0x2000 + +enum circ_dir_type { + TX, + RX, + MAX_DIR, +}; + +enum circ_ptr_type { + HEAD, + TAIL, +}; + +static inline bool circ_valid(u32 qsize, u32 in, u32 out) +{ + if (in >= qsize) + return false; + + if (out >= qsize) + return false; + + return true; +} + +static inline u32 circ_get_space(u32 qsize, u32 in, u32 out) +{ + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline u32 circ_get_usage(u32 qsize, u32 in, u32 out) +{ + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline u32 circ_new_pointer(u32 qsize, u32 p, u32 len) +{ + p += len; + return (p < qsize) ? p : (p - qsize); +} + +/** + * circ_read + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +static inline void circ_read(void *dst, void *src, u32 qsize, u32 out, u32 len) +{ + unsigned len1; + + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + + /* 1) data start (out) ~ buffer end */ + len1 = qsize - out; + memcpy(dst, (src + out), len1); + + /* 2) buffer start ~ data end (in?) */ + memcpy((dst + len1), src, (len - len1)); + } +} + +/** + * circ_write + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +static inline void circ_write(void *dst, void *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + memcpy((dst + in), src, len); + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + memcpy((dst + in), src, ((len > space) ? space : len)); + + /* 2) buffer start ~ data end */ + if (len > space) + memcpy(dst, (src + space), (len - space)); + } +} + +/** + * circ_dir + * @dir: communication direction (enum circ_dir_type) + * + * Returns the direction of a circular queue + * + */ +static const inline char *circ_dir(enum circ_dir_type dir) +{ + if (dir == TX) + return "TXQ"; + else + return "RXQ"; +} + +/** + * circ_ptr + * @ptr: circular queue pointer (enum circ_ptr_type) + * + * Returns the name of a circular queue pointer + * + */ +static const inline char *circ_ptr(enum circ_ptr_type ptr) +{ + if (ptr == HEAD) + return "head"; + else + return "tail"; +} + +/** + * get_dir_str + * @dir: communication direction (enum circ_dir_type) + * + * Returns the direction of a circular queue + * + */ +static const inline char *get_dir_str(enum circ_dir_type dir) +{ + if (dir == TX) + return "AP->CP"; + else + return "CP->AP"; +} + +void memcpy16_from_io(const void *to, const void __iomem *from, u32 count); +void memcpy16_to_io(const void __iomem *to, const void *from, u32 count); +int memcmp16_to_io(const void __iomem *to, const void *from, u32 count); +void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len); +void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len); +int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len); +int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len); + +#define MAX_MEM_LOG_CNT 8192 +#define MAX_TRACE_SIZE 1024 + +struct mem_status { + /* Timestamp */ + struct timespec ts; + + /* Direction (TX or RX) */ + enum circ_dir_type dir; + + /* The status of memory interface at the time */ + u32 magic; + u32 access; + + u32 head[MAX_IPC_DEV][MAX_DIR]; + u32 tail[MAX_IPC_DEV][MAX_DIR]; + + u16 int2ap; + u16 int2cp; +}; + +struct mem_status_queue { + spinlock_t lock; + u32 in; + u32 out; + struct mem_status stat[MAX_MEM_LOG_CNT]; +}; + +struct circ_status { + u8 *buff; + u32 qsize; /* the size of a circular buffer */ + u32 in; + u32 out; + u32 size; /* the size of free space or received data */ +}; + +struct trace_data { + struct timespec ts; + enum dev_format dev; + struct circ_status circ_stat; + u8 *data; + u32 size; +}; + +struct trace_data_queue { + spinlock_t lock; + u32 in; + u32 out; + struct trace_data trd[MAX_TRACE_SIZE]; +}; + +struct mem_status *msq_get_free_slot(struct mem_status_queue *msq); +struct mem_status *msq_get_data_slot(struct mem_status_queue *msq); + +void print_mem_status(struct link_device *ld, struct mem_status *mst); +void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst); +void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, + struct timespec *ts, u8 *buff, u32 rcvd); + +u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size); +struct trace_data *trq_get_free_slot(struct trace_data_queue *trq); +struct trace_data *trq_get_data_slot(struct trace_data_queue *trq); + +#endif + diff --git a/drivers/misc/modem_if/modem_link_device_mipi.c b/drivers/misc/modem_if/modem_link_device_mipi.c index f2804e9..948a61c 100644 --- a/drivers/misc/modem_if/modem_link_device_mipi.c +++ b/drivers/misc/modem_if/modem_link_device_mipi.c @@ -26,7 +26,7 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_mipi.h" #include "modem_utils.h" diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c index b68040e..c0769ff 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.c +++ b/drivers/misc/modem_if/modem_link_device_pld.c @@ -25,190 +25,76 @@ #include #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" - /* ** Function prototypes for basic DPRAM operations */ -static inline void clear_intr(struct dpram_link_device *dpld); -static inline u16 recv_intr(struct dpram_link_device *dpld); -static inline void send_intr(struct dpram_link_device *dpld, u16 mask); - -static inline u16 get_magic(struct dpram_link_device *dpld); -static inline void set_magic(struct dpram_link_device *dpld, u16 val); -static inline u16 get_access(struct dpram_link_device *dpld); -static inline void set_access(struct dpram_link_device *dpld, u16 val); - -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); - -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); - -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); - -static void handle_cp_crash(struct dpram_link_device *dpld); -static int trigger_force_cp_crash(struct dpram_link_device *dpld); +static inline void clear_intr(struct pld_link_device *pld); +static inline u16 recv_intr(struct pld_link_device *pld); +static inline void send_intr(struct pld_link_device *pld, u16 mask); + +static inline u16 get_magic(struct pld_link_device *pld); +static inline void set_magic(struct pld_link_device *pld, u16 val); +static inline u16 get_access(struct pld_link_device *pld); +static inline void set_access(struct pld_link_device *pld, u16 val); + +static inline u32 get_tx_head(struct pld_link_device *pld, int id); +static inline u32 get_tx_tail(struct pld_link_device *pld, int id); +static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in); +static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out); +static inline u8 *get_tx_buff(struct pld_link_device *pld, int id); +static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id); + +static inline u32 get_rx_head(struct pld_link_device *pld, int id); +static inline u32 get_rx_tail(struct pld_link_device *pld, int id); +static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in); +static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out); +static inline u8 *get_rx_buff(struct pld_link_device *pld, int id); +static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id); + +static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id); +static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id); +static inline u16 get_mask_send(struct pld_link_device *pld, int id); + +static void handle_cp_crash(struct pld_link_device *pld); +static int trigger_force_cp_crash(struct pld_link_device *pld); /* ** Functions for debugging */ -static inline void log_dpram_status(struct dpram_link_device *dpld) -{ - pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " - "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", - dpld->ld.mc->name, - get_magic(dpld), get_access(dpld), - get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), - get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), - get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), - get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), - recv_intr(dpld)); -} - -static void set_dpram_map(struct dpram_link_device *dpld, +static void set_dpram_map(struct pld_link_device *pld, struct mif_irq_map *map) { - map->magic = get_magic(dpld); - map->access = get_access(dpld); - - map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); - map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); - map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); - map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); - map->raw_tx_in = get_tx_head(dpld, IPC_RAW); - map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); - map->raw_rx_in = get_rx_head(dpld, IPC_RAW); - map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); - - map->cp2ap = recv_intr(dpld); -} - -/* -** RXB (DPRAM RX buffer) functions -*/ -static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) -{ - struct dpram_rxb *rxb; - u8 *buff; - int i; - - rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); - if (!rxb) { - mif_info("ERR! kzalloc rxb fail\n"); - return NULL; - } - - buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); - if (!buff) { - mif_info("ERR! kzalloc buff fail\n"); - kfree(rxb); - return NULL; - } - - for (i = 0; i < count; i++) { - rxb[i].buff = buff; - rxb[i].size = size; - buff += size; - } - - return rxb; -} - -static inline unsigned rxbq_get_page_size(unsigned len) -{ - return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; -} + map->magic = get_magic(pld); + map->access = get_access(pld); -static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) -{ - return (rxbq->in == rxbq->out) ? true : false; -} - -static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in < out) ? (out - in - 1) : (qsize + out - in - 1); -} - -static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; - - if (likely(rxbq_free_size(rxbq) > 0)) { - rxb = &rxbq->rxb[rxbq->in]; - rxbq->in++; - if (rxbq->in >= rxbq->size) - rxbq->in -= rxbq->size; - rxb->data = rxb->buff; - } - - return rxb; -} - -static inline int rxbq_size(struct dpram_rxb_queue *rxbq) -{ - int in = rxbq->in; - int out = rxbq->out; - int qsize = rxbq->size; - return (in >= out) ? (in - out) : (qsize - out + in); -} - -static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; - - if (likely(!rxbq_empty(rxbq))) { - rxb = &rxbq->rxb[rxbq->out]; - rxbq->out++; - if (rxbq->out >= rxbq->size) - rxbq->out -= rxbq->size; - } - - return rxb; -} - -static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) -{ - rxb->len = len; - return rxb->data; -} + map->fmt_tx_in = get_tx_head(pld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(pld, IPC_FMT); + map->fmt_rx_in = get_rx_head(pld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(pld, IPC_FMT); + map->raw_tx_in = get_tx_head(pld, IPC_RAW); + map->raw_tx_out = get_tx_tail(pld, IPC_RAW); + map->raw_rx_in = get_rx_head(pld, IPC_RAW); + map->raw_rx_out = get_rx_tail(pld, IPC_RAW); -static inline void rxb_clear(struct dpram_rxb *rxb) -{ - rxb->data = NULL; - rxb->len = 0; + map->cp2ap = recv_intr(pld); } /* ** DPRAM operations */ -static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), +static int pld_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), unsigned long flag, const char *name, - struct dpram_link_device *dpld) + struct pld_link_device *pld) { int ret = 0; - ret = request_irq(irq, isr, flag, name, dpld); + ret = request_irq(irq, isr, flag, name, pld); if (ret) { mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); return ret; @@ -223,31 +109,31 @@ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), return 0; } -static inline void clear_intr(struct dpram_link_device *dpld) +static inline void clear_intr(struct pld_link_device *pld) { - if (dpld->dpctl->clear_intr) - dpld->dpctl->clear_intr(); + if (pld->ext_op && pld->ext_op->clear_intr) + pld->ext_op->clear_intr(pld); } -static inline u16 recv_intr(struct dpram_link_device *dpld) +static inline u16 recv_intr(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -255,31 +141,31 @@ static inline u16 recv_intr(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +static inline void send_intr(struct pld_link_device *pld, u16 mask) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - iowrite16((u16)mask, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + iowrite16((u16)mask, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == mask)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -287,35 +173,35 @@ static inline void send_intr(struct dpram_link_device *dpld, u16 mask) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - iowrite16((u16)mask, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + iowrite16((u16)mask, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u16 get_magic(struct dpram_link_device *dpld) +static inline u16 get_magic(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -324,31 +210,31 @@ static inline u16 get_magic(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_magic(struct dpram_link_device *dpld, u16 in) +static inline void set_magic(struct pld_link_device *pld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -356,34 +242,34 @@ static inline void set_magic(struct dpram_link_device *dpld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u16 get_access(struct dpram_link_device *dpld) +static inline u16 get_access(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -392,31 +278,31 @@ static inline u16 get_access(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_access(struct dpram_link_device *dpld, u16 in) +static inline void set_access(struct pld_link_device *pld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -424,34 +310,34 @@ static inline void set_access(struct dpram_link_device *dpld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_head(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -461,29 +347,29 @@ static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_tail(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -493,30 +379,30 @@ static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -525,49 +411,49 @@ static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out) { return; } -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +static inline u8 *get_tx_buff(struct pld_link_device *pld, int id) { - return dpld->dev[id]->txq.buff; + return pld->dev[id]->txq.buff; } -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id) { - return dpld->dev[id]->txq.size; + return pld->dev[id]->txq.size; } -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_head(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -577,29 +463,29 @@ static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_tail(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val1 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -609,35 +495,35 @@ static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in) { return; } -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - iowrite16((u16)out, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + iowrite16((u16)out, pld->base); do { /* Check tail value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val = ioread16(pld->base); if (val == out) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -646,71 +532,60 @@ static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) udelay(100); /* Write tail value again */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - iowrite16((u16)out, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + iowrite16((u16)out, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +static inline u8 *get_rx_buff(struct pld_link_device *pld, int id) { - return dpld->dev[id]->rxq.buff; + return pld->dev[id]->rxq.buff; } -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id) { - return dpld->dev[id]->rxq.size; + return pld->dev[id]->rxq.size; } -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_req_ack; + return pld->dev[id]->mask_req_ack; } -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_res_ack; + return pld->dev[id]->mask_res_ack; } -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_send(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_send; -} - -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) -{ - if (in >= size) - return false; - - if (out >= size) - return false; - - return true; + return pld->dev[id]->mask_send; } /* Get free space in the TXQ as well as in & out pointers */ -static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +static inline int get_txq_space(struct pld_link_device *pld, int dev, u32 qsize, + u32 *in, u32 *out) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int cnt = 3; u32 head; u32 tail; int space; do { - head = get_tx_head(dpld, dev); - tail = get_tx_tail(dpld, dev); + head = get_tx_head(pld, dev); + tail = get_tx_tail(pld, dev); space = (head < tail) ? (tail - head - 1) : (qsize + tail - head - 1); mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", ld->name, get_dev_name(dev), qsize, head, tail, space); - if (dpram_circ_valid(qsize, head, tail)) { + if (circ_valid(qsize, head, tail)) { *in = head; *out = tail; return space; @@ -729,27 +604,27 @@ static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, return -EINVAL; } -static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +static void reset_tx_circ(struct pld_link_device *pld, int dev) { - set_tx_head(dpld, dev, 0); - set_tx_tail(dpld, dev, 0); + set_tx_head(pld, dev, 0); + set_tx_tail(pld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + trigger_force_cp_crash(pld); } /* Get data size in the RXQ as well as in & out pointers */ -static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) +static inline int get_rxq_rcvd(struct pld_link_device *pld, int dev, u32 qsize, + u32 *in, u32 *out) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int cnt = 3; u32 head; u32 tail; u32 rcvd; do { - head = get_rx_head(dpld, dev); - tail = get_rx_tail(dpld, dev); + head = get_rx_head(pld, dev); + tail = get_rx_tail(pld, dev); if (head == tail) { *in = head; *out = tail; @@ -760,7 +635,7 @@ static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, mif_info("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", ld->name, get_dev_name(dev), qsize, head, tail, rcvd); - if (dpram_circ_valid(qsize, head, tail)) { + if (circ_valid(qsize, head, tail)) { *in = head; *out = tail; return rcvd; @@ -779,54 +654,20 @@ static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, return -EINVAL; } -static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +static void reset_rx_circ(struct pld_link_device *pld, int dev) { - set_rx_head(dpld, dev, 0); - set_rx_tail(dpld, dev, 0); + set_rx_head(pld, dev, 0); + set_rx_tail(pld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + trigger_force_cp_crash(pld); } -/* -** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success -*/ -static int dpram_wake_up(struct dpram_link_device *dpld) +static int check_access(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; - - if (!dpld->dpctl->wakeup) - return 0; - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail\n", - ld->name, __builtin_return_address(0)); - return -EACCES; - } - - atomic_inc(&dpld->accessing); - return 0; -} - -static void dpram_allow_sleep(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - - if (!dpld->dpctl->sleep) - return; - - if (atomic_dec_return(&dpld->accessing) <= 0) { - dpld->dpctl->sleep(); - atomic_set(&dpld->accessing, 0); - mif_debug("%s: DPRAM sleep possible\n", ld->name); - } -} - -static int dpram_check_access(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i; - u16 magic = get_magic(dpld); - u16 access = get_access(dpld); + u16 magic = get_magic(pld); + u16 access = get_access(pld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; @@ -836,8 +677,8 @@ static int dpram_check_access(struct dpram_link_device *dpld) ld->name, magic, access, i); udelay(100); - magic = get_magic(dpld); - access = get_access(dpld); + magic = get_magic(pld); + access = get_access(pld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; } @@ -846,9 +687,9 @@ static int dpram_check_access(struct dpram_link_device *dpld) return -EACCES; } -static bool dpram_ipc_active(struct dpram_link_device *dpld) +static bool ipc_active(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { @@ -857,8 +698,8 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return false; } - if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pf> dpram_check_access fail\n", + if (check_access(pld) < 0) { + mif_info("%s: ERR! <%pf> check_access fail\n", ld->name, __builtin_return_address(0)); return false; } @@ -866,67 +707,72 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return true; } -static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, +static void pld_ipc_write(struct pld_link_device *pld, int dev, u32 qsize, u32 in, u32 out, struct sk_buff *skb) { - struct link_device *ld = &dpld->ld; - u8 __iomem *buff = get_tx_buff(dpld, dev); + struct link_device *ld = &pld->ld; + u8 __iomem *buff = get_tx_buff(pld, dev); u8 *src = skb->data; u32 len = skb->len; u32 inp; struct mif_irq_map map; + unsigned long int flags; + + spin_lock_irqsave(&pld->pld_lock, flags); if (in < out) { /* +++++++++ in ---------- out ++++++++++ */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); - memcpy(dpld->dp_base, src, len); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); + memcpy(pld->base, src, len); } else { /* ------ out +++++++++++ in ------------ */ u32 space = qsize - in; /* 1) in -> buffer end */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); - memcpy(dpld->dp_base, src, ((len > space) ? space : len)); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); + memcpy(pld->base, src, ((len > space) ? space : len)); if (len > space) { iowrite16(PLD_ADDR_MASK(&buff[0]), - dpld->address_buffer); - memcpy(dpld->dp_base, (src+space), (len-space)); + pld->address_buffer); + memcpy(pld->base, (src+space), (len-space)); } } + spin_unlock_irqrestore(&pld->pld_lock, flags); + /* update new in pointer */ inp = in + len; if (inp >= qsize) inp -= qsize; - set_tx_head(dpld, dev, inp); + set_tx_head(pld, dev, inp); if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); + set_dpram_map(pld, &map); mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); } } -static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) +static int pld_try_ipc_tx(struct pld_link_device *pld, int dev) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct sk_buff_head *txq = ld->skb_txq[dev]; struct sk_buff *skb; unsigned long int flags; - u32 qsize = get_tx_buff_size(dpld, dev); + u32 qsize = get_tx_buff_size(pld, dev); u32 in; u32 out; int space; int copied = 0; - spin_lock_irqsave(&dpld->tx_rx_lock, flags); + spin_lock_irqsave(&pld->tx_rx_lock, flags); while (1) { - space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); + space = get_txq_space(pld, dev, qsize, &in, &out); if (unlikely(space < 0)) { - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); - dpram_reset_tx_circ(dpld, dev); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + reset_tx_circ(pld, dev); return space; } @@ -935,9 +781,9 @@ static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) break; if (unlikely(space < skb->len)) { - atomic_set(&dpld->res_required[dev], 1); + atomic_set(&pld->res_required[dev], 1); skb_queue_head(txq, skb); - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); mif_info("%s: %s " "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", ld->name, get_dev_name(dev), @@ -946,30 +792,30 @@ static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) } /* TX if there is enough room in the queue */ - dpram_ipc_write(dpld, dev, qsize, in, out, skb); + pld_ipc_write(pld, dev, qsize, in, out, skb); copied += skb->len; dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return copied; } -static void dpram_ipc_rx_task(unsigned long data) +static void pld_ipc_rx_task(unsigned long data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = &dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)data; + struct link_device *ld = &pld->ld; struct io_device *iod; - struct dpram_rxb *rxb; + struct mif_rxb *rxb; unsigned qlen; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { - iod = dpld->iod[i]; - qlen = rxbq_size(&dpld->rxbq[i]); + for (i = 0; i < ld->max_ipc_dev; i++) { + iod = pld->iod[i]; + qlen = rxbq_size(&pld->rxbq[i]); while (qlen > 0) { - rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); + rxb = rxbq_get_data_rxb(&pld->rxbq[i]); iod->recv(iod, ld, rxb->data, rxb->len); rxb_clear(rxb); qlen--; @@ -977,36 +823,39 @@ static void dpram_ipc_rx_task(unsigned long data) } } -static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, +static void pld_ipc_read(struct pld_link_device *pld, int dev, u8 *dst, u8 __iomem *src, u32 out, u32 len, u32 qsize) { u8 *ori_det = dst; unsigned long flags; + spin_lock_irqsave(&pld->pld_lock, flags); + if ((out + len) <= qsize) { /* ----- (out) (in) ----- */ /* ----- 7f 00 00 7e ----- */ - iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, len); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); + memcpy(dst, pld->base, len); } else { /* (in) ----------- (out) */ /* 00 7e ----------- 7f 00 */ unsigned len1 = qsize - out; /* 1) out -> buffer end */ - iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, len1); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); + memcpy(dst, pld->base, len1); /* 2) buffer start -> in */ dst += len1; - iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, (len - len1)); + iowrite16(PLD_ADDR_MASK(&src[0]), pld->address_buffer); + memcpy(dst, pld->base, (len - len1)); } - if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { + spin_unlock_irqrestore(&pld->pld_lock, flags); + if (pld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { mif_info("ipc read error!! in[%d], out[%d]\n", - get_rx_head(dpld, dev), - get_rx_tail(dpld, dev)); + get_rx_head(pld, dev), + get_rx_tail(pld, dev)); } } @@ -1016,190 +865,106 @@ static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, ret == 0 : no data ret > 0 : valid data */ -static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) +static int pld_ipc_recv_data_with_rxb(struct pld_link_device *pld, int dev) { - struct link_device *ld = &dpld->ld; - struct dpram_rxb *rxb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); + struct link_device *ld = &pld->ld; + struct mif_rxb *rxb; + u8 __iomem *src = get_rx_buff(pld, dev); + u32 qsize = get_rx_buff_size(pld, dev); u32 in; u32 out; u32 rcvd; struct mif_irq_map map; unsigned long int flags; - spin_lock_irqsave(&dpld->tx_rx_lock, flags); + spin_lock_irqsave(&pld->tx_rx_lock, flags); - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + rcvd = get_rxq_rcvd(pld, dev, qsize, &in, &out); if (rcvd <= 0) { - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); + set_dpram_map(pld, &map); mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } /* Allocate an rxb */ - rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); + rxb = rxbq_get_free_rxb(&pld->rxbq[dev]); if (!rxb) { mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", ld->name, get_dev_name(dev)); - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return -ENOMEM; } /* Read data from each DPRAM buffer */ - dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); + pld_ipc_read(pld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); /* Calculate and set new out */ out += rcvd; if (out >= qsize) out -= qsize; - set_rx_tail(dpld, dev, out); - - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); - return rcvd; -} - -/* - ret < 0 : error - ret == 0 : no data - ret > 0 : valid data -*/ -static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) -{ - struct link_device *ld = &dpld->ld; - struct io_device *iod = dpld->iod[dev]; - struct sk_buff *skb; - u8 __iomem *src = get_rx_buff(dpld, dev); - u32 qsize = get_rx_buff_size(dpld, dev); - u32 in; - u32 out; - u32 rcvd; - int rest; - u8 *frm; - u8 *dst; - unsigned int len; - unsigned int pad; - unsigned int tot; - struct mif_irq_map map; - - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); - if (rcvd <= 0) - return rcvd; - - if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); - mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); - } - - rest = rcvd; - while (rest > 0) { - frm = src + out; - if (unlikely(!sipc5_start_valid(frm[0]))) { - mif_err("%s: ERR! %s invalid start 0x%02X\n", - ld->name, get_dev_name(dev), frm[0]); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } - - len = sipc5_get_frame_sz16(frm); - if (unlikely(len > rest)) { - mif_err("%s: ERR! %s len %d > rest %d\n", - ld->name, get_dev_name(dev), len, rest); - skb_queue_purge(&dpld->skb_rxq[dev]); - return -EBADMSG; - } - - pad = sipc5_calc_padding_size(len); - tot = len + pad; - - /* Allocate an skb */ - skb = dev_alloc_skb(tot); - if (!skb) { - mif_err("%s: ERR! %s dev_alloc_skb fail\n", - ld->name, get_dev_name(dev)); - return -ENOMEM; - } - - /* Read data from each DPRAM buffer */ - dst = skb_put(skb, tot); - dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); - skb_trim(skb, len); - iod->recv_skb(iod, ld, skb); - - /* Calculate and set new out */ - rest -= tot; - out += tot; - if (out >= qsize) - out -= qsize; - } - - set_rx_tail(dpld, dev, out); + set_rx_tail(pld, dev, out); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } -static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) +static void non_command_handler(struct pld_link_device *pld, u16 non_cmd) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i = 0; int ret = 0; - u16 tx_mask = 0; + u16 mask = 0; - if (!dpram_ipc_active(dpld)) + if (!ipc_active(pld)) return; /* Read data from DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (dpld->use_skb) - ret = dpram_ipc_recv_data_with_skb(dpld, i); - else - ret = dpram_ipc_recv_data_with_rxb(dpld, i); + for (i = 0; i < ld->max_ipc_dev; i++) { + ret = pld_ipc_recv_data_with_rxb(pld, i); if (ret < 0) - dpram_reset_rx_circ(dpld, i); + reset_rx_circ(pld, i); /* Check and process REQ_ACK (at this time, in == out) */ - if (non_cmd & get_mask_req_ack(dpld, i)) { + if (non_cmd & get_mask_req_ack(pld, i)) { mif_debug("%s: send %s_RES_ACK\n", ld->name, get_dev_name(i)); - tx_mask |= get_mask_res_ack(dpld, i); + mask |= get_mask_res_ack(pld, i); } } - if (!dpld->use_skb) { - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); - } + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&pld->rx_tsk); /* Try TX via DPRAM */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (atomic_read(&dpld->res_required[i]) > 0) { - ret = dpram_try_ipc_tx(dpld, i); + for (i = 0; i < ld->max_ipc_dev; i++) { + if (atomic_read(&pld->res_required[i]) > 0) { + ret = pld_try_ipc_tx(pld, i); if (ret > 0) { - atomic_set(&dpld->res_required[i], 0); - tx_mask |= get_mask_send(dpld, i); + atomic_set(&pld->res_required[i], 0); + mask |= get_mask_send(pld, i); } else if (ret == -ENOSPC) { - tx_mask |= get_mask_req_ack(dpld, i); + mask |= get_mask_req_ack(pld, i); } } } - if (tx_mask) { - send_intr(dpld, INT_NON_CMD(tx_mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); + if (mask) { + send_intr(pld, INT_NON_CMD(mask)); + mif_debug("%s: send intr 0x%04X\n", ld->name, mask); } } -static void handle_cp_crash(struct dpram_link_device *dpld) +static void handle_cp_crash(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); skb_queue_purge(ld->skb_txq[i]); } @@ -1210,27 +975,27 @@ static void handle_cp_crash(struct dpram_link_device *dpld) iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_EXIT); - iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); if (iod) iodevs_for_each(iod->msd, iodev_netif_stop, 0); } static void handle_no_crash_ack(unsigned long arg) { - struct dpram_link_device *dpld = (struct dpram_link_device *)arg; - struct link_device *ld = &dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)arg; + struct link_device *ld = &pld->ld; mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name); - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); - handle_cp_crash(dpld); + handle_cp_crash(pld); } -static int trigger_force_cp_crash(struct dpram_link_device *dpld) +static int trigger_force_cp_crash(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; if (ld->mode == LINK_MODE_ULOAD) { mif_err("%s: CP crash is already in progress\n", ld->mc->name); @@ -1240,79 +1005,72 @@ static int trigger_force_cp_crash(struct dpram_link_device *dpld) ld->mode = LINK_MODE_ULOAD; mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); - dpram_wake_up(dpld); - - send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + send_intr(pld, INT_CMD(INT_CMD_CRASH_EXIT)); - mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)dpld); + mif_add_timer(&pld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_crash_ack, (unsigned long)pld); return 0; } -static int dpram_init_ipc(struct dpram_link_device *dpld) +static int pld_init_ipc(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i; if (ld->mode == LINK_MODE_IPC && - get_magic(dpld) == DPRAM_MAGIC_CODE && - get_access(dpld) == 1) + get_magic(pld) == DPRAM_MAGIC_CODE && + get_access(pld) == 1) mif_info("%s: IPC already initialized\n", ld->name); /* Clear pointers in every circular queue */ - for (i = 0; i < dpld->max_ipc_dev; i++) { - set_tx_head(dpld, i, 0); - set_tx_tail(dpld, i, 0); - set_rx_head(dpld, i, 0); - set_rx_tail(dpld, i, 0); + for (i = 0; i < ld->max_ipc_dev; i++) { + set_tx_head(pld, i, 0); + set_tx_tail(pld, i, 0); + set_rx_head(pld, i, 0); + set_rx_tail(pld, i, 0); } /* Initialize variables for efficient TX/RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->iod[i] = link_get_iod_with_format(ld, i); - dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + for (i = 0; i < ld->max_ipc_dev; i++) + pld->iod[i] = link_get_iod_with_format(ld, i); + pld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - if (dpld->iod[IPC_RAW]->recv_skb) - dpld->use_skb = true; + for (i = 0; i < ld->max_ipc_dev; i++) + atomic_set(&pld->res_required[i], 0); - for (i = 0; i < dpld->max_ipc_dev; i++) { - atomic_set(&dpld->res_required[i], 0); - skb_queue_purge(&dpld->skb_rxq[i]); - } - - spin_lock_init(&dpld->tx_rx_lock); + spin_lock_init(&pld->tx_rx_lock); /* Enable IPC */ - atomic_set(&dpld->accessing, 0); + atomic_set(&pld->accessing, 0); - set_magic(dpld, DPRAM_MAGIC_CODE); - set_access(dpld, 1); - if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + set_magic(pld, DPRAM_MAGIC_CODE); + set_access(pld, 1); + if (get_magic(pld) != DPRAM_MAGIC_CODE || get_access(pld) != 1) return -EACCES; ld->mode = LINK_MODE_IPC; - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + if (wake_lock_active(&pld->wlock)) + wake_unlock(&pld->wlock); return 0; } -static void cmd_req_active_handler(struct dpram_link_device *dpld) +static void cmd_req_active_handler(struct pld_link_device *pld) { - send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_intr(pld, INT_CMD(INT_CMD_RES_ACTIVE)); } -static void cmd_crash_reset_handler(struct dpram_link_device *dpld) +static void cmd_crash_reset_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod = NULL; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); @@ -1323,35 +1081,33 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) iod->modem_state_changed(iod, STATE_CRASH_RESET); } -static void cmd_crash_exit_handler(struct dpram_link_device *dpld) +static void cmd_crash_exit_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - dpram_wake_up(dpld); - - del_timer(&dpld->crash_ack_timer); + del_timer(&pld->crash_ack_timer); - if (dpld->ext_op && dpld->ext_op->crash_log) - dpld->ext_op->crash_log(dpld); + if (pld->ext_op && pld->ext_op->crash_log) + pld->ext_op->crash_log(pld); - handle_cp_crash(dpld); + handle_cp_crash(pld); } -static void cmd_phone_start_handler(struct dpram_link_device *dpld) +static void cmd_phone_start_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod = NULL; mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); - dpram_init_ipc(dpld); + pld_init_ipc(pld); iod = link_get_iod_with_format(ld, IPC_FMT); if (!iod) { @@ -1359,8 +1115,8 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) return; } - if (dpld->ext_op && dpld->ext_op->cp_start_handler) - dpld->ext_op->cp_start_handler(dpld); + if (pld->ext_op && pld->ext_op->cp_start_handler) + pld->ext_op->cp_start_handler(pld); if (ld->mc->phone_state != STATE_ONLINE) { mif_info("%s: phone_state: %d -> ONLINE\n", @@ -1369,32 +1125,32 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) } mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); + send_intr(pld, INT_CMD(INT_CMD_INIT_END)); } -static void command_handler(struct dpram_link_device *dpld, u16 cmd) +static void command_handler(struct pld_link_device *pld, u16 cmd) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; switch (INT_CMD_MASK(cmd)) { case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(dpld); + cmd_req_active_handler(pld); break; case INT_CMD_CRASH_RESET: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_reset_handler(dpld); + pld->init_status = PLD_INIT_STATE_NONE; + cmd_crash_reset_handler(pld); break; case INT_CMD_CRASH_EXIT: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_exit_handler(dpld); + pld->init_status = PLD_INIT_STATE_NONE; + cmd_crash_exit_handler(pld); break; case INT_CMD_PHONE_START: - dpld->dpram_init_status = DPRAM_INIT_STATE_READY; - cmd_phone_start_handler(dpld); - complete_all(&dpld->dpram_init_cmd); + pld->init_status = PLD_INIT_STATE_READY; + cmd_phone_start_handler(pld); + complete_all(&pld->dpram_init_cmd); break; case INT_CMD_NV_REBUILDING: @@ -1402,14 +1158,14 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&dpld->modem_pif_init_done); + complete_all(&pld->modem_pif_init_done); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_PWR_OFF: + case INT_CMD_NORMAL_POWER_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1424,123 +1180,46 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) } } -static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) -{ - struct link_device *ld = &dpld->ld; - u16 resp; - - switch (EXT_CMD_MASK(cmd)) { - case EXT_CMD_SET_SPEED_LOW: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); - send_intr(dpld, resp); - } - break; - - case EXT_CMD_SET_SPEED_MID: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_MID); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); - send_intr(dpld, resp); - } - break; - - case EXT_CMD_SET_SPEED_HIGH: - if (dpld->dpctl->setup_speed) { - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); - resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); - send_intr(dpld, resp); - } - break; - - default: - mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); - break; - } -} - -static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) -{ - struct link_device *ld = &dpld->ld; - - if (cmd & UDL_RESULT_FAIL) { - mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); - return; - } - - switch (UDL_CMD_MASK(cmd)) { - case UDL_CMD_RECV_READY: - mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); - send_intr(dpld, CMD_IMG_START_REQ); - break; - default: - complete_all(&dpld->udl_cmd_complete); - } -} - -static irqreturn_t dpram_irq_handler(int irq, void *data) +static irqreturn_t pld_irq_handler(int irq, void *data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = (struct link_device *)&dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)data; + struct link_device *ld = (struct link_device *)&pld->ld; u16 int2ap = 0; if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - if (dpram_wake_up(dpld) < 0) { - log_dpram_status(dpld); - trigger_force_cp_crash(dpld); - return IRQ_HANDLED; - } - - int2ap = recv_intr(dpld); + int2ap = recv_intr(pld); if (unlikely(int2ap == INT_POWERSAFE_FAIL)) { mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); goto exit; } else if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { - if (dpld->ext_op && dpld->ext_op->dload_cmd_handler) { - dpld->ext_op->dload_cmd_handler(dpld, int2ap); + if (pld->ext_op && pld->ext_op->dload_cmd_handler) { + pld->ext_op->dload_cmd_handler(pld, int2ap); goto exit; } } - if (unlikely(EXT_UDL_CMD(int2ap))) { - if (likely(EXT_INT_VALID(int2ap))) { - if (UDL_CMD_VALID(int2ap)) - udl_command_handler(dpld, int2ap); - else if (EXT_CMD_VALID(int2ap)) - ext_command_handler(dpld, int2ap); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); - } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); - } + if (likely(INT_VALID(int2ap))) { + if (unlikely(INT_CMD_VALID(int2ap))) + command_handler(pld, int2ap); + else + non_command_handler(pld, int2ap); } else { - if (likely(INT_VALID(int2ap))) { - if (unlikely(INT_CMD_VALID(int2ap))) - command_handler(dpld, int2ap); - else - non_command_handler(dpld, int2ap); - } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", - ld->name, int2ap); - } + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); } exit: - clear_intr(dpld); - dpram_allow_sleep(dpld); + clear_intr(pld); return IRQ_HANDLED; } -static void dpram_send_ipc(struct link_device *ld, int dev, +static void pld_send_ipc(struct link_device *ld, int dev, struct io_device *iod, struct sk_buff *skb) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; u16 mask; @@ -1551,46 +1230,31 @@ static void dpram_send_ipc(struct link_device *ld, int dev, ld->name, get_dev_name(dev), txq->qlen); } - if (dpram_wake_up(dpld) < 0) { - trigger_force_cp_crash(dpld); - return; - } - - if (!dpram_ipc_active(dpld)) + if (!ipc_active(pld)) goto exit; - if (atomic_read(&dpld->res_required[dev]) > 0) { + if (atomic_read(&pld->res_required[dev]) > 0) { mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); goto exit; } - ret = dpram_try_ipc_tx(dpld, dev); + ret = pld_try_ipc_tx(pld, dev); if (ret > 0) { - mask = get_mask_send(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); + mask = get_mask_send(pld, dev); + send_intr(pld, INT_NON_CMD(mask)); } else if (ret == -ENOSPC) { - mask = get_mask_req_ack(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); + mask = get_mask_req_ack(pld, dev); + send_intr(pld, INT_NON_CMD(mask)); mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); } else { - mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); + mif_info("%s: pld_try_ipc_tx fail (err %d)\n", ld->name, ret); } exit: - dpram_allow_sleep(dpld); -} - -static int dpram_download_bin(struct link_device *ld, struct sk_buff *skb) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - if (dpld->ext_op && dpld->ext_op->dload_bin) - return dpld->ext_op->dload_bin(dpld, skb); - else - return -ENODEV; + return; } -static int dpram_send(struct link_device *ld, struct io_device *iod, +static int pld_send(struct link_device *ld, struct io_device *iod, struct sk_buff *skb) { enum dev_format dev = iod->format; @@ -1601,16 +1265,13 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_send_ipc(ld, dev, iod, skb); + pld_send_ipc(ld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); } return len; - case IPC_BOOT: - return dpram_download_bin(ld, skb); - default: mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name); dev_kfree_skb_any(skb); @@ -1618,48 +1279,38 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static int pld_force_dump(struct link_device *ld, struct io_device *iod) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); - trigger_force_cp_crash(dpld); + struct pld_link_device *pld = to_pld_link_device(ld); + trigger_force_cp_crash(pld); return 0; } -static void dpram_dump_memory(struct link_device *ld, char *buff) +static int pld_dump_start(struct link_device *ld, struct io_device *iod) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *base = dpld->dpctl->dp_base; - u32 size = dpld->dpctl->dp_size; + struct pld_link_device *pld = to_pld_link_device(ld); - dpram_wake_up(dpld); - memcpy(buff, base, size); -} - -static int dpram_dump_start(struct link_device *ld, struct io_device *iod) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - - if (dpld->ext_op && dpld->ext_op->dump_start) - return dpld->ext_op->dump_start(dpld); + if (pld->ext_op && pld->ext_op->dump_start) + return pld->ext_op->dump_start(pld); else return -ENODEV; } -static int dpram_dump_update(struct link_device *ld, struct io_device *iod, +static int pld_dump_update(struct link_device *ld, struct io_device *iod, unsigned long arg) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); - if (dpld->ext_op && dpld->ext_op->dump_update) - return dpld->ext_op->dump_update(dpld, (void *)arg); + if (pld->ext_op && pld->ext_op->dump_update) + return pld->ext_op->dump_update(pld, (void *)arg); else return -ENODEV; } -static int dpram_ioctl(struct link_device *ld, struct io_device *iod, +static int pld_ioctl(struct link_device *ld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); int err = 0; /* @@ -1669,11 +1320,11 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + return pld->init_status; default: - if (dpld->ext_ioctl) { - err = dpld->ext_ioctl(dpld, iod, cmd, arg); + if (pld->ext_ioctl) { + err = pld->ext_ioctl(pld, iod, cmd, arg); } else { mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); err = -EINVAL; @@ -1685,97 +1336,97 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, return err; } -static int dpram_table_init(struct dpram_link_device *dpld) +static int pld_table_init(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; u8 __iomem *dp_base; int i; - if (!dpld->dp_base) { - mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); + if (!pld->base) { + mif_info("%s: ERR! pld->base == NULL\n", ld->name); return -EINVAL; } - dp_base = dpld->dp_base; + dp_base = pld->base; /* Map for IPC */ - if (dpld->dpctl->ipc_map) { - memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, - sizeof(struct dpram_ipc_map)); + if (pld->dpram->ipc_map) { + memcpy(&pld->ipc_map, pld->dpram->ipc_map, + sizeof(struct pld_ipc_map)); } - dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp; - dpld->access_ap2cp = dpld->ipc_map.access_ap2cp; + pld->magic_ap2cp = pld->ipc_map.magic_ap2cp; + pld->access_ap2cp = pld->ipc_map.access_ap2cp; - dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap; - dpld->access_cp2ap = dpld->ipc_map.access_cp2ap; + pld->magic_cp2ap = pld->ipc_map.magic_cp2ap; + pld->access_cp2ap = pld->ipc_map.access_cp2ap; - dpld->address_buffer = dpld->ipc_map.address_buffer; + pld->address_buffer = pld->ipc_map.address_buffer; - for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->dev[i] = &dpld->ipc_map.dev[i]; - dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; - dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + for (i = 0; i < ld->max_ipc_dev; i++) + pld->dev[i] = &pld->ipc_map.dev[i]; + pld->mbx2ap = pld->ipc_map.mbx_cp2ap; + pld->mbx2cp = pld->ipc_map.mbx_ap2cp; /* Map for booting */ - if (dpld->ext_op && dpld->ext_op->init_boot_map) { - dpld->ext_op->init_boot_map(dpld); + if (pld->ext_op && pld->ext_op->init_boot_map) { + pld->ext_op->init_boot_map(pld); } else { - dpld->bt_map.magic = (u32 *)(dp_base); - dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); - dpld->bt_map.size = dpld->dp_size - 8; + pld->bt_map.magic = (u32 *)(dp_base); + pld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); + pld->bt_map.space = pld->size - 8; } /* Map for download (FOTA, UDL, etc.) */ - if (dpld->ext_op && dpld->ext_op->init_dl_map) { - dpld->ext_op->init_dl_map(dpld); + if (pld->ext_op && pld->ext_op->init_dl_map) { + pld->ext_op->init_dl_map(pld); } else { - dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + pld->dl_map.magic = (u32 *)(dp_base); + pld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); } /* Map for upload mode */ - if (dpld->ext_op && dpld->ext_op->init_ul_map) { - dpld->ext_op->init_ul_map(dpld); + if (pld->ext_op && pld->ext_op->init_ul_map) { + pld->ext_op->init_ul_map(pld); } else { - dpld->ul_map.magic = (u32 *)(dp_base); - dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + pld->ul_map.magic = (u32 *)(dp_base); + pld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); } return 0; } -static void dpram_setup_common_op(struct dpram_link_device *dpld) -{ - dpld->clear_intr = clear_intr; - dpld->recv_intr = recv_intr; - dpld->send_intr = send_intr; - dpld->get_magic = get_magic; - dpld->set_magic = set_magic; - dpld->get_access = get_access; - dpld->set_access = set_access; - dpld->get_tx_head = get_tx_head; - dpld->get_tx_tail = get_tx_tail; - dpld->set_tx_head = set_tx_head; - dpld->set_tx_tail = set_tx_tail; - dpld->get_tx_buff = get_tx_buff; - dpld->get_tx_buff_size = get_tx_buff_size; - dpld->get_rx_head = get_rx_head; - dpld->get_rx_tail = get_rx_tail; - dpld->set_rx_head = set_rx_head; - dpld->set_rx_tail = set_rx_tail; - dpld->get_rx_buff = get_rx_buff; - dpld->get_rx_buff_size = get_rx_buff_size; - dpld->get_mask_req_ack = get_mask_req_ack; - dpld->get_mask_res_ack = get_mask_res_ack; - dpld->get_mask_send = get_mask_send; -} - -static int dpram_link_init(struct link_device *ld, struct io_device *iod) +static void pld_setup_common_op(struct pld_link_device *pld) +{ + pld->clear_intr = clear_intr; + pld->recv_intr = recv_intr; + pld->send_intr = send_intr; + pld->get_magic = get_magic; + pld->set_magic = set_magic; + pld->get_access = get_access; + pld->set_access = set_access; + pld->get_tx_head = get_tx_head; + pld->get_tx_tail = get_tx_tail; + pld->set_tx_head = set_tx_head; + pld->set_tx_tail = set_tx_tail; + pld->get_tx_buff = get_tx_buff; + pld->get_tx_buff_size = get_tx_buff_size; + pld->get_rx_head = get_rx_head; + pld->get_rx_tail = get_rx_tail; + pld->set_rx_head = set_rx_head; + pld->set_rx_tail = set_rx_tail; + pld->get_rx_buff = get_rx_buff; + pld->get_rx_buff_size = get_rx_buff_size; + pld->get_mask_req_ack = get_mask_req_ack; + pld->get_mask_res_ack = get_mask_res_ack; + pld->get_mask_send = get_mask_send; +} + +static int pld_link_init(struct link_device *ld, struct io_device *iod) { return 0; } -static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +static void pld_link_terminate(struct link_device *ld, struct io_device *iod) { return; } @@ -1783,11 +1434,11 @@ static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) struct link_device *pld_create_link_device(struct platform_device *pdev) { struct modem_data *mdm_data = NULL; - struct dpram_link_device *dpld = NULL; + struct pld_link_device *pld = NULL; struct link_device *ld = NULL; struct resource *res = NULL; resource_size_t res_size; - struct modemlink_dpram_control *dpctl = NULL; + struct modemlink_dpram_data *dpram = NULL; unsigned long task_data; int ret = 0; int i = 0; @@ -1803,19 +1454,19 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) mif_info("modem = %s\n", mdm_data->name); mif_info("link device = %s\n", mdm_data->link_name); - if (!mdm_data->dpram_ctl) { - mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); + if (!mdm_data->dpram) { + mif_info("ERR! no mdm_data->dpram\n"); goto err; } - dpctl = mdm_data->dpram_ctl; + dpram = mdm_data->dpram; /* Alloc DPRAM link device structure */ - dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); - if (!dpld) { - mif_info("ERR! kzalloc dpld fail\n"); + pld = kzalloc(sizeof(struct pld_link_device), GFP_KERNEL); + if (!pld) { + mif_info("ERR! kzalloc pld fail\n"); goto err; } - ld = &dpld->ld; + ld = &pld->ld; /* Retrieve modem data and DPRAM control data from the modem data */ ld->mdm_data = mdm_data; @@ -1823,30 +1474,30 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) ld->ipc_version = mdm_data->ipc_version; /* Retrieve the most basic data for IPC from the modem data */ - dpld->dpctl = dpctl; - dpld->dp_type = dpctl->dp_type; + pld->dpram = dpram; + pld->type = dpram->type; if (mdm_data->ipc_version < SIPC_VER_50) { - if (!dpctl->max_ipc_dev) { + if (!mdm_data->max_ipc_dev) { mif_info("ERR! no max_ipc_dev\n"); goto err; } - ld->aligned = dpctl->aligned; - dpld->max_ipc_dev = dpctl->max_ipc_dev; + ld->aligned = dpram->aligned; + ld->max_ipc_dev = mdm_data->max_ipc_dev; } else { ld->aligned = 1; - dpld->max_ipc_dev = MAX_SIPC5_DEV; + ld->max_ipc_dev = MAX_SIPC5_DEV; } /* Set attributes as a link device */ - ld->init_comm = dpram_link_init; - ld->terminate_comm = dpram_link_terminate; - ld->send = dpram_send; - ld->force_dump = dpram_force_dump; - ld->dump_start = dpram_dump_start; - ld->dump_update = dpram_dump_update; - ld->ioctl = dpram_ioctl; + ld->init_comm = pld_link_init; + ld->terminate_comm = pld_link_terminate; + ld->send = pld_send; + ld->force_dump = pld_force_dump; + ld->dump_start = pld_dump_start; + ld->dump_update = pld_dump_update; + ld->ioctl = pld_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1858,14 +1509,13 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; /* Set up function pointers */ - dpram_setup_common_op(dpld); - dpld->dpram_dump = dpram_dump_memory; - dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); - if (dpld->ext_op && dpld->ext_op->ioctl) - dpld->ext_ioctl = dpld->ext_op->ioctl; + pld_setup_common_op(pld); + pld->ext_op = pld_get_ext_op(mdm_data->modem_type); + if (pld->ext_op && pld->ext_op->ioctl) + pld->ext_ioctl = pld->ext_op->ioctl; /* Retrieve DPRAM resource */ - if (!dpctl->dp_base) { + if (!dpram->base) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { mif_info("%s: ERR! platform_get_resource fail\n", @@ -1874,59 +1524,54 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } res_size = resource_size(res); - dpctl->dp_base = ioremap_nocache(res->start, res_size); - dpctl->dp_size = res_size; + dpram->base = ioremap_nocache(res->start, res_size); + dpram->size = res_size; } - dpld->dp_base = dpctl->dp_base; - dpld->dp_size = dpctl->dp_size; + pld->base = dpram->base; + pld->size = dpram->size; - mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", - ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, - dpld->dp_size); + mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", + ld->name, pld->type, ld->aligned, (int)pld->base, pld->size); /* Initialize DPRAM map (physical map -> logical map) */ - ret = dpram_table_init(dpld); + ret = pld_table_init(pld); if (ret < 0) { - mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + mif_info("%s: ERR! pld_table_init fail (err %d)\n", ld->name, ret); goto err; } - spin_lock_init(&dpld->pld_lock); + spin_lock_init(&pld->pld_lock); /* Disable IPC */ - set_magic(dpld, 0); - set_access(dpld, 0); + set_magic(pld, 0); + set_access(pld, 0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + pld->init_status = PLD_INIT_STATE_NONE; /* Initialize locks, completions, and bottom halves */ - snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); - wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); - - init_completion(&dpld->dpram_init_cmd); - init_completion(&dpld->modem_pif_init_done); - init_completion(&dpld->udl_start_complete); - init_completion(&dpld->udl_cmd_complete); - init_completion(&dpld->dump_start_complete); - init_completion(&dpld->dump_recv_done); + snprintf(pld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); + wake_lock_init(&pld->wlock, WAKE_LOCK_SUSPEND, pld->wlock_name); - task_data = (unsigned long)dpld; - tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); + init_completion(&pld->dpram_init_cmd); + init_completion(&pld->modem_pif_init_done); + init_completion(&pld->udl_start_complete); + init_completion(&pld->udl_cmd_complete); + init_completion(&pld->crash_start_complete); + init_completion(&pld->crash_recv_done); - /* Prepare SKB queue head for RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - skb_queue_head_init(&dpld->skb_rxq[i]); + task_data = (unsigned long)pld; + tasklet_init(&pld->rx_tsk, pld_ipc_rx_task, task_data); /* Prepare RXB queue */ - qsize = DPRAM_MAX_RXBQ_SIZE; - for (i = 0; i < dpld->max_ipc_dev; i++) { - bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); - dpld->rxbq[i].size = qsize; - dpld->rxbq[i].in = 0; - dpld->rxbq[i].out = 0; - dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); - if (!dpld->rxbq[i].rxb) { + qsize = MAX_RXBQ_SIZE; + for (i = 0; i < ld->max_ipc_dev; i++) { + bsize = rxbq_get_page_size(get_rx_buff_size(pld, i)); + pld->rxbq[i].size = qsize; + pld->rxbq[i].in = 0; + pld->rxbq[i].out = 0; + pld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); + if (!pld->rxbq[i].rxb) { mif_info("%s: ERR! %s rxbq_create_pool fail\n", ld->name, get_dev_name(i)); goto err; @@ -1936,44 +1581,37 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } /* Prepare a multi-purpose miscellaneous buffer */ - dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); - if (!dpld->buff) { - mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + pld->buff = kzalloc(pld->size, GFP_KERNEL); + if (!pld->buff) { + mif_info("%s: ERR! kzalloc pld->buff fail\n", ld->name); goto err; } /* Retrieve DPRAM IRQ GPIO# */ - dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; + pld->gpio_ipc_int2ap = mdm_data->gpio_ipc_int2ap; /* Retrieve DPRAM IRQ# */ - if (!dpctl->dpram_irq) { - dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); - if (dpctl->dpram_irq < 0) { - mif_info("%s: ERR! platform_get_irq_byname fail\n", - ld->name); - goto err; - } - } - dpld->irq = dpctl->dpram_irq; + pld->irq = mdm_data->irq_ipc_int2ap; /* Retrieve DPRAM IRQ flags */ - if (!dpctl->dpram_irq_flags) - dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); - dpld->irq_flags = dpctl->dpram_irq_flags; + if (mdm_data->irqf_ipc_int2ap) + pld->irq_flags = mdm_data->irqf_ipc_int2ap; + else + pld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); /* Register DPRAM interrupt handler */ - snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); - ret = dpram_register_isr(dpld->irq, dpram_irq_handler, dpld->irq_flags, - dpld->irq_name, dpld); + snprintf(pld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); + ret = pld_register_isr(pld->irq, pld_irq_handler, pld->irq_flags, + pld->irq_name, pld); if (ret) goto err; return ld; err: - if (dpld) { - kfree(dpld->buff); - kfree(dpld); + if (pld) { + kfree(pld->buff); + kfree(pld); } return NULL; diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h index 2656110..2690faa 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.h +++ b/drivers/misc/modem_if/modem_link_device_pld.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,181 +11,134 @@ * GNU General Public License for more details. * */ -#ifndef __MODEM_LINK_DEVICE_DPRAM_H__ -#define __MODEM_LINK_DEVICE_DPRAM_H__ - -#include -#include -#include -#include -#include - -#include "modem_prj.h" - -#define DPRAM_MAGIC_CODE 0xAA - -/* interrupt masks.*/ -#define INT_MASK_VALID 0x0080 -#define INT_MASK_CMD 0x0040 -#define INT_VALID(x) ((x) & INT_MASK_VALID) -#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) -#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) -#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) - -#define EXT_UDL_MASK 0xF000 -#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) -#define EXT_INT_VALID_MASK 0x8000 -#define EXT_CMD_VALID_MASK 0x4000 -#define UDL_CMD_VALID_MASK 0x2000 -#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) -#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) -#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) -#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) - -#define EXT_CMD_MASK(x) ((x) & 0x0FFF) -#define EXT_CMD_SET_SPEED_LOW 0x0011 -#define EXT_CMD_SET_SPEED_MID 0x0012 -#define EXT_CMD_SET_SPEED_HIGH 0x0013 - -#define UDL_RESULT_SUCCESS 0x1 -#define UDL_RESULT_FAIL 0x2 - -#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) -#define UDL_CMD_RECV_READY 0x1 -#define UDL_CMD_DL_START_REQ 0x2 -#define UDL_CMD_DL_START_RESP 0x3 -#define UDL_CMD_IMAGE_SEND_REQ 0x4 -#define UDL_CMD_SEND_DONE_RESP 0x5 -#define UDL_CMD_SEND_DONE_REQ 0x6 -#define UDL_CMD_UPDATE_DONE 0x7 -#define UDL_CMD_STATUS_UPDATE 0x8 -#define UDL_CMD_IMAGE_SEND_RESP 0x9 -#define UDL_CMD_EFS_CLEAR_RESP 0xB -#define UDL_CMD_ALARM_BOOT_OK 0xC -#define UDL_CMD_ALARM_BOOT_FAIL 0xD - -#define CMD_IMG_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 -#define CMD_UL_RECV_RESP 0x9601 -#define CMD_UL_RECV_DONE_RESP 0x9801 - -/* special interrupt cmd indicating modem boot failure. */ -#define INT_POWERSAFE_FAIL 0xDEAD - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_REQ_ACK_F 0x0020 -#define INT_MASK_REQ_ACK_R 0x0010 -#define INT_MASK_RES_ACK_F 0x0008 -#define INT_MASK_RES_ACK_R 0x0004 -#define INT_MASK_SEND_F 0x0002 -#define INT_MASK_SEND_R 0x0001 - -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - -#define INT_MASK_RES_ACK_SET \ - (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) - -#define INT_MASK_SEND_SET \ - (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) - -#define INT_CMD_MASK(x) ((x) & 0xF) -#define INT_CMD_INIT_START 0x1 -#define INT_CMD_INIT_END 0x2 -#define INT_CMD_REQ_ACTIVE 0x3 -#define INT_CMD_RES_ACTIVE 0x4 -#define INT_CMD_REQ_TIME_SYNC 0x5 -#define INT_CMD_CRASH_RESET 0x7 -#define INT_CMD_PHONE_START 0x8 -#define INT_CMD_ERR_DISPLAY 0x9 -#define INT_CMD_CRASH_EXIT 0x9 -#define INT_CMD_CP_DEEP_SLEEP 0xA -#define INT_CMD_NV_REBUILDING 0xB -#define INT_CMD_EMER_DOWN 0xC -#define INT_CMD_PIF_INIT_DONE 0xD -#define INT_CMD_SILENT_NV_REBUILDING 0xE -#define INT_CMD_NORMAL_PWR_OFF 0xF - -#define START_FLAG 0x7F -#define END_FLAG 0x7E - -#define DP_MAGIC_DMDL 0x4445444C -#define DP_MAGIC_UMDL 0x4445444D -#define DP_DPRAM_SIZE 0x4000 -#define DP_DEFAULT_WRITE_LEN 8168 -#define DP_DEFAULT_DUMP_LEN 16128 -#define DP_DUMP_HEADER_SIZE 7 - -#define UDL_TIMEOUT (50 * HZ) -#define UDL_SEND_TIMEOUT (200 * HZ) -#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) -#define DUMP_TIMEOUT (30 * HZ) -#define DUMP_START_TIMEOUT (100 * HZ) -#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ +#ifndef __MODEM_LINK_DEVICE_PLD_H__ +#define __MODEM_LINK_DEVICE_PLD_H__ -#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) +#include "modem_link_device_memory.h" -enum host_boot_mode { - HOST_BOOT_MODE_NORMAL, - HOST_BOOT_MODE_DUMP, -}; +#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) -enum dpram_init_status { - DPRAM_INIT_STATE_NONE, - DPRAM_INIT_STATE_READY, +enum pld_init_status { + PLD_INIT_STATE_NONE, + PLD_INIT_STATE_READY, }; -struct dpram_boot_img { - char *addr; - int size; - enum host_boot_mode mode; - unsigned req; - unsigned resp; -}; +#if 1 +#define MAX_RXBQ_SIZE 256 -#define MAX_PAYLOAD_SIZE 0x2000 -struct dpram_boot_frame { - unsigned req; /* AP->CP request */ - unsigned resp; /* response expected by AP */ - ssize_t len; /* data size in the buffer */ - unsigned offset; /* offset to write into DPRAM */ - char data[MAX_PAYLOAD_SIZE]; -}; +struct mif_rxb { + u8 *buff; + unsigned size; -/* buffer type for modem image */ -struct dpram_dump_arg { - char *buff; /* pointer to the buffer */ - int buff_size; /* buffer size */ - unsigned req; /* AP->CP request */ - unsigned resp; /* CP->AP response */ - bool cmd; /* AP->CP command */ + u8 *data; + unsigned len; }; -struct dpram_firmware { - char *firmware; +struct mif_rxb_queue { int size; - int is_delta; -}; -enum dpram_link_mode { - DPRAM_LINK_MODE_INVALID = 0, - DPRAM_LINK_MODE_IPC, - DPRAM_LINK_MODE_BOOT, - DPRAM_LINK_MODE_DLOAD, - DPRAM_LINK_MODE_ULOAD, + int in; + int out; + struct mif_rxb *rxb; }; -struct dpram_boot_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 __iomem *req; - u32 __iomem *resp; - u32 size; -}; +/* +** RXB (DPRAM RX buffer) functions +*/ +static inline struct mif_rxb *rxbq_create_pool(unsigned size, int count) +{ + struct mif_rxb *rxb; + u8 *buff; + int i; + + rxb = kzalloc(sizeof(struct mif_rxb) * count, GFP_KERNEL); + if (!rxb) { + mif_info("ERR! kzalloc rxb fail\n"); + return NULL; + } + + buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); + if (!buff) { + mif_info("ERR! kzalloc buff fail\n"); + kfree(rxb); + return NULL; + } + + for (i = 0; i < count; i++) { + rxb[i].buff = buff; + rxb[i].size = size; + buff += size; + } + + return rxb; +} + +static inline unsigned rxbq_get_page_size(unsigned len) +{ + return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; +} + +static inline bool rxbq_empty(struct mif_rxb_queue *rxbq) +{ + return (rxbq->in == rxbq->out) ? true : false; +} + +static inline int rxbq_free_size(struct mif_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline struct mif_rxb *rxbq_get_free_rxb(struct mif_rxb_queue *rxbq) +{ + struct mif_rxb *rxb = NULL; + + if (likely(rxbq_free_size(rxbq) > 0)) { + rxb = &rxbq->rxb[rxbq->in]; + rxbq->in++; + if (rxbq->in >= rxbq->size) + rxbq->in -= rxbq->size; + rxb->data = rxb->buff; + } + + return rxb; +} + +static inline int rxbq_size(struct mif_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline struct mif_rxb *rxbq_get_data_rxb(struct mif_rxb_queue *rxbq) +{ + struct mif_rxb *rxb = NULL; + + if (likely(!rxbq_empty(rxbq))) { + rxb = &rxbq->rxb[rxbq->out]; + rxbq->out++; + if (rxbq->out >= rxbq->size) + rxbq->out -= rxbq->size; + } + + return rxb; +} + +static inline u8 *rxb_put(struct mif_rxb *rxb, unsigned len) +{ + rxb->len = len; + return rxb->data; +} + +static inline void rxb_clear(struct mif_rxb *rxb) +{ + rxb->data = NULL; + rxb->len = 0; +} +#endif struct qc_dpram_boot_map { u8 __iomem *buff; @@ -195,39 +147,14 @@ struct qc_dpram_boot_map { u16 __iomem *count; }; -struct dpram_dload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct dpram_uload_map { - u32 __iomem *magic; - u8 __iomem *buff; -}; - -struct dpram_ota_header { - u8 start_index; - u16 nframes; - u16 curframe; - u16 len; - -} __packed; - -struct ul_header { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -} __packed; - -struct dpram_udl_param { +struct qc_dpram_udl_param { unsigned char *addr; unsigned int size; unsigned int count; unsigned int tag; }; -struct dpram_udl_check { +struct qc_dpram_udl_check { unsigned int total_size; unsigned int rest_size; unsigned int send_size; @@ -236,180 +163,38 @@ struct dpram_udl_check { unsigned int boot_complete; }; -#define DP_BOOT_BUFF_OFFSET 4 -#define DP_DLOAD_BUFF_OFFSET 4 -#define DP_ULOAD_BUFF_OFFSET 4 -#define DP_BOOT_REQ_OFFSET 0 -#define DP_BOOT_RESP_OFFSET 8 - -#define MAX_WQ_NAME_LENGTH 64 - -#define DPRAM_MAX_RXBQ_SIZE 256 - -struct dpram_rxb { - u8 *buff; - unsigned size; +struct pld_ext_op; - u8 *data; - unsigned len; -}; - -struct dpram_rxb_queue { - int size; - int in; - int out; - struct dpram_rxb *rxb; -}; - -/* - mbx_ap2cp + 0x0 - magic_code + - access_enable + - padding + - mbx_cp2ap + 0x1000 - magic_code + - access_enable + - padding + - fmt_tx_head + fmt_tx_tail + fmt_tx_buff + 0x2000 - raw_tx_head + raw_tx_tail + raw_tx_buff + - fmt_rx_head + fmt_rx_tail + fmt_rx_buff + 0x3000 - raw_rx_head + raw_rx_tail + raw_rx_buff + - = 2 + - 4094 + - 2 + - 4094 + - 2 + - 2 + - 2 + 2 + 1020 + - 2 + 2 + 3064 + - 2 + 2 + 1020 + - 2 + 2 + 3064 - */ - -#define DP_PLD_FMT_TX_BUFF_SZ 1024 -#define DP_PLD_RAW_TX_BUFF_SZ 3072 -#define DP_PLD_FMT_RX_BUFF_SZ 1024 -#define DP_PLD_RAW_RX_BUFF_SZ 3072 - -#define MAX_MSM_EDPRAM_IPC_DEV 2 /* FMT, RAW */ - -struct dpram_ipc_pld_map { - u16 mbx_ap2cp; - u16 magic_ap2cp; - u16 access_ap2cp; - u16 fmt_tx_head; - u16 raw_tx_head; - u16 fmt_rx_tail; - u16 raw_rx_tail; - u16 temp1; - u8 padding1[4080]; - - u16 mbx_cp2ap; - u16 magic_cp2ap; - u16 access_cp2ap; - u16 fmt_tx_tail; - u16 raw_tx_tail; - u16 fmt_rx_head; - u16 raw_rx_head; - u16 temp2; - u8 padding2[4080]; - - u8 fmt_tx_buff[DP_PLD_FMT_TX_BUFF_SZ]; - u8 raw_tx_buff[DP_PLD_RAW_TX_BUFF_SZ]; - u8 fmt_rx_buff[DP_PLD_RAW_TX_BUFF_SZ]; - u8 raw_rx_buff[DP_PLD_RAW_RX_BUFF_SZ]; - - u8 padding3[16384]; - - u16 address_buffer; -}; - -/* - magic_code + - access_enable + - fmt_tx_head + fmt_tx_tail + fmt_tx_buff + - raw_tx_head + raw_tx_tail + raw_tx_buff + - fmt_rx_head + fmt_rx_tail + fmt_rx_buff + - raw_rx_head + raw_rx_tail + raw_rx_buff + - mbx_cp2ap + - mbx_ap2cp - = 2 + - 2 + - 2 + 2 + 1336 + - 2 + 2 + 4564 + - 2 + 2 + 1336 + - 2 + 2 + 9124 + - 2 + - 2 - = 16384 -*/ -#define DP_16K_FMT_TX_BUFF_SZ 1336 -#define DP_16K_RAW_TX_BUFF_SZ 4564 -#define DP_16K_FMT_RX_BUFF_SZ 1336 -#define DP_16K_RAW_RX_BUFF_SZ 9124 - -struct dpram_ipc_16k_map { - u16 magic; - u16 access; - - u16 fmt_tx_head; - u16 fmt_tx_tail; - u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ]; - - u16 raw_tx_head; - u16 raw_tx_tail; - u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ]; - - u16 fmt_rx_head; - u16 fmt_rx_tail; - u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ]; - - u16 raw_rx_head; - u16 raw_rx_tail; - u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ]; - - u16 mbx_cp2ap; - u16 mbx_ap2cp; -}; - -#define DP_MAX_NAME_LEN 32 - -struct dpram_ext_op; - -struct dpram_link_device { +struct pld_link_device { struct link_device ld; - /* The mode of this DPRAM link device */ - enum dpram_link_mode mode; - /* DPRAM address and size */ - u8 __iomem *dp_base; /* DPRAM base virtual address */ - u32 dp_size; /* DPRAM size */ - enum dpram_type dp_type; /* DPRAM type */ + enum dpram_type type; /* DPRAM type */ + u8 __iomem *base; /* DPRAM base virtual address */ + u32 size; /* DPRAM size */ /* DPRAM IRQ GPIO# */ - unsigned gpio_dpram_int; + unsigned gpio_ipc_int2ap; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[DP_MAX_NAME_LEN]; + char irq_name[MIF_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - int max_ipc_dev; - struct modemlink_dpram_control *dpctl; + struct modemlink_dpram_data *dpram; /* Physical configuration -> logical configuration */ union { - struct dpram_boot_map bt_map; + struct memif_boot_map bt_map; struct qc_dpram_boot_map qc_bt_map; }; - struct dpram_dload_map dl_map; - struct dpram_uload_map ul_map; + struct memif_dload_map dl_map; + struct memif_uload_map ul_map; /* IPC device map */ - struct dpram_ipc_map ipc_map; + struct pld_ipc_map ipc_map; /* Pointers (aliases) to IPC device map */ u16 __iomem *magic_ap2cp; @@ -424,7 +209,7 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[DP_MAX_NAME_LEN]; + char wlock_name[MIF_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; @@ -432,19 +217,16 @@ struct dpram_link_device { struct completion modem_pif_init_done; /* For UDL */ - struct tasklet_struct ul_tsk; struct tasklet_struct dl_tsk; struct completion udl_start_complete; struct completion udl_cmd_complete; - struct dpram_udl_check udl_check; - struct dpram_udl_param udl_param; + struct qc_dpram_udl_check qc_udl_check; + struct qc_dpram_udl_param qc_udl_param; - /* For CP RAM dump */ + /* For CP crash dump */ struct timer_list crash_ack_timer; - struct completion dump_start_complete; - struct completion dump_recv_done; - struct timer_list dump_timer; - int dump_rcvd; /* Count of dump packets received */ + struct completion crash_start_complete; + struct completion crash_recv_done; /* For locking TX process */ spinlock_t tx_rx_lock; @@ -452,10 +234,8 @@ struct dpram_link_device { /* For efficient RX process */ struct tasklet_struct rx_tsk; - struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct mif_rxb_queue rxbq[MAX_IPC_DEV]; struct io_device *iod[MAX_IPC_DEV]; - bool use_skb; - struct sk_buff_head skb_rxq[MAX_IPC_DEV]; /* For retransmission after buffer full state */ atomic_t res_required[MAX_IPC_DEV]; @@ -466,67 +246,65 @@ struct dpram_link_device { /* Multi-purpose miscellaneous buffer */ u8 *buff; - /* DPRAM IPC initialization status */ - int dpram_init_status; + /* PLD IPC initialization status */ + int init_status; /* Alias to device-specific IOCTL function */ - int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + int (*ext_ioctl)(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg); - /* For DPRAM dump */ - void (*dpram_dump)(struct link_device *ld, char *buff); - /* Common operations for each DPRAM */ - void (*clear_intr)(struct dpram_link_device *dpld); - u16 (*recv_intr)(struct dpram_link_device *dpld); - void (*send_intr)(struct dpram_link_device *dpld, u16 mask); - u16 (*get_magic)(struct dpram_link_device *dpld); - void (*set_magic)(struct dpram_link_device *dpld, u16 value); - u16 (*get_access)(struct dpram_link_device *dpld); - void (*set_access)(struct dpram_link_device *dpld, u16 value); - u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); - void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); - void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); - void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); - u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); - u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); - u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); - u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); - u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); + void (*clear_intr)(struct pld_link_device *pld); + u16 (*recv_intr)(struct pld_link_device *pld); + void (*send_intr)(struct pld_link_device *pld, u16 mask); + u16 (*get_magic)(struct pld_link_device *pld); + void (*set_magic)(struct pld_link_device *pld, u16 value); + u16 (*get_access)(struct pld_link_device *pld); + void (*set_access)(struct pld_link_device *pld, u16 value); + u32 (*get_tx_head)(struct pld_link_device *pld, int id); + u32 (*get_tx_tail)(struct pld_link_device *pld, int id); + void (*set_tx_head)(struct pld_link_device *pld, int id, u32 head); + void (*set_tx_tail)(struct pld_link_device *pld, int id, u32 tail); + u8 *(*get_tx_buff)(struct pld_link_device *pld, int id); + u32 (*get_tx_buff_size)(struct pld_link_device *pld, int id); + u32 (*get_rx_head)(struct pld_link_device *pld, int id); + u32 (*get_rx_tail)(struct pld_link_device *pld, int id); + void (*set_rx_head)(struct pld_link_device *pld, int id, u32 head); + void (*set_rx_tail)(struct pld_link_device *pld, int id, u32 tail); + u8 *(*get_rx_buff)(struct pld_link_device *pld, int id); + u32 (*get_rx_buff_size)(struct pld_link_device *pld, int id); + u16 (*get_mask_req_ack)(struct pld_link_device *pld, int id); + u16 (*get_mask_res_ack)(struct pld_link_device *pld, int id); + u16 (*get_mask_send)(struct pld_link_device *pld, int id); /* Extended operations for various modems */ - struct dpram_ext_op *ext_op; + struct pld_ext_op *ext_op; }; /* converts from struct link_device* to struct xxx_link_device* */ -#define to_dpram_link_device(linkdev) \ - container_of(linkdev, struct dpram_link_device, ld) +#define to_pld_link_device(linkdev) \ + container_of(linkdev, struct pld_link_device, ld) -struct dpram_ext_op { +struct pld_ext_op { int exist; - void (*init_boot_map)(struct dpram_link_device *dpld); - void (*init_dl_map)(struct dpram_link_device *dpld); - void (*init_ul_map)(struct dpram_link_device *dpld); + void (*init_boot_map)(struct pld_link_device *pld); + void (*init_dl_map)(struct pld_link_device *pld); + void (*init_ul_map)(struct pld_link_device *pld); - int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb); - void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd); + void (*dload_cmd_handler)(struct pld_link_device *pld, u16 cmd); - void (*cp_start_handler)(struct dpram_link_device *dpld); + void (*cp_start_handler)(struct pld_link_device *pld); - void (*crash_log)(struct dpram_link_device *dpld); - int (*dump_start)(struct dpram_link_device *dpld); - int (*dump_update)(struct dpram_link_device *dpld, void *arg); + void (*crash_log)(struct pld_link_device *pld); + int (*dump_start)(struct pld_link_device *pld); + int (*dump_update)(struct pld_link_device *pld, void *arg); - int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + int (*ioctl)(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg); + + void (*clear_intr)(struct pld_link_device *pld); }; -struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +struct pld_ext_op *pld_get_ext_op(enum modem_t modem); #endif diff --git a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c index ae6578c..26b0e28 100644 --- a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c @@ -25,8 +25,8 @@ #include #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" @@ -41,56 +41,55 @@ enum qc_dload_tag { static void qc_dload_task(unsigned long data); -static void qc_init_boot_map(struct dpram_link_device *dpld) +static void qc_init_boot_map(struct pld_link_device *pld) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct modemlink_dpram_data *dpram = pld->dpram; - bt_map->buff = dpld->dev[0]->txq.buff; - bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); - bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); - bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); + qbt_map->buff = pld->dev[0]->txq.buff; + qbt_map->frame_size = (u16 *)(pld->base + dpram->boot_size_offset); + qbt_map->tag = (u16 *)(pld->base + dpram->boot_tag_offset); + qbt_map->count = (u16 *)(pld->base + dpram->boot_count_offset); - tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); + tasklet_init(&pld->dl_tsk, qc_dload_task, (unsigned long)pld); } -static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload) +static void qc_dload_map(struct pld_link_device *pld, u8 is_upload) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct modemlink_dpram_data *dpram = pld->dpram; unsigned int upload_offset = 0; if (is_upload == 1) { upload_offset = 0x1000; - bt_map->buff = dpld->dev[0]->rxq.buff; + qbt_map->buff = pld->dev[0]->rxq.buff; } else { upload_offset = 0; - bt_map->buff = dpld->dev[0]->txq.buff; + qbt_map->buff = pld->dev[0]->txq.buff; } - bt_map->frame_size = (u16 *)(dpld->dp_base + - dpctl->boot_size_offset + upload_offset); - bt_map->tag = (u16 *)(dpld->dp_base + - dpctl->boot_tag_offset + upload_offset); - bt_map->count = (u16 *)(dpld->dp_base + - dpctl->boot_count_offset + upload_offset); - + qbt_map->frame_size = (u16 *)(pld->base + + dpram->boot_size_offset + upload_offset); + qbt_map->tag = (u16 *)(pld->base + + dpram->boot_tag_offset + upload_offset); + qbt_map->count = (u16 *)(pld->base + + dpram->boot_count_offset + upload_offset); } -static int qc_prepare_download(struct dpram_link_device *dpld) +static int qc_prepare_download(struct pld_link_device *pld) { int retval = 0; int count = 0; - qc_dload_map(dpld, 0); + qc_dload_map(pld, 0); while (1) { - if (dpld->udl_check.copy_start) { - dpld->udl_check.copy_start = 0; + if (pld->qc_udl_check.copy_start) { + pld->qc_udl_check.copy_start = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 1000) { @@ -102,42 +101,42 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void _qc_do_download(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static void _qc_do_download(struct pld_link_device *pld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; - if (param->size <= dpld->dpctl->max_boot_frame_size) { - iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), - dpld->address_buffer); - memcpy(dpld->dp_base, param->addr, param->size); + if (param->size <= pld->dpram->max_boot_frame_size) { + iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), + pld->address_buffer); + memcpy(pld->base, param->addr, param->size); - iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), - dpld->address_buffer); - iowrite16(param->size, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), + pld->address_buffer); + iowrite16(param->size, pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), - dpld->address_buffer); - iowrite16(param->tag, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), + pld->address_buffer); + iowrite16(param->tag, pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), - dpld->address_buffer); - iowrite16(param->count, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), + pld->address_buffer); + iowrite16(param->count, pld->base); - dpld->send_intr(dpld, 0xDB12); + pld->send_intr(pld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int _qc_download(struct dpram_link_device *dpld, void *arg, +static int _qc_download(struct pld_link_device *pld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -153,24 +152,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, memset(img, 0, param.size); memcpy(img, param.addr, param.size); - dpld->udl_check.total_size = param.size; - dpld->udl_check.rest_size = param.size; - dpld->udl_check.send_size = 0; - dpld->udl_check.copy_complete = 0; + pld->qc_udl_check.total_size = param.size; + pld->qc_udl_check.rest_size = param.size; + pld->qc_udl_check.send_size = 0; + pld->qc_udl_check.copy_complete = 0; - dpld->udl_param.addr = img; - dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + pld->qc_udl_param.addr = img; + pld->qc_udl_param.size = pld->dpram->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - dpld->udl_param.count = 1; + pld->qc_udl_param.count = 1; else - dpld->udl_param.count = param.count; - dpld->udl_param.tag = tag; + pld->qc_udl_param.count = param.count; + pld->qc_udl_param.tag = tag; - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) + pld->qc_udl_param.size = pld->qc_udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(dpld, &dpld->udl_param); + _qc_do_download(pld, &pld->qc_udl_param); /* Wait for completion */ @@ -180,13 +179,13 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (dpld->udl_check.copy_complete) { - dpld->udl_check.copy_complete = 0; + if (pld->qc_udl_check.copy_complete) { + pld->qc_udl_check.copy_complete = 0; retval = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > cnt_limit) { @@ -201,53 +200,53 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, return retval; } -static int qc_download_bin(struct dpram_link_device *dpld, void *arg) +static int qc_download_bin(struct pld_link_device *pld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + return _qc_download(pld, arg, QC_DLOAD_TAG_BIN); } -static int qc_download_nv(struct dpram_link_device *dpld, void *arg) +static int qc_download_nv(struct pld_link_device *pld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); + return _qc_download(pld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct pld_link_device *pld = (struct pld_link_device *)data; - dpld->udl_check.send_size += dpld->udl_param.size; - dpld->udl_check.rest_size -= dpld->udl_param.size; + pld->qc_udl_check.send_size += pld->qc_udl_param.size; + pld->qc_udl_check.rest_size -= pld->qc_udl_param.size; - dpld->udl_param.addr += dpld->udl_param.size; + pld->qc_udl_param.addr += pld->qc_udl_param.size; - if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { - dpld->udl_check.copy_complete = 1; - dpld->udl_param.tag = 0; + if (pld->qc_udl_check.send_size >= pld->qc_udl_check.total_size) { + pld->qc_udl_check.copy_complete = 1; + pld->qc_udl_param.tag = 0; return; } - if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) - dpld->udl_param.size = dpld->udl_check.rest_size; + if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) + pld->qc_udl_param.size = pld->qc_udl_check.rest_size; - dpld->udl_param.count += 1; + pld->qc_udl_param.count += 1; - _qc_do_download(dpld, &dpld->udl_param); + _qc_do_download(pld, &pld->qc_udl_param); } -static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) +static void qc_dload_cmd_handler(struct pld_link_device *pld, u16 cmd) { switch (cmd) { case 0x1234: - dpld->udl_check.copy_start = 1; + pld->qc_udl_check.copy_start = 1; break; case 0xDBAB: - tasklet_schedule(&dpld->dl_tsk); + tasklet_schedule(&pld->dl_tsk); break; case 0xABCD: - mif_info("[%s] booting Start\n", dpld->ld.name); - dpld->udl_check.boot_complete = 1; + mif_info("[%s] booting Start\n", pld->ld.name); + pld->qc_udl_check.boot_complete = 1; break; default: @@ -255,22 +254,22 @@ static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) } } -static int qc_boot_start(struct dpram_link_device *dpld) +static int qc_boot_start(struct pld_link_device *pld) { u16 mask = 0; int count = 0; /* Send interrupt -> '0x4567' */ mask = 0x4567; - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); while (1) { - if (dpld->udl_check.boot_complete) { - dpld->udl_check.boot_complete = 0; + if (pld->qc_udl_check.boot_complete) { + pld->qc_udl_check.boot_complete = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -282,17 +281,17 @@ static int qc_boot_start(struct dpram_link_device *dpld) return 0; } -static int qc_boot_post_process(struct dpram_link_device *dpld) +static int qc_boot_post_process(struct pld_link_device *pld) { int count = 0; while (1) { - if (dpld->boot_start_complete) { - dpld->boot_start_complete = 0; + if (pld->boot_start_complete) { + pld->boot_start_complete = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -304,7 +303,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) return 0; } -static void qc_start_handler(struct dpram_link_device *dpld) +static void qc_start_handler(struct pld_link_device *pld) { /* * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT | @@ -312,38 +311,38 @@ static void qc_start_handler(struct dpram_link_device *dpld) */ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002); - dpld->boot_start_complete = 1; + pld->boot_start_complete = 1; /* Send INIT_END code to CP */ mif_info("send 0x%04X (INIT_END)\n", mask); - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); } -static void qc_crash_log(struct dpram_link_device *dpld) +static void qc_crash_log(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = dpld->get_rx_buff(dpld, IPC_FMT); + data = pld->get_rx_buff(pld, IPC_FMT); memcpy(buf, data, (sizeof(buf) - 1)); mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); mif_info("PHONE ERR MSG\t| %s\n", buf); } -static int _qc_data_upload(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static int _qc_data_upload(struct pld_link_device *pld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { - intval = dpld->recv_intr(dpld); + if (!gpio_get_value(pld->gpio_ipc_int2ap)) { + intval = pld->recv_intr(pld); if (intval == 0xDBAB) { break; } else { @@ -352,7 +351,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - msleep(20); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -361,43 +360,43 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), - dpld->address_buffer); - param->size = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), + pld->address_buffer); + param->size = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), - dpld->address_buffer); - param->tag = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), + pld->address_buffer); + param->tag = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), - dpld->address_buffer); - param->count = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), + pld->address_buffer); + param->count = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), - dpld->address_buffer); - memcpy(param->addr, dpld->dp_base, param->size); + iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), + pld->address_buffer); + memcpy(param->addr, pld->base, param->size); - dpld->send_intr(dpld, 0xDB12); + pld->send_intr(pld, 0xDB12); return retval; } -static int qc_uload_step1(struct dpram_link_device *dpld) +static int qc_uload_step1(struct pld_link_device *pld) { int retval = 0; int count = 0; u16 intval = 0; u16 mask = 0; - qc_dload_map(dpld, 1); + qc_dload_map(pld, 1); mif_info("+---------------------------------------------+\n"); mif_info("| UPLOAD PHONE SDRAM |\n"); mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { - intval = dpld->recv_intr(dpld); + if (!gpio_get_value(pld->gpio_ipc_int2ap)) { + intval = pld->recv_intr(pld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { break; @@ -407,11 +406,11 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - msleep(20); + usleep_range(1000, 2000); count++; if (count > 200) { - intval = dpld->recv_intr(dpld); + intval = pld->recv_intr(pld); mif_info("count %d, intr 0x%04x\n", count, intval); if (intval == 0x1234) break; @@ -420,15 +419,15 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } mask = 0xDEAD; - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); return retval; } -static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) +static int qc_uload_step2(struct pld_link_device *pld, void *arg) { int retval = 0; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -436,7 +435,7 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) return -1; } - retval = _qc_data_upload(dpld, ¶m); + retval = _qc_data_upload(pld, ¶m); if (retval < 0) { mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); return -1; @@ -446,7 +445,7 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) mif_info("param->count = %d\n", param.count); if (param.tag == 4) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("param->tag = %d\n", param.tag); } @@ -459,57 +458,57 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) return retval; } -static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, +static int qc_ioctl(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: - err = qc_prepare_download(dpld); + err = qc_prepare_download(pld); if (err < 0) mif_info("%s: ERR! prepare_download fail\n", ld->name); break; case IOCTL_DPRAM_PHONEIMG_LOAD: - err = qc_download_bin(dpld, (void *)arg); + err = qc_download_bin(pld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_bin fail\n", ld->name); break; case IOCTL_DPRAM_NVDATA_LOAD: - err = qc_download_nv(dpld, (void *)arg); + err = qc_download_nv(pld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_nv fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_BOOTSTART: - err = qc_boot_start(dpld); + err = qc_boot_start(pld); if (err < 0) { mif_info("%s: ERR! boot_start fail\n", ld->name); break; } - err = qc_boot_post_process(dpld); + err = qc_boot_post_process(pld); if (err < 0) mif_info("%s: ERR! boot_post_process fail\n", ld->name); break; case IOCTL_DPRAM_PHONE_UPLOAD_STEP1: - disable_irq_nosync(dpld->irq); - err = qc_uload_step1(dpld); + disable_irq_nosync(pld->irq); + err = qc_uload_step1(pld); if (err < 0) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("%s: ERR! upload_step1 fail\n", ld->name); } break; case IOCTL_DPRAM_PHONE_UPLOAD_STEP2: - err = qc_uload_step2(dpld, (void *)arg); + err = qc_uload_step2(pld, (void *)arg); if (err < 0) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("%s: ERR! upload_step2 fail\n", ld->name); } break; @@ -524,7 +523,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, } #endif -static struct dpram_ext_op ext_op_set[] = { +static struct pld_ext_op ext_op_set[] = { #if defined(CONFIG_CDMA_MODEM_MDM6600) [QC_MDM6600] = { .exist = 1, @@ -547,7 +546,7 @@ static struct dpram_ext_op ext_op_set[] = { #endif }; -struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) +struct pld_ext_op *pld_get_ext_op(enum modem_t modem) { if (ext_op_set[modem].exist) return &ext_op_set[modem]; diff --git a/drivers/misc/modem_if/modem_link_device_shmem.h b/drivers/misc/modem_if/modem_link_device_shmem.h new file mode 100644 index 0000000..1f33c2a --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_shmem.h @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 __MODEM_LINK_DEVICE_SHMEM_H__ +#define __MODEM_LINK_DEVICE_SHMEM_H__ + +#include "modem_utils.h" +#include "modem_link_device_memory.h" + +#define SHM_BOOT_MAGIC 0x424F4F54 +#define SHM_DUMP_MAGIC 0x44554D50 +#define SHM_IPC_MAGIC 0xAA +#define SHM_PM_MAGIC 0x5F + +#define SHM_4M_RESERVED_SZ 4056 +#define SHM_4M_FMT_TX_BUFF_SZ 4096 +#define SHM_4M_FMT_RX_BUFF_SZ 4096 +#define SHM_4M_RAW_TX_BUFF_SZ 2084864 +#define SHM_4M_RAW_RX_BUFF_SZ 2097152 + +struct shmem_4mb_phys_map { + u32 magic; + u32 access; + + u32 fmt_tx_head; + u32 fmt_tx_tail; + + u32 fmt_rx_head; + u32 fmt_rx_tail; + + u32 raw_tx_head; + u32 raw_tx_tail; + + u32 raw_rx_head; + u32 raw_rx_tail; + + u8 reserved[SHM_4M_RESERVED_SZ]; + + u8 fmt_tx_buff[SHM_4M_FMT_TX_BUFF_SZ]; + u8 fmt_rx_buff[SHM_4M_FMT_RX_BUFF_SZ]; + + u8 raw_tx_buff[SHM_4M_RAW_TX_BUFF_SZ]; + u8 raw_rx_buff[SHM_4M_RAW_RX_BUFF_SZ]; +} __packed; + +struct shmem_circ { + u32 __iomem *head; + u32 __iomem *tail; + u8 __iomem *buff; + u32 size; +}; + +struct shmem_ipc_device { + char name[16]; + int id; + + struct shmem_circ txq; + struct shmem_circ rxq; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; + + int req_ack_rcvd; +}; + +struct shmem_ipc_map { + u32 __iomem *magic; + u32 __iomem *access; + + struct shmem_ipc_device dev[MAX_SIPC5_DEV]; + + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; +}; + +struct shmem_link_device { + struct link_device ld; + + enum shmem_type type; + + /* SHMEM (SHARED MEMORY) address and size */ + u32 start; /* physical "start" address of SHMEM */ + u32 size; /* size of SHMEM */ + u8 __iomem *base; /* virtual address of the "start" */ + + /* SHMEM GPIO & IRQ */ + unsigned gpio_pda_active; + + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* IPC device map */ + struct shmem_ipc_map ipc_map; + + /* Pointers (aliases) to IPC device map */ + u32 __iomem *magic; + u32 __iomem *access; + struct shmem_ipc_device *dev[MAX_SIPC5_DEV]; + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; + + /* Wakelock for SHMEM device */ + struct wake_lock wlock; + char wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock ap_wlock; + char ap_wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock cp_wlock; + char cp_wlock_name[MIF_MAX_NAME_LEN]; + + /* for UDL */ + struct completion udl_cmpl; + struct std_dload_info dl_info; + + /* for CP crash dump */ + bool forced_cp_crash; + struct timer_list crash_ack_timer; + + /* for locking TX process */ + spinlock_t tx_lock[MAX_SIPC5_DEV]; + + /* for retransmission under SHMEM flow control after TXQ full state */ + atomic_t res_required[MAX_SIPC5_DEV]; + struct completion req_ack_cmpl[MAX_SIPC5_DEV]; + + /* for efficient RX process */ + struct tasklet_struct rx_tsk; + struct delayed_work ipc_rx_dwork; + struct delayed_work udl_rx_dwork; + struct io_device *iod[MAX_SIPC5_DEV]; + + /* for logging SHMEM status */ + struct mem_status_queue stat_list; + + /* for logging SHMEM dump */ + struct trace_data_queue trace_list; +#ifdef DEBUG_MODEM_IF + struct delayed_work dump_dwork; + char dump_path[MIF_MAX_PATH_LEN]; +#endif + + /* to hold/release "cp_wakeup" for PM (power-management) */ + struct delayed_work cp_sleep_dwork; + struct delayed_work link_off_dwork; + atomic_t ref_cnt; + spinlock_t pm_lock; +}; + +/* converts from struct link_device* to struct xxx_link_device* */ +#define to_shmem_link_device(linkdev) \ + container_of(linkdev, struct shmem_link_device, ld) + +#if 1 +#endif + +/** + * get_magic + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "magic code" field. + */ +static inline u32 get_magic(struct shmem_link_device *shmd) +{ + return ioread32(shmd->magic); +} + +/** + * get_access + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "access enable" field. + */ +static inline u32 get_access(struct shmem_link_device *shmd) +{ + return ioread32(shmd->access); +} + +/** + * set_magic + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "magic code" field + */ +static inline void set_magic(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->magic); +} + +/** + * set_access + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "access enable" field + */ +static inline void set_access(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->access); +} + +/** + * get_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.head); +} + +/** + * get_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (out) pointer in a TX queue. + * + * It is useless for an AP to read a tail pointer in a TX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_txq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.tail); +} + +/** + * get_txq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.buff; +} + +/** + * get_txq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.size; +} + +/** + * get_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in an RX queue. + * + * It is useless for an AP to read a head pointer in an RX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_rxq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.head); +} + +/** + * get_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.tail); +} + +/** + * get_rxq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.buff; +} + +/** + * get_rxq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.size; +} + +/** + * set_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->txq.head); +} + +/** + * set_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->txq.tail); +} + +/** + * set_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->rxq.head); +} + +/** + * set_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->rxq.tail); +} + +/** + * get_mask_req_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_req_ack; +} + +/** + * get_mask_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_res_ack; +} + +/** + * get_mask_send + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_send; +} + +#ifndef CONFIG_LINK_DEVICE_C2C +/** + * read_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the AP-to-CP interrupt register. + */ +static inline u16 read_int2cp(struct shmem_link_device *shmd) +{ + if (shmd->mbx2cp) + return ioread16(shmd->mbx2cp); + else + return 0; +} +#endif + +/** + * reset_txq_circ + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties a TXQ by resetting the head (in) pointer with the value in the tail + * (out) pointer. + */ +static inline void reset_txq_circ(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_txq_head(shmd, dev); + u32 tail = get_txq_tail(shmd, dev); + + mif_err("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); + + set_txq_head(shmd, dev, tail); +} + +/** + * reset_rxq_circ + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties an RXQ by resetting the tail (out) pointer with the value in the head + * (in) pointer. + */ +static inline void reset_rxq_circ(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_rxq_head(shmd, dev); + u32 tail = get_rxq_tail(shmd, dev); + + mif_err("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); + + set_rxq_tail(shmd, dev, head); +} + +/** + * ipc_active + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns whether or not IPC via the shmem_link_device instance is possible. + */ +static bool ipc_active(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + u32 magic = get_magic(shmd); + u32 access = get_access(shmd); + + /* Check link mode */ + if (unlikely(ld->mode != LINK_MODE_IPC)) { + mif_err("%s: ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); + return false; + } + + /* Check "magic code" and "access enable" values */ + if (unlikely(magic != SHM_IPC_MAGIC || access != 1)) { + mif_err("%s: ERR! magic:0x%X access:%d\n", + ld->name, CALLER, magic, access); + return false; + } + + return true; +} + +/** + * get_rxq_rcvd + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a RXQ, size of the buffer, in & out + * pointer values, size of received data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_rxq_rcvd(struct shmem_link_device *shmd, int dev, + struct mem_status *mst, struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + + circ->buff = get_rxq_buff(shmd, dev); + circ->qsize = get_rxq_buff_size(shmd, dev); + circ->in = mst->head[dev][RX]; + circ->out = mst->tail[dev][RX]; + circ->size = circ_get_usage(circ->qsize, circ->in, circ->out); + + if (circ_valid(circ->qsize, circ->in, circ->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out, circ->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out); + return -EIO; + } +} + +/** + * get_txq_space + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of free space} into the 'circ' instance. + * + * Returns the size of free space in the buffer or an error code. + */ +static int get_txq_space(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int space; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + space = circ_get_space(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = space; + + return space; +} + +/** + * get_txq_saved + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of stored data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_txq_saved(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int saved; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + saved = circ_get_usage(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u saved:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, saved); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "saved:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + saved, cnt); + if (cnt >= MAX_RETRY_CNT) { + saved = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = saved; + + return saved; +} + +/** + * clear_shmem_map + * @shmd: pointer to an instance of shmem_link_device structure + * + * Clears all pointers in every circular queue. + */ +static void clear_shmem_map(struct shmem_link_device *shmd) +{ + set_txq_head(shmd, IPC_FMT, 0); + set_txq_tail(shmd, IPC_FMT, 0); + set_rxq_head(shmd, IPC_FMT, 0); + set_rxq_tail(shmd, IPC_FMT, 0); + + set_txq_head(shmd, IPC_RAW, 0); + set_txq_tail(shmd, IPC_RAW, 0); + set_rxq_head(shmd, IPC_RAW, 0); + set_rxq_tail(shmd, IPC_RAW, 0); +} + +/** + * reset_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Reset SHMEM with IPC map. + */ +static void reset_shmem_ipc(struct shmem_link_device *shmd) +{ + set_access(shmd, 0); + + clear_shmem_map(shmd); + + atomic_set(&shmd->res_required[IPC_FMT], 0); + atomic_set(&shmd->res_required[IPC_RAW], 0); + + atomic_set(&shmd->ref_cnt, 0); + + set_magic(shmd, SHM_IPC_MAGIC); + set_access(shmd, 1); +} + +/** + * init_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Initializes IPC via SHMEM. + */ +static int init_shmem_ipc(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode == LINK_MODE_IPC && + get_magic(shmd) == SHM_IPC_MAGIC && + get_access(shmd) == 1) { + mif_err("%s: IPC already initialized\n", ld->name); + return 0; + } + + /* Initialize variables for efficient TX/RX processing */ + shmd->iod[IPC_FMT] = link_get_iod_with_format(ld, IPC_FMT); + shmd->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + + reset_shmem_ipc(shmd); + + if (get_magic(shmd) != SHM_IPC_MAGIC || get_access(shmd) != 1) + return -EACCES; + + return 0; +} + +#endif + diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c index c4715e0..ece6b65 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.c +++ b/drivers/misc/modem_if/modem_link_device_spi.c @@ -26,9 +26,10 @@ #include #include #include +#include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_spi.h" #include "modem_utils.h" @@ -66,8 +67,7 @@ static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld) if (!spild->boot_done) return result; - if (!wake_lock_active(&spild->spi_wake_lock) - && spild->send_modem_spi != 1) { + if (!wake_lock_active(&spild->spi_wake_lock)) { wake_lock(&spild->spi_wake_lock); pr_debug("[SPI] [%s](%d) spi_wakelock locked . spild->spi_state[%d]\n", __func__, __LINE__, (int)spild->spi_state); @@ -311,7 +311,7 @@ static void spi_prepare_tx_packet(void) ld = &p_spild->ld; - for (i = 0; i < p_spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { while ((skb = skb_dequeue(ld->skb_txq[i]))) { ret = spi_buff_write(p_spild, i, skb->data, skb->len); if (!ret) { @@ -331,7 +331,7 @@ static void spi_start_data_send(void) ld = &p_spild->ld; - for (i = 0; i < p_spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { if (skb_queue_len(ld->skb_txq[i]) > 0) spi_send_work(SPI_WORK_SEND, SPI_WORK); } @@ -345,6 +345,14 @@ static void spi_tx_work(void) char *spi_sync_buf; spild = p_spild; + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + + if (iod->mc->phone_state == STATE_CRASH_EXIT) + return; /* check SUB SRDY, SRDY state */ if (gpio_get_value(spild->gpio_ipc_sub_srdy) == @@ -420,8 +428,6 @@ static void spi_tx_work(void) pr_err("[SPI] spi_dev_send fail\n"); /* add cp reset when spi sync fail */ - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (iod) iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -553,6 +559,15 @@ static void spi_rx_work(void) if (!spild) pr_err("[LNK/E] <%s> dpld == NULL\n", __func__); + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + + if (iod->mc->phone_state == STATE_CRASH_EXIT) + return; + if (!wake_lock_active(&spild->spi_wake_lock) || gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW || get_console_suspended() || @@ -606,7 +621,7 @@ static void spi_rx_work(void) /* parsing SPI packet */ if (spi_buff_read(spild) > 0) { /* call function for send data to IPC, RAW, RFS */ - for (i = 0; i < spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { iod = spild->iod[i]; while ((skb = skb_dequeue(&spild->skb_rxq[i])) != NULL) { @@ -623,8 +638,6 @@ static void spi_rx_work(void) "spi sync failed"); /* add cp reset when spi sync fail */ - iod = link_get_iod_with_format(&spild->ld, IPC_FMT); - if (iod) iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -639,6 +652,34 @@ static void spi_rx_work(void) spi_start_data_send(); } + +static int spi_init_ipc(struct spi_link_device *spild) +{ + struct link_device *ld = &spild->ld; + + int i; + + /* Make aliases to each IO device */ + for (i = 0; i < MAX_DEV_FORMAT; i++) + spild->iod[i] = link_get_iod_with_format(ld, i); + + spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; + + /* List up the IO devices connected to each IPC channel */ + for (i = 0; i < MAX_DEV_FORMAT; i++) { + if (spild->iod[i]) + pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", + __func__, ld->name, i, spild->iod[i]->name); + else + pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", + __func__, ld->name, i); + } + + ld->mode = LINK_MODE_IPC; + + return 0; +} + unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len) { unsigned int i; @@ -1239,10 +1280,19 @@ err3: static void spi_send_modem_bin(struct work_struct *send_modem_w) { + struct spi_link_device *spild; + struct io_device *iod; int retval; struct image_buf img; unsigned long tick1, tick2 = 0; + spild = p_spild; + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + tick1 = jiffies_to_msecs(jiffies); retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img); @@ -1285,16 +1335,25 @@ static void spi_send_modem_bin(struct work_struct *send_modem_w) tick2 = jiffies_to_msecs(jiffies); pr_info("Downloading takes %lu msec\n", (tick2-tick1)); - complete_all(&p_spild->ril_init); + spi_init_ipc(p_spild); + sprd_boot_done = 1; p_spild->ril_send_cnt = 0; + p_spild->spi_state = SPI_STATE_IDLE; + if (iod) + iod->modem_state_changed(iod, + STATE_ONLINE); + return; err: + if (iod) + iod->modem_state_changed(iod, + STATE_OFFLINE); return; } -static inline int _request_mem(struct ipc_spi *od) +static inline int _request_mem(struct spi_v_buff *od) { if (!p_spild->p_virtual_buff) { od->mmio = vmalloc(od->size); @@ -1320,7 +1379,7 @@ void spi_tx_timer_callback(unsigned long param) { if (p_spild->spi_state == SPI_STATE_TX_WAIT) { p_spild->spi_timer_tx_state = SPI_STATE_TIME_OVER; - pr_err("[SPI] spi_tx_timer_callback -timer expires\n"); + pr_debug("[SPI] spi_tx_timer_callback -timer expires\n"); } } @@ -1328,7 +1387,7 @@ void spi_rx_timer_callback(unsigned long param) { if (p_spild->spi_state == SPI_STATE_RX_WAIT) { p_spild->spi_timer_rx_state = SPI_STATE_TIME_OVER; - pr_err("[SPI] spi_rx_timer_callback -timer expires\n"); + pr_debug("[SPI] spi_rx_timer_callback -timer expires\n"); } } @@ -1381,96 +1440,37 @@ static void spi_work(struct work_struct *work) } } -static int spi_init_ipc(struct spi_link_device *spild) +static int link_pm_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) { - struct link_device *ld = &spild->ld; - - int i; - - /* Make aliases to each IO device */ - for (i = 0; i < MAX_DEV_FORMAT; i++) - spild->iod[i] = link_get_iod_with_format(ld, i); - - spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; - - /* List up the IO devices connected to each IPC channel */ - for (i = 0; i < MAX_DEV_FORMAT; i++) { - if (spild->iod[i]) - pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", - __func__, ld->name, i, spild->iod[i]->name); - else - pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", - __func__, ld->name, i); - } - - ld->mode = LINK_MODE_IPC; - - return 0; -} - -static int spi_thread(void *data) -{ - struct spi_link_device *spild = (struct spi_link_device *)data; + struct io_device *iod; + struct link_pm_data *pm_data = + container_of(this, struct link_pm_data, pm_notifier); - if (lpcharge == 1) { - pr_err("[LPM MODE] spi_thread_exit!\n"); - return 0; + iod = link_get_iod_with_format(&pm_data->spild->ld, IPC_FMT); + if (!iod) { + pr_err("no iodevice for modem control\n"); + return NOTIFY_BAD; } - daemonize("spi_thread"); - - pr_info("[%s] spi_thread start.\n", __func__); - p_spild->boot_done = 1; - - wait_for_completion(&p_spild->ril_init); - - pr_info("[%s] ril_init completed.\n", __func__); + if (!gpio_get_value(iod->mc->gpio_phone_active)) + return NOTIFY_DONE; - pr_info("<%s> wait 2 sec... srdy : %d\n", - __func__, gpio_get_value(spild->gpio_ipc_srdy)); - msleep_interruptible(1700); + switch (event) { + case PM_SUSPEND_PREPARE: + /* set TD PDA Active High if previous state was LPA */ + mif_info("TD PDA active low to LPA suspend spot\n"); + gpio_set_value(iod->mc->gpio_pda_active, 0); - while (gpio_get_value(spild->gpio_ipc_srdy)) - ; - pr_info("(%s) cp booting... Done.\n", __func__); - - spi_init_ipc(spild); - - pr_info("[spi_thread] Start IPC Communication. SRDY : %d\n", - gpio_get_value(spild->gpio_ipc_srdy)); - - /* CP booting is already completed, just set submrdy to high */ - if (gpio_get_value(spild->gpio_ipc_sub_srdy) == SPI_GPIOLEVEL_HIGH) { - gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); - pr_err("[spi_thread] CP booting is already completed\n"); + return NOTIFY_OK; + case PM_POST_SUSPEND: + /* LPA to Kernel suspend and User Freezing task fail resume, + restore to LPA GPIO states. */ + mif_info("TD PDA active High to LPA GPIO state\n"); + gpio_set_value(iod->mc->gpio_pda_active, 1); + return NOTIFY_OK; } - /* CP booting is not completed. - set submrdy to high and wait until subsrdy is high */ - else { - pr_err("[spi_thread] CP booting is not completed. wait...\n"); - - gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); - do { - msleep_interruptible(5); - } while (gpio_get_value(spild->gpio_ipc_sub_srdy) == - SPI_GPIOLEVEL_LOW); - - pr_err("[spi_thread] CP booting is done...\n"); - } - - if (p_spild->spi_is_restart) - msleep_interruptible(100); - else - msleep_interruptible(30); - - gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); - - pr_info("(%s) spi sync done.\n", __func__); - - spild->spi_state = SPI_STATE_IDLE; - p_spild->spi_is_restart = 0; - - return 0; + return NOTIFY_DONE; } static int spi_probe(struct spi_device *spi) @@ -1512,8 +1512,7 @@ static struct spi_driver spi_driver = { static int spi_link_init(void) { int ret; - struct ipc_spi *od; - struct task_struct *th; + struct spi_v_buff *od; struct link_device *ld = &p_spild->ld; p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy; @@ -1522,7 +1521,7 @@ static int spi_link_init(void) p_spild->gpio_ipc_mrdy, p_spild->gpio_modem_bin_srdy, gpio_get_value(p_spild->gpio_ipc_srdy)); - od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL); + od = kzalloc(sizeof(struct spi_v_buff), GFP_KERNEL); if (!od) { pr_err("(%d) failed to allocate device\n", __LINE__); ret = -ENOMEM; @@ -1537,7 +1536,6 @@ static int spi_link_init(void) if (ret) goto err; - init_completion(&p_spild->ril_init); sema_init(&p_spild->srdy_sem, 0); INIT_WORK(&p_spild->send_modem_w, @@ -1563,12 +1561,7 @@ static int spi_link_init(void) if (ret) goto err; - th = kthread_create(spi_thread, (void *)p_spild, "spi_thread"); - if (IS_ERR(th)) { - pr_err("kernel_thread() failed\n"); - goto err; - } - wake_up_process(th); + p_spild->boot_done = 1; pr_info("[%s] Done\n", __func__); return 0; @@ -1591,7 +1584,6 @@ void spi_set_restart(void) gpio_set_value(p_spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); p_spild->spi_state = SPI_STATE_END; - p_spild->spi_is_restart = 1; /* Flush SPI work queue */ flush_workqueue(p_spild->spi_wq); @@ -1635,6 +1627,35 @@ exit: } EXPORT_SYMBOL(spi_thread_restart); +static int spi_link_pm_init(struct spi_link_device *spild, + struct platform_device *pdev) +{ + struct modem_data *pdata = + (struct modem_data *)pdev->dev.platform_data; + struct modemlink_pm_data *pm_pdata; + struct link_pm_data *pm_data = + kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); + + if (!pdata || !pdata->link_pm_data) { + mif_err("platform data is NULL\n"); + return -EINVAL; + } + pm_pdata = pdata->link_pm_data; + + if (!pm_data) { + mif_err("link_pm_data is NULL\n"); + return -ENOMEM; + } + + pm_data->spild = spild; + spild->link_pm_data = pm_data; + + pm_data->pm_notifier.notifier_call = link_pm_notifier_event; + register_pm_notifier(&pm_data->pm_notifier); + + return 0; +} + struct link_device *spi_create_link_device(struct platform_device *pdev) { struct spi_link_device *spild = NULL; @@ -1689,9 +1710,9 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) } spild->spi_state = SPI_STATE_END; - spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */ + ld->max_ipc_dev = (IPC_RFS + 1); /* FMT, RAW, RFS */ - for (i = 0; i < spild->max_ipc_dev; i++) + for (i = 0; i < ld->max_ipc_dev; i++) skb_queue_head_init(&spild->skb_rxq[i]); /* Prepare a clean buffer for SPI access */ @@ -1738,6 +1759,11 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) goto err; } + /* create link pm device */ + ret = spi_link_pm_init(spild, pdev); + if (ret) + goto err; + /* Create SPI device */ ret = spi_link_init(); if (ret) diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h index 210d815..b1b334f 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.h +++ b/drivers/misc/modem_if/modem_link_device_spi.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include "modem.h" #define SPI_TIMER_TX_WAIT_TIME 60 /* ms */ @@ -135,20 +135,24 @@ struct spi_data_packet_header { unsigned long more:1; }; +struct link_pm_data { + struct miscdevice miscdev; + struct spi_link_device *spild; + + struct notifier_block pm_notifier; +}; + struct spi_link_device { struct link_device ld; - /* Link to SPI control functions dependent on each platform */ - int max_ipc_dev; - /* Wakelock for SPI device */ struct wake_lock spi_wake_lock; + /* Workqueue for modem bin transfers */ struct workqueue_struct *ipc_spi_wq; /* SPI state */ int spi_state; - int spi_is_restart; /* SPI Timer state */ int spi_timer_tx_state; @@ -182,11 +186,13 @@ struct spi_link_device { struct io_device *iod[MAX_DEV_FORMAT]; struct sk_buff_head skb_rxq[MAX_DEV_FORMAT]; + /* LINK PM DEVICE DATA */ + struct link_pm_data *link_pm_data; + /* Multi-purpose miscellaneous buffer */ u8 *buff; u8 *sync_buff; - struct completion ril_init; struct semaphore srdy_sem; int send_modem_spi; @@ -202,6 +208,12 @@ struct spi_link_device { #define to_spi_link_device(linkdev) \ container_of(linkdev, struct spi_link_device, ld) +struct spi_v_buff { + unsigned long base; + unsigned long size; + void __iomem *mmio; +}; + extern unsigned int lpcharge; extern int get_console_suspended(void); static void spi_work(struct work_struct *work); diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c index 14aee9f..9e6cbea 100644 --- a/drivers/misc/modem_if/modem_link_device_usb.c +++ b/drivers/misc/modem_if/modem_link_device_usb.c @@ -13,7 +13,7 @@ * */ -/* #define DEBUG */ +#define DEBUG #include #include @@ -26,7 +26,7 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_usb.h" #include "modem_utils.h" @@ -297,10 +297,15 @@ static void usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state) { struct io_device *iod; + struct io_device *bootd; iod = link_get_iod_with_format(&usb_ld->ld, IPC_FMT); if (iod) iod->modem_state_changed(iod, state); + + bootd = usb_ld->ld.mc->bootd; + if (bootd) + bootd->modem_state_changed(bootd, state); } static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld, @@ -624,7 +629,12 @@ static void if_usb_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work); usb_put_dev(usbdev); usb_ld->usbdev = NULL; - pm_runtime_forbid(pm_data->root_hub); + if (!has_hub(usb_ld)) { + if (pm_data->root_hub) + pm_runtime_forbid(pm_data->root_hub); + schedule_delayed_work(&usb_ld->wait_enumeration, + WAIT_ENUMURATION_TIMEOUT_JIFFIES); + } } } diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c index 75ad970..c63f08e 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.c +++ b/drivers/misc/modem_if/modem_link_pm_usb.c @@ -12,7 +12,7 @@ * */ -/* #define DEBUG */ +#define DEBUG #include #include @@ -27,12 +27,15 @@ #include "modem_link_pm_usb.h" +int during_hub_resume; + static inline void start_hub_work(struct link_pm_data *pm_data, int delay) { if (pm_data->hub_work_running == false) { pm_data->hub_work_running = true; wake_lock(&pm_data->hub_lock); mif_debug("link_pm_hub_work is started\n"); + during_hub_resume = 1; } schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay)); @@ -81,12 +84,13 @@ void link_pm_preactive(struct link_pm_data *pm_data) static void link_pm_hub_work(struct work_struct *work) { - int err; + int err, cnt; struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_hub.work); if (pm_data->hub_status == HUB_STATE_ACTIVE) { end_hub_work(pm_data); + during_hub_resume = 0; return; } @@ -111,7 +115,16 @@ static void link_pm_hub_work(struct work_struct *work) /* skip 1st time before first probe */ if (pm_data->root_hub) pm_runtime_get_sync(pm_data->root_hub); - err = pm_data->port_enable(2, 1); + + for (cnt=0;cnt<5;cnt++) { + err = pm_data->port_enable(2, 1); + if (err >= 0) { + mif_err("hub on success\n"); + break; + } + mif_err("hub on fail %d th\n", cnt); + msleep(100); + } if (err < 0) { mif_err("hub on fail err=%d\n", err); err = pm_data->port_enable(2, 0); @@ -132,6 +145,8 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); + + mif_err("USB Hub resume fail !!!\n"); end_hub_work(pm_data); } else { mif_info("hub resumming: %d\n", @@ -164,9 +179,6 @@ static int link_pm_hub_standby(void *args) /* this function is atomic. * make force disconnect in workqueue.. */ - if (pm_data->usb_ld->if_usb_connected) - schedule_work(&usb_ld->disconnect_work); - return err; } @@ -210,6 +222,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, sizeof(int))) return -EFAULT; gpio_set_value(pm_data->gpio_link_active, value); + mif_info("> H-ACT %d\n", value); break; case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); @@ -233,7 +246,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, case IOCTL_LINK_PORT_OFF: err = link_pm_hub_standby(pm_data); if (err < 0) { - mif_err("usb3503 active fail\n"); + mif_err("usb3503 standby fail\n"); goto exit; } pm_data->hub_init_lock = 1; @@ -363,6 +376,8 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->miscdev.name = "link_pm"; pm_data->miscdev.fops = &link_pm_fops; + during_hub_resume = 0; + err = misc_register(&pm_data->miscdev); if (err < 0) { mif_err("fail to register pm device(%d)\n", err); diff --git a/drivers/misc/modem_if/modem_link_pm_usb.h b/drivers/misc/modem_if/modem_link_pm_usb.h index d26af76..5fc762d 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.h +++ b/drivers/misc/modem_if/modem_link_pm_usb.h @@ -15,7 +15,7 @@ #ifndef __MODEM_LINK_PM_USB_H__ #define __MODEM_LINK_PM_USB_H__ -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_usb.h" diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp71.c b/drivers/misc/modem_if/modem_modemctl_device_cbp71.c index 28f2ce7..5a4db56 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp71.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp71.c @@ -23,7 +23,7 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_dpram.h" diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c index 2617be8..d8f2af9 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_cbp7.1.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -24,14 +22,13 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_dpram.h" #define PIF_TIMEOUT (180 * HZ) #define DPRAM_INIT_TIMEOUT (30 * HZ) - static irqreturn_t phone_active_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; @@ -163,13 +160,13 @@ static int cbp72_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - struct dpram_link_device *dpld = to_dpram_link_device(ld); + mif_debug("\n"); /* Wait here until the PHONE is up. * Waiting as the this called from IOCTL->UM thread */ mif_info("Waiting for INT_CMD_PHONE_START\n"); - ret = wait_for_completion_interruptible_timeout( - &dpld->dpram_init_cmd, DPRAM_INIT_TIMEOUT); + ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, + DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ mif_err("Timeout!!! (PHONE_START was not arrived.)\n"); @@ -177,8 +174,8 @@ static int cbp72_boot_off(struct modem_ctl *mc) } mif_info("Waiting for INT_CMD_PIF_INIT_DONE\n"); - ret = wait_for_completion_interruptible_timeout( - &dpld->modem_pif_init_done, PIF_TIMEOUT); + ret = wait_for_completion_interruptible_timeout(&ld->pif_cmpl, + PIF_TIMEOUT); if (!ret) { mif_err("Timeout!!! (PIF_INIT_DONE was not arrived.)\n"); return -ENXIO; @@ -199,10 +196,6 @@ static int cbp72_force_crash_exit(struct modem_ctl *mc) /* Make DUMP start */ ld->force_dump(ld, mc->bootd); - msleep_interruptible(1000); - - mc->bootd->modem_state_changed(mc->bootd, STATE_CRASH_EXIT); - return 0; } @@ -221,16 +214,15 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - struct platform_device *pdev = NULL; - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_off = pdata->gpio_cp_off; - mc->gpio_reset_req_n = pdata->gpio_reset_req_n; - mc->gpio_cp_reset = pdata->gpio_cp_reset; - mc->gpio_pda_active = pdata->gpio_pda_active; - mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; - mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_reset_req_n = pdata->gpio_reset_req_n; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset; if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { @@ -245,10 +237,9 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cbp72_get_ops(mc); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; if (!mc->irq_phone_active) { - mif_err("get irq fail\n"); + mif_err("get irq_phone_active fail\n"); return -1; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp82.c b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c new file mode 100644 index 0000000..dc5799e --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" + +#define DPRAM_INIT_TIMEOUT (30 * HZ) +#define PIF_TIMEOUT (180 * HZ) + +static irqreturn_t phone_active_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int old_state = mc->phone_state; + int new_state = mc->phone_state; + + mif_info("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(old_state), cp_on, cp_reset, cp_active); + + if (cp_reset && cp_active) { + if (mc->phone_state == STATE_BOOTING) { + new_state = STATE_ONLINE; + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + } else if (cp_reset && !cp_active) { + if (mc->phone_state == STATE_ONLINE) { + new_state = STATE_CRASH_EXIT; + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + } else { + new_state = STATE_OFFLINE; + if (mc->bootd && mc->bootd->modem_state_changed) + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + + if (new_state != old_state) { + mif_err("%s: phone_state changed (%s -> %s\n)", + mc->name, get_cp_state_str(old_state), + get_cp_state_str(new_state)); + } + + return IRQ_HANDLED; +} + +static int cbp82_on(struct modem_ctl *mc) +{ + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_off = gpio_get_value(mc->gpio_cp_off); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("+++\n"); + + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + /* prevent sleep during bootloader downloading */ + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_cp_off, 1); + gpio_set_value(mc->gpio_cp_reset, 0); + + msleep(500); + + cp_on = gpio_get_value(mc->gpio_cp_on); + cp_off = gpio_get_value(mc->gpio_cp_off); + cp_reset = gpio_get_value(mc->gpio_cp_reset); + cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + gpio_set_value(mc->gpio_cp_off, 0); + gpio_set_value(mc->gpio_cp_on, 1); + + msleep(100); + + gpio_set_value(mc->gpio_cp_reset, 1); + + msleep(300); + + cp_on = gpio_get_value(mc->gpio_cp_on); + cp_off = gpio_get_value(mc->gpio_cp_off); + cp_reset = gpio_get_value(mc->gpio_cp_reset); + cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + if (mc->gpio_pda_active) + gpio_set_value(mc->gpio_pda_active, 1); + + if (mc->bootd) + mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); + else + mif_err("no mc->bootd\n"); + + mif_err("---\n"); + return 0; +} + +static int cbp82_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_err("+++\n"); + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_cp_off, 1); + + mc->bootd->modem_state_changed(mc->bootd, STATE_OFFLINE); + ld->mode = LINK_MODE_OFFLINE; + + mif_err("---\n"); + return 0; +} + +static int cbp82_reset(struct modem_ctl *mc) +{ + int ret = 0; + + mif_debug("cbp82_reset()\n"); + + ret = cbp82_off(mc); + if (ret) + return -ENXIO; + + msleep(100); + + ret = cbp82_on(mc); + if (ret) + return -ENXIO; + + return 0; +} + +static int cbp82_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_info("+++\n"); + + ld->mode = LINK_MODE_BOOT; + + mif_info("---\n"); + return 0; +} + +static int cbp82_boot_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + int ret; + mif_err("+++\n"); + + /* Wait here until the PHONE is up. + * Waiting as the this called from IOCTL->UM thread */ + mif_err("Waiting for PHONE_START\n"); + ret = wait_for_completion_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); + if (!ret) { + /* ret == 0 on timeout */ + mif_err("T-I-M-E-O-U-T (PHONE_START)\n"); + cbp82_off(mc); + ret = -EIO; + goto exit; + } + mif_err("recv PHONE_START\n"); + + mif_err("Waiting for PIF_INIT_DONE\n"); + ret = wait_for_completion_timeout(&ld->pif_cmpl, PIF_TIMEOUT); + if (!ret) { + /* ret == 0 on timeout */ + mif_err("T-I-M-E-O-U-T (PIF_INIT_DONE)!!!\n"); + cbp82_off(mc); + ret = -EIO; + goto exit; + } + mif_err("recv PIF_INIT_DONE\n"); + + mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE); + ret = 0; + +exit: + wake_unlock(&mc->mc_wake_lock); + mif_err("---\n"); + return ret; +} + +static int cbp82_force_crash_exit(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + + mif_err("device = %s\n", mc->bootd->name); + + /* Make DUMP start */ + ld->force_dump(ld, mc->bootd); + + return 0; +} + +static void cbp82_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = cbp82_on; + mc->ops.modem_off = cbp82_off; + mc->ops.modem_reset = cbp82_reset; + mc->ops.modem_boot_on = cbp82_boot_on; + mc->ops.modem_boot_off = cbp82_boot_off; + mc->ops.modem_force_crash_exit = cbp82_force_crash_exit; +} + +int cbp82_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + int irq = 0; + unsigned long flag = 0; + mif_err("+++\n"); + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_phone_active = pdata->gpio_phone_active; + + if (!mc->gpio_cp_on || !mc->gpio_cp_off || !mc->gpio_cp_reset + || !mc->gpio_phone_active) { + mif_err("no GPIO data\n"); + mif_err("---\n"); + return -ENXIO; + } + + mc->gpio_pda_active = pdata->gpio_pda_active; + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_off, 1); + gpio_set_value(mc->gpio_cp_on, 0); + + cbp82_get_ops(mc); + + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp82_wake_lock"); + + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("get irq fail\n"); + mif_err("---\n"); + return -1; + } + mif_info("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); + + irq = mc->irq_phone_active; + flag = IRQF_NO_SUSPEND | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + ret = request_irq(irq, phone_active_handler, flag, "cdma_active", mc); + if (ret) { + mif_err("request_irq fail (%d)\n", ret); + mif_err("---\n"); + return ret; + } + + ret = enable_irq_wake(irq); + if (ret) + mif_err("enable_irq_wake fail (%d)\n", ret); + + mif_err("---\n"); + return 0; +} + diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c index fe8ce69..a960edb 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_cmc221.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -13,15 +11,15 @@ * GNU General Public License for more details. * */ -#include +#include #include #include #include #include #include +#include "modem.h" -#include #include "modem_prj.h" #include "modem_link_device_usb.h" #include "modem_link_device_dpram.h" @@ -77,7 +75,7 @@ static irqreturn_t phone_active_handler(int irq, void *arg) static irqreturn_t dynamic_switching_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; - int txpath = gpio_get_value(mc->gpio_dynamic_switching); + int txpath = gpio_get_value(mc->gpio_link_switch); bool enumerated = usb_is_enumerated(mc->msd); mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated); @@ -94,6 +92,52 @@ static irqreturn_t dynamic_switching_handler(int irq, void *arg) return IRQ_HANDLED; } +#ifdef CONFIG_EXYNOS4_CPUFREQ /* Set cpu clock to 800MHz for high TP */ +static void cmc221_cpufreq_lock(struct work_struct *work) +{ + struct modem_ctl *mc; + + mc = container_of(work, struct modem_ctl, work_cpu_lock.work); + if (mc->mdm_data->link_pm_data->freq_lock) { + mif_debug("Call freq lock func.\n"); + mc->mdm_data->link_pm_data->freq_lock(mc->dev); + + cancel_delayed_work(&mc->work_cpu_unlock); + schedule_delayed_work(&mc->work_cpu_unlock, + msecs_to_jiffies(5000)); + } +} + +static void cmc221_cpufreq_unlock(struct work_struct *work) +{ + struct modem_ctl *mc; + int tp_level; + + mc = container_of(work, struct modem_ctl, work_cpu_unlock.work); + tp_level = gpio_get_value(mc->gpio_cpufreq_lock); + + mif_debug("TP Level is (%d)\n", tp_level); + if (tp_level) { + mif_debug("maintain cpufreq lock !!!\n"); + schedule_delayed_work(&mc->work_cpu_unlock, + msecs_to_jiffies(5000)); + } else { + if (mc->mdm_data->link_pm_data->freq_unlock) { + mif_debug("Call freq unlock func.\n"); + mc->mdm_data->link_pm_data->freq_unlock(mc->dev); + } + } +} + +static irqreturn_t cpufreq_lock_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + + schedule_delayed_work(&mc->work_cpu_lock, 0); + return IRQ_HANDLED; +} +#endif + static int cmc221_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); @@ -204,11 +248,10 @@ static int cmc221_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - struct dpram_link_device *dpld = to_dpram_link_device(ld); mif_err("%s\n", mc->name); - ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd, + ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ @@ -249,22 +292,23 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - struct platform_device *pdev = NULL; - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_reset = pdata->gpio_cp_reset; mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_pda_active = pdata->gpio_pda_active; #if 0 /*TODO: check the GPIO map*/ - mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup; - mc->gpio_host_active = pdata->gpio_host_active; - mc->gpio_host_wakeup = pdata->gpio_host_wakeup; + mc->gpio_host_active = pdata->gpio_host_active; + mc->gpio_host_wakeup = pdata->gpio_host_wakeup; #endif - mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching; + mc->gpio_link_switch = pdata->gpio_link_switch; mc->need_switch_to_usb = false; - +#ifdef CONFIG_EXYNOS4_CPUFREQ + mc->gpio_cpufreq_lock = pdata->gpio_cpufreq_lock; +#endif if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { mif_err("%s: ERR! no GPIO data\n", mc->name); return -ENXIO; @@ -276,10 +320,9 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cmc221_get_ops(mc); dev_set_drvdata(mc->dev, mc); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; if (!mc->irq_phone_active) { - mif_err("%s: ERR! get cp_active_irq fail\n", mc->name); + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); return -1; } mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active); @@ -301,8 +344,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } flag = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND; - if (mc->gpio_dynamic_switching) { - irq = gpio_to_irq(mc->gpio_dynamic_switching); + if (mc->gpio_link_switch) { + irq = gpio_to_irq(mc->gpio_link_switch); mif_err("%s: DYNAMIC_SWITCH IRQ# = %d\n", mc->name, irq); ret = request_irq(irq, dynamic_switching_handler, flag, "dynamic_switching", mc); @@ -313,5 +356,23 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } } +#ifdef CONFIG_EXYNOS4_CPUFREQ + INIT_DELAYED_WORK(&mc->work_cpu_lock, cmc221_cpufreq_lock); + INIT_DELAYED_WORK(&mc->work_cpu_unlock, cmc221_cpufreq_unlock); + + flag = IRQF_TRIGGER_RISING; + if (mc->gpio_cpufreq_lock) { + irq = gpio_to_irq(mc->gpio_cpufreq_lock); + mif_err("%s: CPUFREQ_LOCK_CNT IRQ# = %d\n", mc->name, irq); + ret = request_irq(irq, cpufreq_lock_handler, flag, + "cpufreq_lock", mc); + if (ret) { + mif_err("%s: ERR! request_irq(#%d) fail (err %d)\n", + mc->name, irq, ret); + return ret; + } + } +#endif + return 0; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c index 5a42755..79cdd95 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c +++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c @@ -24,7 +24,7 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include @@ -115,6 +115,13 @@ static int esc6270_reset(struct modem_ctl *mc) int esc6270_boot_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); +#if defined(CONFIG_LINK_DEVICE_DPRAM) + /* clear intr */ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u16 recv_msg = dpld->recv_intr(dpld); + + pr_info("[MODEM_IF:ESC] dpram intr: %x\n", recv_msg); +#endif pr_info("[MODEM_IF:ESC] <%s>\n", __func__); @@ -273,8 +280,11 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -302,7 +312,7 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + mc->irq_sim_detect = pdata->irq_sim_detect; pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c index ad44579..38bef9a 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c +++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c @@ -24,12 +24,14 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include #include +#include "modem_link_device_pld.h" + #if defined(CONFIG_MACH_M0_CTC) #include #endif @@ -39,6 +41,8 @@ static int mdm6600_on(struct modem_ctl *mc) { + struct link_device *ld = get_current_link(mc->iod); + pr_info("[MODEM_IF] mdm6600_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_reset_msm || !mc->gpio_cp_on) { @@ -58,6 +62,7 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_pda_active, 1); mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; return 0; } @@ -82,7 +87,8 @@ static int mdm6600_off(struct modem_ctl *mc) static int mdm6600_reset(struct modem_ctl *mc) { - int ret; + struct link_device *ld = get_current_link(mc->iod); + /* int ret; */ pr_info("[MODEM_IF] mdm6600_reset()\n"); @@ -109,6 +115,9 @@ static int mdm6600_reset(struct modem_ctl *mc) msleep(40); /* > 37.2 + 2 msec */ } + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; + return 0; } @@ -159,6 +168,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int cp_dump_value = 0; int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; + struct link_device *ld; if (!mc->gpio_cp_reset || !mc->gpio_phone_active /*|| !mc->gpio_cp_dump_int */) { @@ -179,11 +189,6 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) } else if (phone_reset && !phone_active_value) { if (count == 1) { phone_state = STATE_CRASH_EXIT; - if (mc->iod) { - ld = get_current_link(mc->iod); - if (ld->terminate_comm) - ld->terminate_comm(ld, mc->iod); - } if (mc->iod && mc->iod->modem_state_changed) mc->iod->modem_state_changed (mc->iod, phone_state); @@ -226,8 +231,11 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->vbus_on = pdata->vbus_on; mc->vbus_off = pdata->vbus_off; - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MODEM_IF] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -332,7 +340,7 @@ static int mdm6600_on(struct modem_ctl *mc) return -ENXIO; } - gpio_set_value(mc->gpio_pda_active, 0); + gpio_set_value(mc->gpio_pda_active, 1); gpio_set_value(mc->gpio_cp_on, 1); msleep(500); @@ -346,8 +354,6 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_on, 0); msleep(500); - gpio_set_value(mc->gpio_pda_active, 1); - #if defined(CONFIG_LINK_DEVICE_PLD) gpio_set_value(mc->gpio_fpga_cs_n, 1); #endif @@ -420,9 +426,13 @@ static int mdm6600_reset(struct modem_ctl *mc) static int mdm6600_boot_on(struct modem_ctl *mc) { struct regulator *regulator; + struct link_device *ld = get_current_link(mc->iod); + struct pld_link_device *dpld = to_pld_link_device(ld); pr_info("[MSM] <%s>\n", __func__); + dpld->recv_intr(dpld); + if (!mc->gpio_flm_uart_sel) { pr_err("[MSM] no gpio data\n"); return -ENXIO; @@ -729,8 +739,11 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MSM] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -754,7 +767,7 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + mc->irq_sim_detect = pdata->irq_sim_detect; pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c new file mode 100644 index 0000000..25d3cfe --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c @@ -0,0 +1,218 @@ +/* /linux/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c + * + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem.h" + +#include "modem_prj.h" +#include "modem_link_device_dpram.h" +#include "modem_utils.h" + +#define IDPRAM_NORMAL_BOOT_MAGIC 0x4D4E + +static irqreturn_t phone_active_irq_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int phone_reset = gpio_get_value(mc->gpio_cp_reset); + int phone_active = gpio_get_value(mc->gpio_phone_active); + int phone_state = mc->phone_state; + + pr_info("MIF: <%s> state = %d, phone_reset = %d, phone_active = %d\n", + __func__, phone_state, phone_reset, phone_active); + + if (phone_reset && phone_active) { + phone_state = STATE_ONLINE; + if (mc->phone_state != STATE_BOOTING) + mc->iod->modem_state_changed(mc->iod, phone_state); + } else if (phone_reset && !phone_active) { + if (mc->phone_state == STATE_ONLINE) { + phone_state = STATE_CRASH_EXIT; + mc->iod->modem_state_changed(mc->iod, phone_state); + } + } else { + phone_state = STATE_OFFLINE; + if (mc->iod && mc->iod->modem_state_changed) + mc->iod->modem_state_changed(mc->iod, phone_state); + } + + if (phone_active) + irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW); + else + irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH); + + pr_info("MIF: <%s> phone_state = %d\n", __func__, phone_state); + + return IRQ_HANDLED; +} + +static void set_idpram_boot_magic(struct dpram_link_device *dpld) +{ + dpld->set_access(dpld, 0); + dpld->set_magic(dpld, IDPRAM_NORMAL_BOOT_MAGIC); + dpld->set_access(dpld, 1); +} + +static int qsc6085_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + mif_err("+++\n"); + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset) { + mif_err("no gpio_cp_on or no gpio_cp_reset\n"); + return -ENXIO; + } + + set_idpram_boot_magic(dpld); + + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + + gpio_set_value(mc->gpio_cp_reset, 1); + gpio_set_value(mc->gpio_cp_on, 0); + + msleep(100); + + gpio_set_value(mc->gpio_cp_on, 1); + + msleep(400); + msleep(400); + msleep(200); + + gpio_set_value(mc->gpio_cp_on, 0); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_off(struct modem_ctl *mc) +{ + int phone_wait_cnt = 0; + + pr_info("MIF: <%s+>\n", __func__); + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || + !mc->gpio_phone_active) { + pr_err("MIF: <%s> no gpio data\n", __func__); + return -ENXIO; + } + + gpio_set_value(mc->gpio_cp_on, 0); + + /* confirm phone off */ + while (1) { + if (gpio_get_value(mc->gpio_phone_active)) { + pr_err("MIF: <%s> Try to Turn Phone Off by CP_RST\n", + __func__); + gpio_set_value(mc->gpio_cp_reset, 0); + if (phone_wait_cnt > 10) { + pr_emerg("MIF: <%s> OFF Failed\n", __func__); + break; + } + phone_wait_cnt++; + mdelay(100); + } else { + pr_emerg("MIF: <%s> OFF Success\n", __func__); + break; + } + } + + mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); + + pr_info("MIF: <%s->\n", __func__); + + return 0; +} + +static int qsc6085_reset(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + mif_err("+++\n"); + + set_idpram_boot_magic(dpld); + + gpio_set_value(mc->gpio_cp_reset, 0); + msleep(100); + gpio_set_value(mc->gpio_cp_reset, 1); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_modem_dump_reset(struct modem_ctl *mc) +{ + pr_info("MIF: <%s>\n", __func__); + panic("CP Crashed"); +} + +static void qsc6085_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = qsc6085_on; + mc->ops.modem_off = qsc6085_off; + mc->ops.modem_reset = qsc6085_reset; + mc->ops.modem_dump_reset = qsc6085_modem_dump_reset; +} + +int qsc6085_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + unsigned long flag = 0; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { + mif_err("no GPIO data\n"); + return -ENXIO; + } + + mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); + pr_err("MIF: <%s> PHONE_ACTIVE IRQ# = %d\n", + __func__, mc->irq_phone_active); + + qsc6085_get_ops(mc); + + /*register phone_active_handler*/ + flag = IRQF_TRIGGER_HIGH; + + ret = request_irq(mc->irq_phone_active, + phone_active_irq_handler, + flag, "phone_active", mc); + if (ret) { + pr_err("MIF: failed to irq_phone_active request_irq: %d\n" + , ret); + return ret; + } + + ret = enable_irq_wake(mc->irq_phone_active); + if (ret) { + pr_err("MIF: <%s> failed to enable_irq_wake:%d\n", + __func__, ret); + free_irq(mc->irq_phone_active, mc); + return ret; + } + return ret; +} diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c index cfa2896..7a6e6fc 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c +++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c @@ -22,10 +22,13 @@ #include #include #include -#include +#include "modem.h" #include "modem_prj.h" #include +spinlock_t irq_lock; +int irq_lock_flag; + int sprd_boot_done; extern int spi_thread_restart(void); @@ -45,10 +48,20 @@ static int sprd8803_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_ctrl2, 1); #endif msleep(100); -// pr_info("[MODEM_IF] %s\n", __func__); // Kill spam + pr_info("[MODEM_IF] %s\n", __func__); gpio_set_value(mc->gpio_cp_on, 1); gpio_set_value(mc->gpio_pda_active, 1); + spin_lock(&irq_lock); + if (!irq_lock_flag) { + enable_irq(mc->irq_phone_active); + enable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + enable_irq_wake(mc->irq_phone_active); + enable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 1; + } + spin_unlock(&irq_lock); + mc->phone_state = STATE_BOOTING; return 0; @@ -56,7 +69,7 @@ static int sprd8803_on(struct modem_ctl *mc) static int sprd8803_off(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); if (!mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -64,6 +77,17 @@ static int sprd8803_off(struct modem_ctl *mc) } gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_pda_active, 0); + + spin_lock(&irq_lock); + if (irq_lock_flag) { + disable_irq(mc->irq_phone_active); + disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + disable_irq_wake(mc->irq_phone_active); + disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 0; + } + spin_unlock(&irq_lock); mc->phone_state = STATE_OFFLINE; @@ -72,7 +96,7 @@ static int sprd8803_off(struct modem_ctl *mc) static int sprd8803_reset(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); spi_thread_restart(); @@ -81,20 +105,20 @@ static int sprd8803_reset(struct modem_ctl *mc) static int sprd8803_boot_on(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done); - return sprd_boot_done; + pr_info("[MODEM_IF] %s %d\n", __func__, mc->phone_state); + return mc->phone_state; } static int sprd8803_boot_off(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); spi_sema_init(); return 0; } static int sprd8803_dump_reset(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); if (!mc->gpio_ap_cp_int2) return -ENXIO; @@ -115,6 +139,8 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; + disable_irq_nosync(mc->irq_phone_active); + if (!mc->gpio_phone_active || !mc->gpio_cp_dump_int) { pr_err("[MODEM_IF] no gpio data\n"); @@ -135,7 +161,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) else phone_state = STATE_OFFLINE; - if (cp_dump_value) + if (phone_active_value && cp_dump_value) phone_state = STATE_CRASH_EXIT; if (mc->iod && mc->iod->modem_state_changed) @@ -145,6 +171,8 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) mc->bootd->modem_state_changed(mc->bootd, phone_state); exit: + enable_irq(mc->irq_phone_active); + return IRQ_HANDLED; } @@ -217,5 +245,19 @@ int sprd8803_init_modemctl_device(struct modem_ctl *mc, __func__, ret); free_irq(irq_cp_dump_int, mc); } + + irq_lock_flag = 1; + spin_lock_init(&irq_lock); + + spin_lock(&irq_lock); + if (irq_lock_flag) { + disable_irq(mc->irq_phone_active); + disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + disable_irq_wake(mc->irq_phone_active); + disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 0; + } + spin_unlock(&irq_lock); + return ret; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_ss222.c b/drivers/misc/modem_if/modem_modemctl_device_ss222.c new file mode 100644 index 0000000..5dbda45 --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_ss222.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" + +#define MIF_INIT_TIMEOUT (30 * HZ) + +static void ss222_mc_state_fsm(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int old_state = mc->phone_state; + int new_state = mc->phone_state; + + mif_err("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(old_state), cp_on, cp_reset, cp_active); + + if (cp_active) { + if (!cp_on) { + new_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + } else if (old_state == STATE_ONLINE) { + new_state = STATE_CRASH_EXIT; + ld->mode = LINK_MODE_ULOAD; + } else { + mif_err("don't care!!!\n"); + } + } + + if (old_state != new_state) { + mif_err("new_state = %s\n", get_cp_state_str(new_state)); + mc->bootd->modem_state_changed(mc->bootd, new_state); + mc->iod->modem_state_changed(mc->iod, new_state); + } +} + +static irqreturn_t phone_active_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + + if (cp_reset) + ss222_mc_state_fsm(mc); + + return IRQ_HANDLED; +} + +static inline void make_gpio_floating(int gpio, bool floating) +{ + if (floating) + gpio_direction_input(gpio); + else + gpio_direction_output(gpio, 0); +} + +static int ss222_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_off = gpio_get_value(mc->gpio_cp_off); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int cp_status = gpio_get_value(mc->gpio_cp_status); + mif_err("+++\n"); + mif_err("cp_on:%d cp_reset:%d ps_hold:%d cp_active:%d cp_status:%d\n", + cp_on, cp_reset, cp_off, cp_active, cp_status); + + gpio_set_value(mc->gpio_pda_active, 1); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + + /* Make PS_HOLD floating (Hi-Z) for CP ON */ + make_gpio_floating(mc->gpio_cp_off, true); + + gpio_set_value(mc->gpio_cp_on, 0); + msleep(100); + + gpio_set_value(mc->gpio_cp_reset, 0); + msleep(500); + + gpio_set_value(mc->gpio_cp_on, 1); + msleep(100); + + c2c_reload(); + gpio_set_value(mc->gpio_cp_reset, 1); + msleep(300); + + mif_err("---\n"); + return 0; +} + +static int ss222_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + mif_err("+++\n"); + + if (mc->phone_state == STATE_OFFLINE || cp_on == 0) + return 0; + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + + gpio_set_value(mc->gpio_cp_reset, 0); + + /* Make PS_HOLD LOW for CP OFF */ + make_gpio_floating(mc->gpio_cp_off, false); + gpio_set_value(mc->gpio_cp_on, 0); + + mif_err("---\n"); + return 0; +} + +static int ss222_reset(struct modem_ctl *mc) +{ + mif_err("+++\n"); + + if (ss222_off(mc)) + return -EIO; + + msleep(100); + + if (ss222_on(mc)) + return -EIO; + + mif_err("---\n"); + return 0; +} + +static int ss222_force_crash_exit(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_err("+++\n"); + + /* Make DUMP start */ + ld->force_dump(ld, mc->bootd); + + mif_err("---\n"); + return 0; +} + +static int ss222_dump_reset(struct modem_ctl *mc) +{ + unsigned int gpio_cp_reset = mc->gpio_cp_reset; + mif_err("+++\n"); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + gpio_set_value(gpio_cp_reset, 0); + udelay(200); + + c2c_reload(); + gpio_set_value(gpio_cp_reset, 1); + msleep(300); + + gpio_set_value(mc->gpio_ap_status, 1); + + mif_err("---\n"); + return 0; +} + +static int ss222_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_debug("+++\n"); + + disable_irq_nosync(mc->irq_phone_active); + + gpio_set_value(mc->gpio_ap_status, 1); + + ld->mode = LINK_MODE_BOOT; + + mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + + INIT_COMPLETION(ld->init_cmpl); + + mif_debug("---\n"); + return 0; +} + +static int ss222_boot_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + unsigned long remain; + mif_debug("+++\n"); + + ld->mode = LINK_MODE_IPC; + + remain = wait_for_completion_timeout(&ld->init_cmpl, MIF_INIT_TIMEOUT); + if (remain == 0) { + mif_err("T-I-M-E-O-U-T\n"); + mif_err("xxx\n"); + return -EAGAIN; + } + + mif_debug("---\n"); + return 0; +} + +static int ss222_boot_done(struct modem_ctl *mc) +{ + mif_debug("+++\n"); + + if (wake_lock_active(&mc->mc_wake_lock)) + wake_unlock(&mc->mc_wake_lock); + + enable_irq(mc->irq_phone_active); + + mif_debug("---\n"); + return 0; +} + +static void ss222_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = ss222_on; + mc->ops.modem_off = ss222_off; + mc->ops.modem_reset = ss222_reset; + mc->ops.modem_boot_on = ss222_boot_on; + mc->ops.modem_boot_off = ss222_boot_off; + mc->ops.modem_boot_done = ss222_boot_done; + mc->ops.modem_force_crash_exit = ss222_force_crash_exit; + mc->ops.modem_dump_reset = ss222_dump_reset; +} + +int ss222_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + int irq = 0; + unsigned long flag = 0; + mif_debug("+++\n"); + + if (!pdata->gpio_cp_on || !pdata->gpio_cp_off || !pdata->gpio_cp_reset + || !pdata->gpio_pda_active || !pdata->gpio_phone_active + || !pdata->gpio_ap_wakeup || !pdata->gpio_ap_status + || !pdata->gpio_cp_wakeup || !pdata->gpio_cp_status) { + mif_err("ERR! no GPIO data\n"); + mif_err("xxx\n"); + return -ENXIO; + } + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_ap_wakeup = pdata->gpio_ap_wakeup; + mc->gpio_ap_status = pdata->gpio_ap_status; + mc->gpio_cp_wakeup = pdata->gpio_cp_wakeup; + mc->gpio_cp_status = pdata->gpio_cp_status; + + gpio_set_value(mc->gpio_cp_reset, 0); + + gpio_set_value(mc->gpio_cp_on, 0); + + ss222_get_ops(mc); + dev_set_drvdata(mc->dev, mc); + + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "umts_wake_lock"); + + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("ERR! no irq_phone_active\n"); + mif_err("xxx\n"); + return -1; + } + mif_err("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); + + irq = mc->irq_phone_active; + flag = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND; + ret = request_irq(irq, phone_active_handler, flag, "umts_active", mc); + if (ret) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, ret); + mif_err("xxx\n"); + return ret; + } + ret = enable_irq_wake(irq); + if (ret) + mif_err("enable_irq_wake(#%d) fail (err %d)\n", irq, ret); + + mif_debug("---\n"); + return 0; +} + diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c index d2fcf5b..9741f59 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c @@ -22,12 +22,12 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" static int xmm6260_on(struct modem_ctl *mc) { - mif_debug("xmm6260_on()\n"); + mif_info("xmm6260_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) { mif_err("no gpio data\n"); @@ -66,7 +66,7 @@ static int xmm6260_on(struct modem_ctl *mc) static int xmm6260_off(struct modem_ctl *mc) { - mif_debug("xmm6260_off()\n"); + mif_info("xmm6260_off()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -85,7 +85,7 @@ static int xmm6260_off(struct modem_ctl *mc) static int xmm6260_reset(struct modem_ctl *mc) { - mif_debug("xmm6260_reset()\n"); + mif_info("xmm6260_reset()\n"); if (!mc->gpio_cp_reset || !mc->gpio_reset_req_n) return -ENXIO; @@ -122,7 +122,7 @@ static int xmm6260_reset(struct modem_ctl *mc) static int xmm6260_boot_on(struct modem_ctl *mc) { - mif_debug("xmm6260_boot_on()\n"); + mif_info("xmm6260_boot_on()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); @@ -136,7 +136,7 @@ static int xmm6260_boot_on(struct modem_ctl *mc) static int xmm6260_boot_off(struct modem_ctl *mc) { - mif_debug("xmm6260_boot_off()\n"); + mif_info("xmm6260_boot_off()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c index 4d0b69c..5473aad 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c @@ -24,9 +24,11 @@ #include #include #include -#include +#include "modem.h" #include "modem_prj.h" - +#ifdef CONFIG_FAST_BOOT +#include +#endif static int xmm6262_on(struct modem_ctl *mc) { mif_info("\n"); @@ -59,11 +61,13 @@ static int xmm6262_on(struct modem_ctl *mc) udelay(60); gpio_set_value(mc->gpio_cp_on, 0); msleep(20); + + mc->phone_state = STATE_BOOTING; + if (mc->gpio_revers_bias_restore) mc->gpio_revers_bias_restore(); gpio_set_value(mc->gpio_pda_active, 1); - mc->phone_state = STATE_BOOTING; return 0; } @@ -120,6 +124,20 @@ static int xmm6262_reset(struct modem_ctl *mc) return 0; } +static int xmm6262_force_crash_exit(struct modem_ctl *mc) +{ + mif_info("\n"); + + if (!mc->gpio_ap_dump_int) + return -ENXIO; + + gpio_set_value(mc->gpio_ap_dump_int, 1); + mif_info("set ap_dump_int(%d) to high=%d\n", + mc->gpio_ap_dump_int, gpio_get_value(mc->gpio_ap_dump_int)); + return 0; +} + + static irqreturn_t phone_active_irq_handler(int irq, void *_mc) { int phone_reset = 0; @@ -172,14 +190,72 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) return IRQ_HANDLED; } +#ifdef CONFIG_FAST_BOOT +#include + +static void mif_sim_detect_complete(struct modem_ctl *mc) +{ + if (mc->sim_shutdown_req) { + mif_info("fake shutdown sim changed shutdown\n"); + kernel_power_off(); + /*kernel_restart(NULL);*/ + mc->sim_shutdown_req = false; + } +} + +static int mif_init_sim_shutdown(struct modem_ctl *mc) +{ + mc->sim_shutdown_req = false; + mc->modem_complete = mif_sim_detect_complete; + + return 0; +} + +static void mif_check_fake_shutdown(struct modem_ctl *mc, bool online) +{ + if (fake_shut_down && mc->sim_state.online != online) + mc->sim_shutdown_req = true; +} + +#else +static inline int mif_init_sim_shutdown(struct modem_ctl *mc) { return 0; } +#define mif_check_fake_shutdown(a, b) do {} while (0) +#endif + + +#define SIM_DETECT_DEBUG static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) { struct modem_ctl *mc = (struct modem_ctl *)_mc; +#ifdef SIM_DETECT_DEBUG + int val = gpio_get_value(mc->gpio_sim_detect); + static int unchange; + static int prev_val; + + if (mc->phone_state == STATE_BOOTING) { + mif_info("BOOTING, reset unchange\n"); + unchange = 0; + } - if (mc->iod && mc->iod->sim_state_changed) + if (prev_val == val) { + if (unchange++ > 50) { + mif_err("Abnormal SIM detect GPIO irqs"); + disable_irq_nosync(mc->gpio_sim_detect); + panic("SIM detect IRQ Error"); + } + } else { + unchange = 0; + } + prev_val = val; +#endif + if (mc->iod && mc->iod->sim_state_changed) { + mif_check_fake_shutdown(mc, + gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity + ); mc->iod->sim_state_changed(mc->iod, gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity ); + } return IRQ_HANDLED; } @@ -189,6 +265,7 @@ static void xmm6262_get_ops(struct modem_ctl *mc) mc->ops.modem_on = xmm6262_on; mc->ops.modem_off = xmm6262_off; mc->ops.modem_reset = xmm6262_reset; + mc->ops.modem_force_crash_exit = xmm6262_force_crash_exit; } int xmm6262_init_modemctl_device(struct modem_ctl *mc, @@ -218,6 +295,7 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2; #endif + pdev = to_platform_device(mc->dev); mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); @@ -261,6 +339,12 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, /* initialize sim_state => insert: gpio=0, remove: gpio=1 */ mc->sim_state.online = gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity; + + ret = mif_init_sim_shutdown(mc); + if (ret) { + mif_err("failed to sim fake shutdown init: %d\n", ret); + goto err_sim_detect_set_wake_irq; + } } return ret; diff --git a/drivers/misc/modem_if/modem_net_flowcontrol_device.c b/drivers/misc/modem_if/modem_net_flowcontrol_device.c index b3f055d..3aa340f 100644 --- a/drivers/misc/modem_if/modem_net_flowcontrol_device.c +++ b/drivers/misc/modem_if/modem_net_flowcontrol_device.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include "modem.h" #include "modem_prj.h" diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h index f85596f..ccff272 100644 --- a/drivers/misc/modem_if/modem_prj.h +++ b/drivers/misc/modem_if/modem_prj.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -19,12 +18,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include "modem.h" + +#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP +#define DEBUG_MODEM_IF +#endif #define MAX_CPINFO_SIZE 512 @@ -33,7 +38,10 @@ #define MAX_FMT_DEVS 10 #define MAX_RAW_DEVS 32 #define MAX_RFS_DEVS 10 -#define MAX_NUM_IO_DEV (MAX_FMT_DEVS + MAX_RAW_DEVS + MAX_RFS_DEVS) +#define MAX_BOOT_DEVS 10 +#define MAX_DUMP_DEVS 10 + +#define MAX_IOD_RXQ_LEN 2048 #define IOCTL_MODEM_ON _IO('o', 0x19) #define IOCTL_MODEM_OFF _IO('o', 0x20) @@ -62,22 +70,40 @@ #define IOCTL_MODEM_SWITCH_MODEM _IO('o', 0x37) #endif -#define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40) -#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) - -/* ioctl command definitions. */ -#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xd0) -#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xd1) -#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xd2) -#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xd3) +#define IOCTL_MODEM_RAMDUMP_START _IO('o', 0xCE) +#define IOCTL_MODEM_RAMDUMP_STOP _IO('o', 0xCF) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf) +#define IOCTL_MODEM_XMIT_BOOT _IO('o', 0x40) +#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) /* ioctl command for IPC Logger */ #define IOCTL_MIF_LOG_DUMP _IO('o', 0x51) #define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52) +/* ioctl command definitions. */ +#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xD0) +#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xD1) +#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xD2) +#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xD3) + +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xDE) +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xDF) + +#define CPBOOT_DIR_MASK 0xF000 +#define CPBOOT_STAGE_MASK 0x0F00 +#define CPBOOT_CMD_MASK 0x000F +#define CPBOOT_REQ_RESP_MASK 0x0FFF + +#define CPBOOT_DIR_AP2CP 0x9000 +#define CPBOOT_DIR_CP2AP 0xA000 + +#define CPBOOT_STAGE_SHIFT 8 + +#define CPBOOT_STAGE_START 0x0000 +#define CPBOOT_CRC_SEND 0x000C +#define CPBOOT_STAGE_DONE 0x000D +#define CPBOOT_STAGE_FAIL 0x000F + /* modem status */ #define MODEM_OFF 0 #define MODEM_CRASHED 1 @@ -93,83 +119,50 @@ #define PSD_DATA_CHID_BEGIN 0x2A #define PSD_DATA_CHID_END 0x38 -#define PS_DATA_CH_0 10 -#define PS_DATA_CH_LAST 24 +#define PS_DATA_CH_0 10 +#define PS_DATA_CH_LAST 24 +#define RMNET0_CH_ID PS_DATA_CH_0 #define IP6VERSION 6 #define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC} -/* loopback: CP -> AP -> CP */ -#define CP2AP_LOOPBACK_CHANNEL 30 - -/* ip loopback */ -#define RMNET0_CH_ID 10 +/* IP loopback */ +#define DATA_DRAIN_CHANNEL 30 /* Drain channel to drop RX packets */ #define DATA_LOOPBACK_CHANNEL 31 /* Debugging features */ -#define MAX_MIF_LOG_PATH_LEN 128 -#define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */ - -#define MAX_MIF_EVT_BUFF_SIZE 256 -#define MAX_MIF_TIME_LEN 32 -#define MAX_MIF_NAME_LEN 16 -#define MAX_MIF_STR_LEN 127 -#define MAX_MIF_LOG_LEN 128 - -enum mif_event_id { - MIF_IRQ_EVT = 0, - MIF_LNK_RX_EVT, - MIF_MUX_RX_EVT, - MIF_IOD_RX_EVT, - MIF_IOD_TX_EVT, - MIF_MUX_TX_EVT, - MIF_LNK_TX_EVT, - MAX_MIF_EVT -}; - -struct dpram_queue_status { - unsigned in; - unsigned out; -}; - -struct dpram_queue_status_pair { - struct dpram_queue_status txq; - struct dpram_queue_status rxq; -}; - -struct dpram_irq_buff { - unsigned magic; - unsigned access; - struct dpram_queue_status_pair qsp[MAX_IPC_DEV]; - unsigned int2ap; - unsigned int2cp; -}; - -/* Not use */ -struct mif_event_buff { - char time[MAX_MIF_TIME_LEN]; - - struct timeval tv; - enum mif_event_id evt; - - char mc[MAX_MIF_NAME_LEN]; - - char iod[MAX_MIF_NAME_LEN]; - - char ld[MAX_MIF_NAME_LEN]; - enum modem_link link_type; - - unsigned rcvd; - unsigned len; - union { - u8 data[MAX_MIF_LOG_LEN]; - struct dpram_irq_buff dpram_irqb; - }; +#define MIF_LOG_DIR "/sdcard/log" +#define MIF_MAX_PATH_LEN 256 +#define MIF_MAX_NAME_LEN 64 +#define MIF_MAX_STR_LEN 32 + +#define CP_CRASH_TAG "CP Crash " + +static const char const *dev_format_str[] = { + [IPC_FMT] = "FMT", + [IPC_RAW] = "RAW", + [IPC_RFS] = "RFS", + [IPC_MULTI_RAW] = "MULTI_RAW", + [IPC_CMD] = "CMD", + [IPC_BOOT] = "BOOT", + [IPC_RAMDUMP] = "RAMDUMP", + [IPC_DEBUG] = "DEBUG", }; -#define MIF_LOG_DIR "/sdcard" -#define MIF_LOG_LV_FILE "/data/.mif_log_level" +/** + * get_dev_name + * @dev: IPC device (enum dev_format) + * + * Returns IPC device name as a string. + */ +static const inline char *get_dev_name(unsigned int dev) +{ + if (unlikely(dev >= MAX_DEV_FORMAT)) + return "INVALID"; + else + return dev_format_str[dev]; +} /* Does modem ctl structure will use state ? or status defined below ?*/ enum modem_state { @@ -187,6 +180,26 @@ enum modem_state { #endif }; +static const char const *cp_state_str[] = { + [STATE_OFFLINE] = "OFFLINE", + [STATE_CRASH_RESET] = "CRASH_RESET", + [STATE_CRASH_EXIT] = "CRASH_EXIT", + [STATE_BOOTING] = "BOOTING", + [STATE_ONLINE] = "ONLINE", + [STATE_NV_REBUILDING] = "NV_REBUILDING", + [STATE_LOADER_DONE] = "LOADER_DONE", + [STATE_SIM_ATTACH] = "SIM_ATTACH", + [STATE_SIM_DETACH] = "SIM_DETACH", +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + [STATE_MODEM_SWITCH] = "MODEM_SWITCH", +#endif +}; + +static const inline char *get_cp_state_str(int state) +{ + return cp_state_str[state]; +} + enum com_state { COM_NONE, COM_ONLINE, @@ -208,6 +221,17 @@ struct sim_state { bool changed; /* online is changed? */ }; +enum cp_boot_mode { + CP_BOOT_MODE_NORMAL, + CP_BOOT_MODE_DUMP, + MAX_CP_BOOT_MODE +}; + +struct modem_firmware { + char *binary; + u32 size; +}; + #define HDLC_START 0x7F #define HDLC_END 0x7E #define SIZE_OF_HDLC_START 1 @@ -216,8 +240,8 @@ struct sim_state { struct header_data { char hdr[HDLC_HEADER_MAX_SIZE]; - unsigned len; - unsigned frag_len; + u32 len; + u32 frag_len; char start; /*hdlc start header 0x7F*/ }; @@ -255,10 +279,14 @@ struct sipc_fmt_hdr { #define SIPC5_EXT_FIELD_EXIST 0b00000010 #define SIPC5_CTL_FIELD_EXIST 0b00000001 -#define SIPC5_MAX_HEADER_SIZE 6 -#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 +#define SIPC5_EXT_LENGTH_MASK SIPC5_EXT_FIELD_EXIST +#define SIPC5_CTL_FIELD_MASK (SIPC5_EXT_FIELD_EXIST | SIPC5_CTL_FIELD_EXIST) + +#define SIPC5_MIN_HEADER_SIZE 4 #define SIPC5_HEADER_SIZE_WITH_CTL_FLD 5 -#define SIPC5_MIN_HEADER_SIZE 4 +#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 +#define SIPC5_MAX_HEADER_SIZE SIPC5_HEADER_SIZE_WITH_EXT_LEN + #define SIPC5_CONFIG_SIZE 1 #define SIPC5_CH_ID_SIZE 1 @@ -267,13 +295,18 @@ struct sipc_fmt_hdr { #define SIPC5_LEN_OFFSET 2 #define SIPC5_CTL_OFFSET 4 -#define SIPC5_CH_ID_RAW_0 0 #define SIPC5_CH_ID_PDP_0 10 #define SIPC5_CH_ID_PDP_LAST 24 +#define SIPC5_CH_ID_BOOT0 215 +#define SIPC5_CH_ID_DUMP0 225 #define SIPC5_CH_ID_FMT_0 235 #define SIPC5_CH_ID_RFS_0 245 #define SIPC5_CH_ID_MAX 255 +#define SIPC5_CH_ID_FLOW_CTRL 255 +#define FLOW_CTRL_SUSPEND ((u8)(0xCA)) +#define FLOW_CTRL_RESUME ((u8)(0xCB)) + /* If iod->id is 0, do not need to store to `iodevs_tree_fmt' in SIPC4 */ #define sipc4_is_not_reserved_channel(ch) ((ch) != 0) @@ -295,20 +328,6 @@ struct sipc5_link_hdr { } __packed; struct sipc5_frame_data { - /* Config octet */ - u8 config; - - /* Channel ID */ - u8 ch_id; - - /* Control for multiple FMT frame */ - u8 control; - - /* Frame configuration set by header analysis */ - bool padding; - bool ctl_fld; - bool ext_len; - /* Frame length calculated from the length fields */ unsigned len; @@ -318,11 +337,17 @@ struct sipc5_frame_data { /* The length of received header */ unsigned hdr_rcvd; - /* The length of data payload */ - unsigned data_len; + /* The length of link layer payload */ + unsigned pay_len; /* The length of received data */ - unsigned data_rcvd; + unsigned pay_rcvd; + + /* The length of link layer padding */ + unsigned pad_len; + + /* The length of received padding */ + unsigned pad_rcvd; /* Header buffer */ u8 hdr[SIPC5_MAX_HEADER_SIZE]; @@ -349,8 +374,9 @@ struct skbuff_private { struct io_device *iod; struct link_device *ld; struct io_device *real_iod; /* for rx multipdp */ - u8 ch_id; - u8 control; + + /* for indicating that thers is only one IPC frame in an skb */ + bool single_frame; } __packed; static inline struct skbuff_private *skbpriv(struct sk_buff *skb) @@ -359,6 +385,35 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb) return (struct skbuff_private *)&skb->cb; } +enum iod_rx_state { + IOD_RX_ON_STANDBY = 0, + IOD_RX_HEADER, + IOD_RX_PAYLOAD, + IOD_RX_PADDING, + MAX_IOD_RX_STATE +}; + +static const char const *rx_state_str[] = { + [IOD_RX_ON_STANDBY] = "RX_ON_STANDBY", + [IOD_RX_HEADER] = "RX_HEADER", + [IOD_RX_PAYLOAD] = "RX_PAYLOAD", + [IOD_RX_PADDING] = "RX_PADDING", +}; + +/** + * get_dev_name + * @dev: IPC device (enum dev_format) + * + * Returns IPC device name as a string. + */ +static const inline char *get_rx_state_str(unsigned int state) +{ + if (unlikely(state >= MAX_IOD_RX_STATE)) + return "INVALID_STATE"; + else + return rx_state_str[state]; +} + struct io_device { /* rb_tree node for an io device */ struct rb_node node_chan; @@ -367,6 +422,7 @@ struct io_device { /* Name of the IO device */ char *name; + /* Reference count */ atomic_t opened; /* Wait queue for the IO device */ @@ -383,7 +439,11 @@ struct io_device { enum modem_io io_typ; enum modem_network net_typ; - bool use_handover; /* handover 2+ link devices */ + /* The name of the application that will use this IO device */ + char *app; + + /* Whether or not handover among 2+ link devices */ + bool use_handover; /* SIPC version */ enum sipc_ver ipc_version; @@ -391,6 +451,10 @@ struct io_device { /* Rx queue of sk_buff */ struct sk_buff_head sk_rx_q; + /* RX state used in RX FSM */ + enum iod_rx_state curr_rx_state; + enum iod_rx_state next_rx_state; + /* ** work for each io device, when delayed work needed ** use this for private io device rx action @@ -447,6 +511,9 @@ struct link_device { /* SIPC version */ enum sipc_ver ipc_version; + /* Maximum IPC device = the last IPC device (e.g. IPC_RFS) + 1 */ + int max_ipc_dev; + /* Modem data */ struct modem_data *mdm_data; @@ -459,6 +526,12 @@ struct link_device { /* Operation mode of the link device */ enum link_mode mode; + /* completion for waiting for link initialization */ + struct completion init_cmpl; + + /* completion for waiting for PIF initialization in a CP */ + struct completion pif_cmpl; + struct io_device *fmt_iods[4]; /* TX queue of socket buffers */ @@ -468,13 +541,30 @@ struct link_device { struct sk_buff_head *skb_txq[MAX_IPC_DEV]; + /* RX queue of socket buffers */ + struct sk_buff_head sk_fmt_rx_q; + struct sk_buff_head sk_raw_rx_q; + struct sk_buff_head sk_rfs_rx_q; + + struct sk_buff_head *skb_rxq[MAX_IPC_DEV]; + bool raw_tx_suspended; /* for misc dev */ struct completion raw_tx_resumed_by_cp; + /** + * This flag is for TX flow control on network interface. + * This must be set and clear only by a flow control command from CP. + */ + bool suspend_netif_tx; + struct workqueue_struct *tx_wq; struct work_struct tx_work; struct delayed_work tx_delayed_work; - struct delayed_work tx_dwork; + + struct delayed_work *tx_dwork[MAX_IPC_DEV]; + struct delayed_work fmt_tx_dwork; + struct delayed_work raw_tx_dwork; + struct delayed_work rfs_tx_dwork; struct workqueue_struct *rx_wq; struct work_struct rx_work; @@ -495,20 +585,26 @@ struct link_device { int (*send)(struct link_device *ld, struct io_device *iod, struct sk_buff *skb); - int (*udl_start)(struct link_device *ld, struct io_device *iod); - - int (*force_dump)(struct link_device *ld, struct io_device *iod); - - int (*dump_start)(struct link_device *ld, struct io_device *iod); + /* method for CP booting */ + int (*xmit_boot)(struct link_device *ld, struct io_device *iod, + unsigned long arg); - int (*modem_update)(struct link_device *ld, struct io_device *iod, + /* methods for CP firmware upgrade */ + int (*dload_start)(struct link_device *ld, struct io_device *iod); + int (*firm_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); + /* methods for CP crash dump */ + int (*force_dump)(struct link_device *ld, struct io_device *iod); + int (*dump_start)(struct link_device *ld, struct io_device *iod); int (*dump_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); + int (*dump_finish)(struct link_device *ld, struct io_device *iod, + unsigned long arg); + /* IOCTL extension */ int (*ioctl)(struct link_device *ld, struct io_device *iod, - unsigned cmd, unsigned long _arg); + unsigned cmd, unsigned long arg); }; /** rx_alloc_skb - allocate an skbuff and set skb's iod, ld @@ -567,6 +663,9 @@ struct modem_shared { struct mif_storage storage; spinlock_t lock; + /* CP crash information */ + char cp_crash_info[530]; + /* loopbacked IP address * default is 0.0.0.0 (disabled) * after you setted this, you can use IP packet loopback using this IP. @@ -586,35 +685,52 @@ struct modem_ctl { struct sim_state sim_state; unsigned gpio_cp_on; + unsigned gpio_cp_off; unsigned gpio_reset_req_n; unsigned gpio_cp_reset; + + /* for broadcasting AP's PM state (active or sleep) */ unsigned gpio_pda_active; + + /* for checking aliveness of CP */ unsigned gpio_phone_active; + int irq_phone_active; + + /* for AP-CP power management (PM) handshaking */ + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* for USB/HSIC PM */ + unsigned gpio_host_wakeup; + int irq_host_wakeup; + unsigned gpio_host_active; + unsigned gpio_slave_wakeup; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + unsigned gpio_cpufreq_lock; + struct delayed_work work_cpu_lock; + struct delayed_work work_cpu_unlock; +#endif + unsigned gpio_cp_dump_int; unsigned gpio_ap_dump_int; unsigned gpio_flm_uart_sel; + unsigned gpio_cp_warm_reset; #if defined(CONFIG_MACH_M0_CTC) unsigned gpio_flm_uart_sel_rev06; #endif - unsigned gpio_cp_warm_reset; - unsigned gpio_cp_off; - unsigned gpio_sim_detect; - unsigned gpio_dynamic_switching; - int irq_phone_active; + unsigned gpio_sim_detect; int irq_sim_detect; -#ifdef CONFIG_LTE_MODEM_CMC221 - const struct attribute_group *group; - unsigned gpio_slave_wakeup; - unsigned gpio_host_wakeup; - unsigned gpio_host_active; - int irq_host_wakeup; - - struct delayed_work dwork; -#endif /*CONFIG_LTE_MODEM_CMC221*/ - - struct work_struct work; +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga_cs_n; +#endif #if defined(CONFIG_MACH_U1_KOR_LGT) unsigned gpio_cp_reset_msm; @@ -635,9 +751,13 @@ struct modem_ctl { unsigned gpio_cp_ctrl2; #endif -#ifdef CONFIG_LINK_DEVICE_PLD - unsigned gpio_fpga_cs_n; -#endif + /* Switch with 2 links in a modem */ + unsigned gpio_link_switch; + + const struct attribute_group *group; + + struct delayed_work dwork; + struct work_struct work; struct modemctl_ops ops; struct io_device *iod; @@ -651,119 +771,45 @@ struct modem_ctl { bool need_switch_to_usb; bool sim_polarity; + + bool sim_shutdown_req; + void (*modem_complete)(struct modem_ctl *mc); }; int sipc4_init_io_device(struct io_device *iod); int sipc5_init_io_device(struct io_device *iod); -/** - * sipc5_start_valid - * @cfg: configuration field of an SIPC5 link frame - * - * Returns TRUE if the start (configuration field) of an SIPC5 link frame - * is valid or returns FALSE if it is not valid. - * - */ -static inline int sipc5_start_valid(u8 cfg) -{ - return (cfg & SIPC5_START_MASK) == SIPC5_START_MASK; -} - -/** - * sipc5_get_hdr_len - * @cfg: configuration field of an SIPC5 link frame - * - * Returns the length of SIPC5 link layer header in an SIPC5 link frame - * - */ -static inline unsigned sipc5_get_hdr_len(u8 cfg) -{ - if (cfg & SIPC5_EXT_FIELD_EXIST) { - if (cfg & SIPC5_CTL_FIELD_EXIST) - return SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - return SIPC5_HEADER_SIZE_WITH_EXT_LEN; - } else { - return SIPC5_MIN_HEADER_SIZE; - } -} - -/** - * sipc5_get_ch_id - * @frm: pointer to an SIPC5 frame - * - * Returns the channel ID in an SIPC5 link frame - * - */ -static inline u8 sipc5_get_ch_id(u8 *frm) -{ - return *(frm + SIPC5_CH_ID_OFFSET); -} - -/** - * sipc5_get_frame_sz16 - * @frm: pointer to an SIPC5 link frame - * - * Returns the length of an SIPC5 link frame without the extended length field - * - */ -static inline unsigned sipc5_get_frame_sz16(u8 *frm) -{ - return *((u16 *)(frm + SIPC5_LEN_OFFSET)); -} - -/** - * sipc5_get_frame_sz32 - * @frm: pointer to an SIPC5 frame - * - * Returns the length of an SIPC5 link frame with the extended length field - * - */ -static inline unsigned sipc5_get_frame_sz32(u8 *frm) -{ - return *((u32 *)(frm + SIPC5_LEN_OFFSET)); -} - -/** - * sipc5_calc_padding_size - * @len: length of an SIPC5 link frame - * - * Returns the padding size for an SIPC5 link frame - * - */ -static inline unsigned sipc5_calc_padding_size(unsigned len) -{ - unsigned residue = len & 0x3; - return residue ? (4 - residue) : 0; -} - -extern void set_sromc_access(bool access); +bool sipc5_start_valid(u8 *frm); +bool sipc5_padding_exist(u8 *frm); +bool sipc5_multi_frame(u8 *frm); +bool sipc5_ext_len(u8 *frm); +int sipc5_get_hdr_len(u8 *frm); +u8 sipc5_get_ch_id(u8 *frm); +u8 sipc5_get_ctrl_field(u8 *frm); +int sipc5_get_frame_len(u8 *frm); +int sipc5_calc_padding_size(int len); +int sipc5_get_total_len(u8 *frm); + +u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count); +void sipc5_build_header(struct io_device *iod, struct link_device *ld, + u8 *buff, u8 cfg, u8 ctrl, u32 count); #if defined(CONFIG_TDSCDMA_MODEM_SPRD8803) && defined(CONFIG_LINK_DEVICE_SPI) extern int spi_sema_init(void); extern int sprd_boot_done; -struct ipc_spi { - struct class *class; - struct device *dev; - struct cdev cdev; - dev_t devid; - - wait_queue_head_t waitq; - struct fasync_struct *async_queue; - u32 mailbox; - - unsigned long base; - unsigned long size; - void __iomem *mmio; +#endif - int irq; +#define STD_UDL_STEP_MASK 0x0000000F +#define STD_UDL_SEND 0x1 +#define STD_UDL_CRC 0xC - struct completion comp; - atomic_t ref_sem; - unsigned long flags; +struct std_dload_info { + u32 size; + u32 mtu; + u32 num_frames; +} __packed; - const struct attribute_group *group; -}; -#endif +u32 std_udl_get_cmd(u8 *frm); +bool std_udl_with_payload(u32 cmd); #endif diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c index c8e83ed..366d0fa 100644 --- a/drivers/misc/modem_if/modem_sim_slot_switch.c +++ b/drivers/misc/modem_if/modem_sim_slot_switch.c @@ -17,7 +17,7 @@ static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr //return '0' slot path is '||', return '1' slot path is 'X' value = gpio_get_value(GPIO_UIM_SIM_SEL); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) value = (~value & 0x1); #endif printk("Current Slot is %x\n", value); @@ -34,7 +34,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr switch(value) { case 0: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif @@ -43,7 +43,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr break; case 1: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 0); else #endif @@ -57,7 +57,8 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr return size; } -static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch); +static DEVICE_ATTR(slot_sel, S_IRUGO | S_IWUSR | S_IWGRP, + get_slot_switch, set_slot_switch); static int __init slot_switch_manager_init(void) { @@ -75,7 +76,7 @@ static int __init slot_switch_manager_init(void) gpio_direction_output(GPIO_UIM_SIM_SEL, 1); s3c_gpio_setpull(GPIO_UIM_SIM_SEL, S3C_GPIO_PULL_NONE); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c index d62aaa6..e829324 100644 --- a/drivers/misc/modem_if/modem_utils.c +++ b/drivers/misc/modem_if/modem_utils.c @@ -12,18 +12,35 @@ * */ -#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 "modem.h" #include "modem_prj.h" +#include "modem_variation.h" #include "modem_utils.h" #define CMD_SUSPEND ((unsigned short)(0x00CA)) @@ -35,42 +52,29 @@ "mif: ------------------------------------------------------------" #define LINE_BUFF_SIZE 80 -#ifdef CONFIG_LINK_DEVICE_DPRAM -#include "modem_link_device_dpram.h" -int mif_dump_dpram(struct io_device *iod) -{ - struct link_device *ld = get_current_link(iod); - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u32 size = dpld->dp_size; - unsigned long read_len = 0; - struct sk_buff *skb; - char *buff; +static const char *hex = "0123456789abcdef"; - buff = kzalloc(size, GFP_ATOMIC); - if (!buff) { - pr_err("[MIF] <%s> alloc dpram buff failed\n", __func__); - return -ENOMEM; - } else { - dpld->dpram_dump(ld, buff); - } +void ts2utc(struct timespec *ts, struct utc_time *utc) +{ + struct tm tm; + + time_to_tm((ts->tv_sec - (sys_tz.tz_minuteswest * 60)), 0, &tm); + utc->year = 1900 + tm.tm_year; + utc->mon = 1 + tm.tm_mon; + utc->day = tm.tm_mday; + utc->hour = tm.tm_hour; + utc->min = tm.tm_min; + utc->sec = tm.tm_sec; + utc->msec = ns2ms(ts->tv_nsec); +} - while (read_len < size) { - skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); - if (!skb) { - pr_err("[MIF] <%s> alloc skb failed\n", __func__); - kfree(buff); - return -ENOMEM; - } - memcpy(skb_put(skb, MAX_IPC_SKB_SIZE), - buff + read_len, MAX_IPC_SKB_SIZE); - skb_queue_tail(&iod->sk_rx_q, skb); - read_len += MAX_IPC_SKB_SIZE; - wake_up(&iod->wq); - } - kfree(buff); - return 0; +void get_utc_time(struct utc_time *utc) +{ + struct timespec ts; + getnstimeofday(&ts); + ts2utc(&ts, utc); } -#endif +EXPORT_SYMBOL(get_utc_time); int mif_dump_log(struct modem_shared *msd, struct io_device *iod) { @@ -82,7 +86,7 @@ int mif_dump_log(struct modem_shared *msd, struct io_device *iod) while (read_len < MAX_MIF_BUFF_SIZE) { skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); if (!skb) { - pr_err("[MIF] <%s> alloc skb failed\n", __func__); + mif_err("ERR! alloc_skb fail\n"); spin_unlock_irqrestore(&msd->lock, flags); return -ENOMEM; } @@ -164,7 +168,6 @@ void _mif_com_log(enum mif_log_id id, struct mif_common_block *block; unsigned long int flags; va_list args; - int ret; spin_lock_irqsave(&msd->lock, flags); @@ -179,7 +182,7 @@ void _mif_com_log(enum mif_log_id id, block->time = get_kernel_time(); va_start(args, format); - ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); + vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); va_end(args); } @@ -209,13 +212,12 @@ void _mif_time_log(enum mif_log_id id, struct modem_shared *msd, /* dump2hex * dump data to hex as fast as possible. - * the length of @buf must be greater than "@len * 3" + * the length of @buff must be greater than "@len * 3" * it need 3 bytes per one data byte to print. */ -static inline int dump2hex(char *buf, const char *data, size_t len) +static inline int dump2hex(char *buff, const char *data, size_t len) { - static const char *hex = "0123456789abcdef"; - char *dest = buf; + char *dest = buff; int i; for (i = 0; i < len; i++) { @@ -228,24 +230,26 @@ static inline int dump2hex(char *buf, const char *data, size_t len) *dest = '\0'; - return dest - buf; + return dest - buff; } -int pr_ipc(const char *str, const char *data, size_t len) +void pr_ipc(int level, const char *tag, const char *data, size_t len) { - struct timeval tv; - struct tm date; - unsigned char hexstr[128]; + struct utc_time utc; + unsigned char str[128]; - do_gettimeofday(&tv); - time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); - - dump2hex(hexstr, data, (len > 40 ? 40 : len)); + if (level < 0) + return; - return pr_info("mif: %s: [%02d-%02d %02d:%02d:%02d.%03ld] %s\n", - str, date.tm_mon + 1, date.tm_mday, - date.tm_hour, date.tm_min, date.tm_sec, - (tv.tv_usec > 0 ? tv.tv_usec / 1000 : 0), hexstr); + get_utc_time(&utc); + dump2hex(str, data, (len > 32 ? 32 : len)); + if (level > 0) { + pr_err("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, + utc.hour, utc.min, utc.sec, utc.msec, tag, str); + } else { + pr_info("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, + utc.hour, utc.min, utc.sec, utc.msec, tag, str); + } } /* print buffer as hex string */ @@ -253,12 +257,12 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len) { size_t len = min(data_len, max_len); - unsigned char hexstr[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ - dump2hex(hexstr, data, len); + unsigned char str[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ + dump2hex(str, data, len); /* don't change this printk to mif_debug for print this as level7 */ - return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr, - len == data_len ? "" : " ..."); + return printk(KERN_INFO "%s: %s(%u): %s%s\n", MIF_TAG, tag, data_len, + str, (len == data_len) ? "" : " ..."); } /* flow control CM from CP, it use in serial devices */ @@ -285,7 +289,7 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) break; default: - mif_err("flowctl BACMD: %04X\n", *cmd); + mif_err("flowctl BAD CMD: %04X\n", *cmd); break; } } @@ -411,6 +415,42 @@ void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type) iodevs_for_each(msd, iodev_set_tx_link, ld); } +void mif_netif_stop(struct link_device *ld) +{ + struct io_device *iod; + + if (ld->ipc_version < SIPC_VER_50) + iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); + else + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + + if (iod) + iodevs_for_each(iod->msd, iodev_netif_stop, 0); +} + +void mif_netif_wake(struct link_device *ld) +{ + struct io_device *iod; + + /** + * If ld->suspend_netif_tx is true, this means that there was a SUSPEND + * flow control command from CP so MIF must wait for a RESUME command + * from CP. + */ + if (ld->suspend_netif_tx) { + mif_info("%s: waiting for FLOW_CTRL_RESUME\n", ld->name); + return; + } + + if (ld->ipc_version < SIPC_VER_50) + iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); + else + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + + if (iod) + iodevs_for_each(iod->msd, iodev_netif_wake, 0); +} + /** * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer) * @return: return zero when errors occurred @@ -447,7 +487,7 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, add_timer(timer); } -void mif_print_data(char *buf, int len) +void mif_print_data(const char *buff, int len) { int words = len >> 4; int residue = len - (words << 4); @@ -458,11 +498,8 @@ void mif_print_data(char *buf, int len) /* Make the last line, if ((len % 16) > 0) */ if (residue > 0) { - memset(last, 0, sizeof(last)); - memset(tb, 0, sizeof(tb)); - b = buf + (words << 4); - sprintf(last, "%04X: ", (words << 4)); + b = (char *)buff + (words << 4); for (i = 0; i < residue; i++) { sprintf(tb, "%02x ", b[i]); strcat(last, tb); @@ -474,7 +511,7 @@ void mif_print_data(char *buf, int len) } for (i = 0; i < words; i++) { - b = buf + (i << 4); + b = (char *)buff + (i << 4); mif_err("%04X: " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -488,13 +525,141 @@ void mif_print_data(char *buf, int len) mif_err("%s\n", last); } +void mif_dump2format16(const char *data, int len, char *buff, char *tag) +{ + char *d; + int i; + int words = len >> 4; + int residue = len - (words << 4); + char line[LINE_BUFF_SIZE]; + char tb[8]; + + for (i = 0; i < words; i++) { + memset(line, 0, LINE_BUFF_SIZE); + d = (char *)data + (i << 4); + + if (tag) + sprintf(line, "%s%04X| " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + tag, (i << 4), + d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], + d[12], d[13], d[14], d[15]); + else + sprintf(line, "%04X| " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + (i << 4), + d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], + d[12], d[13], d[14], d[15]); + + strcat(buff, line); + } + + /* Make the last line, if (len % 16) > 0 */ + if (residue > 0) { + memset(line, 0, LINE_BUFF_SIZE); + memset(tb, 0, sizeof(tb)); + d = (char *)data + (words << 4); + + if (tag) + sprintf(line, "%s%04X|", tag, (words << 4)); + else + sprintf(line, "%04X|", (words << 4)); + + for (i = 0; i < residue; i++) { + sprintf(tb, " %02x", d[i]); + strcat(line, tb); + if ((i & 0x3) == 0x3) { + sprintf(tb, " "); + strcat(line, tb); + } + } + strcat(line, "\n"); + + strcat(buff, line); + } +} + +void mif_dump2format4(const char *data, int len, char *buff, char *tag) +{ + char *d; + int i; + int words = len >> 2; + int residue = len - (words << 2); + char line[LINE_BUFF_SIZE]; + char tb[8]; + + for (i = 0; i < words; i++) { + memset(line, 0, LINE_BUFF_SIZE); + d = (char *)data + (i << 2); + + if (tag) + sprintf(line, "%s%04X| %02x %02x %02x %02x\n", + tag, (i << 2), d[0], d[1], d[2], d[3]); + else + sprintf(line, "%04X| %02x %02x %02x %02x\n", + (i << 2), d[0], d[1], d[2], d[3]); + + strcat(buff, line); + } + + /* Make the last line, if (len % 4) > 0 */ + if (residue > 0) { + memset(line, 0, LINE_BUFF_SIZE); + memset(tb, 0, sizeof(tb)); + d = (char *)data + (words << 2); + + if (tag) + sprintf(line, "%s%04X|", tag, (words << 2)); + else + sprintf(line, "%04X|", (words << 2)); + + for (i = 0; i < residue; i++) { + sprintf(tb, " %02x", d[i]); + strcat(line, tb); + } + strcat(line, "\n"); + + strcat(buff, line); + } +} + +void mif_print_dump(const char *data, int len, int width) +{ + char *buff; + + buff = kzalloc(len << 3, GFP_ATOMIC); + if (!buff) { + mif_err("ERR! kzalloc fail\n"); + return; + } + + if (width == 16) + mif_dump2format16(data, len, buff, LOG_TAG); + else + mif_dump2format4(data, len, buff, LOG_TAG); + + pr_info("%s", buff); + + kfree(buff); +} + void print_sipc4_hdlc_fmt_frame(const u8 *psrc) { u8 *frm; /* HDLC Frame */ struct fmt_hdr *hh; /* HDLC Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - u16 hh_len = sizeof(struct fmt_hdr); - u16 fh_len = sizeof(struct sipc_fmt_hdr); + int hh_len = sizeof(struct fmt_hdr); + int fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -531,7 +696,7 @@ void print_sipc4_hdlc_fmt_frame(const u8 *psrc) void print_sipc4_fmt_frame(const u8 *psrc) { struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)psrc; - u16 fh_len = sizeof(struct sipc_fmt_hdr); + int fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -558,8 +723,8 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) u8 *lf; /* Link Frame */ struct sipc5_link_hdr *lh; /* Link Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - u16 lh_len; - u16 fh_len; + int lh_len; + int fh_len; u8 *data; int dlen; @@ -567,10 +732,7 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) /* Point HDLC header and IPC header */ lh = (struct sipc5_link_hdr *)lf; - if (lh->cfg & SIPC5_CTL_FIELD_EXIST) - lh_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - lh_len = SIPC5_MIN_HEADER_SIZE; + lh_len = (u16)sipc5_get_hdr_len((u8 *)lh); fh = (struct sipc_fmt_hdr *)(lf + lh_len); fh_len = sizeof(struct sipc_fmt_hdr); @@ -601,8 +763,8 @@ static void strcat_tcp_header(char *buff, u8 *pkt) { struct tcphdr *tcph = (struct tcphdr *)pkt; int eol; - char line[LINE_BUFF_SIZE]; - char flag_str[32]; + char line[LINE_BUFF_SIZE] = {0, }; + char flag_str[32] = {0, }; /*------------------------------------------------------------------------- @@ -630,21 +792,17 @@ static void strcat_tcp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: TCP:: Src.Port %u, Dst.Port %u\n", - ntohs(tcph->source), ntohs(tcph->dest)); + "%s: TCP:: Src.Port %u, Dst.Port %u\n", + MIF_TAG, ntohs(tcph->source), ntohs(tcph->dest)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", - ntohs(tcph->seq), ntohs(tcph->seq), + "%s: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", + MIF_TAG, ntohs(tcph->seq), ntohs(tcph->seq), ntohs(tcph->ack_seq), ntohs(tcph->ack_seq)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); - memset(flag_str, 0, sizeof(flag_str)); if (tcph->cwr) strcat(flag_str, "CWR "); if (tcph->ece) @@ -664,12 +822,12 @@ static void strcat_tcp_header(char *buff, u8 *pkt) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str); + snprintf(line, LINE_BUFF_SIZE, "%s: TCP:: Flags {%s}\n", + MIF_TAG, flag_str); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n", + "%s: TCP:: Window %u, Checksum 0x%04X, Urgent %u\n", MIF_TAG, ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr)); strcat(buff, line); } @@ -677,7 +835,7 @@ static void strcat_tcp_header(char *buff, u8 *pkt) static void strcat_udp_header(char *buff, u8 *pkt) { struct udphdr *udph = (struct udphdr *)pkt; - char line[LINE_BUFF_SIZE]; + char line[LINE_BUFF_SIZE] = {0, }; /*------------------------------------------------------------------------- @@ -693,41 +851,39 @@ static void strcat_udp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: UDP:: Src.Port %u, Dst.Port %u\n", - ntohs(udph->source), ntohs(udph->dest)); + "%s: UDP:: Src.Port %u, Dst.Port %u\n", + MIF_TAG, ntohs(udph->source), ntohs(udph->dest)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: UDP:: Length %u, Checksum 0x%04X\n", - ntohs(udph->len), ntohs(udph->check)); + "%s: UDP:: Length %u, Checksum 0x%04X\n", + MIF_TAG, ntohs(udph->len), ntohs(udph->check)); strcat(buff, line); if (ntohs(udph->dest) == 53) { - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n"); + snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS query!!!\n", + MIF_TAG); strcat(buff, line); } if (ntohs(udph->source) == 53) { - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n"); + snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS response!!!\n", + MIF_TAG); strcat(buff, line); } } -void print_ip4_packet(u8 *ip_pkt, bool tx) +void print_ip4_packet(const u8 *ip_pkt, bool tx) { char *buff; struct iphdr *iph = (struct iphdr *)ip_pkt; - u8 *pkt = ip_pkt + (iph->ihl << 2); + u8 *pkt = (u8 *)ip_pkt + (iph->ihl << 2); u16 flags = (ntohs(iph->frag_off) & 0xE000); u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF); int eol; - char line[LINE_BUFF_SIZE]; - char flag_str[16]; + char line[LINE_BUFF_SIZE] = {0, }; + char flag_str[16] = {0, }; /*--------------------------------------------------------------------------- IPv4 Header Format @@ -764,32 +920,25 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) if (!buff) return; - - memset(line, 0, LINE_BUFF_SIZE); if (tx) snprintf(line, LINE_BUFF_SIZE, "\n%s\n", TX_SEPARATOR); else snprintf(line, LINE_BUFF_SIZE, "\n%s\n", RX_SEPARATOR); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", - iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len)); + "%s: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", + MIF_TAG, iph->version, (iph->ihl << 2), iph->tos, + ntohs(iph->tot_len)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: ID %u, Fragment Offset %u\n", - ntohs(iph->id), frag_off); + snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: ID %u, Fragment Offset %u\n", + MIF_TAG, ntohs(iph->id), frag_off); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); - memset(flag_str, 0, sizeof(flag_str)); if (flags & IP_CE) strcat(flag_str, "CE "); if (flags & IP_DF) @@ -799,19 +948,18 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str); + snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: Flags {%s}\n", + MIF_TAG, flag_str); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", - iph->ttl, iph->protocol, ntohs(iph->check)); + "%s: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", + MIF_TAG, iph->ttl, iph->protocol, ntohs(iph->check)); strcat(buff, line); - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, - "mif: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", - ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], + "%s: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", + MIF_TAG, ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]); strcat(buff, line); @@ -828,7 +976,6 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) break; } - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); @@ -837,7 +984,7 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) kfree(buff); } -bool is_dns_packet(u8 *ip_pkt) +bool is_dns_packet(const u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2)); @@ -852,7 +999,7 @@ bool is_dns_packet(u8 *ip_pkt) return false; } -bool is_syn_packet(u8 *ip_pkt) +bool is_syn_packet(const u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2)); @@ -867,124 +1014,248 @@ bool is_syn_packet(u8 *ip_pkt) return false; } -int memcmp16_to_io(const void __iomem *to, void *from, int size) +/** + * mif_register_isr + * @irq: IRQ number for a DPRAM interrupt + * @isr: function pointer to an interrupt service routine + * @flags: set of interrupt flags + * @name: name of the interrupt + * @data: pointer to a data for @isr + * + * Registers the ISR for the IRQ number. + */ +int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, + const char *name, void *data) { - u16 *d = (u16 *)to; - u16 *s = (u16 *)from; - int count = size >> 1; - int diff = 0; - int i; - u16 d1; - u16 s1; - - for (i = 0; i < count; i++) { - d1 = ioread16(d); - s1 = *s; - if (d1 != s1) { - diff++; - mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); - } - d++; - s++; + int ret; + + ret = request_irq(irq, isr, flags, name, data); + if (ret) { + mif_err("%s: ERR! request_irq fail (err %d)\n", name, ret); + return ret; } - return diff; + ret = enable_irq_wake(irq); + if (ret) + mif_err("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); + + mif_err("%s (#%d) handler registered\n", name, irq); + + return 0; } -int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size) +int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes) { - u8 __iomem *dst; - int i; + u16 i; + u16 words = bytes >> 1; + u16 __iomem *dst = (u16 __iomem *)start; u16 val; + int err_cnt = 0; - mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size); + mif_err("%s: start 0x%p, bytes %d\n", dp_name, start, bytes); - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16((i & 0xFFFF), dst); - dst += 2; + mif_err("%s: 0/6 stage ...\n", dp_name); + for (i = 1; i <= 100; i++) { + iowrite16(0x1234, dst); + val = ioread16(dst); + if (val != 0x1234) { + mif_err("%s: [0x0000] read 0x%04X != written 0x1234 " + "(try# %d)\n", dp_name, val, i); + err_cnt++; + } + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 1/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0, dst); + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); - if (val != (i & 0xFFFF)) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n", - dp_name, i, val, (i & 0xFFFF)); - return -EINVAL; + if (val != 0x0000) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x0000\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + mif_err("%s: 2/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0xFFFF, dst); + dst++; + } + + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + val = ioread16(dst); + if (val != 0xFFFF) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0xFFFF\n", dp_name, i, val); + err_cnt++; + } + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 3/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0x00FF, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0x00FF) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x00FF\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 4/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0x0FF0, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0x0FF0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x0FF0\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + mif_err("%s: 5/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0xFF00, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0xFF00) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0xFF00\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0, dst); - dst += 2; + mif_err("%s: 6/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16((i & 0xFFFF), dst); + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); - if (val != 0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0\n", - dp_name, i, val); - return -EINVAL; + if (val != (i & 0xFFFF)) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x%04X\n", dp_name, i, val, (i & 0xFFFF)); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: PASS!!!\n", dp_name); + + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0, dst); + dst++; } - mif_info("%s: PASS!!!\n", dp_name); return 0; } +struct file *mif_open_file(const char *path) +{ + struct file *fp; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + fp = filp_open(path, O_RDWR|O_CREAT|O_APPEND, 0666); + + set_fs(old_fs); + + if (IS_ERR(fp)) + return NULL; + + return fp; +} + +void mif_save_file(struct file *fp, const char *buff, size_t size) +{ + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + ret = fp->f_op->write(fp, buff, size, &fp->f_pos); + if (ret < 0) + mif_err("ERR! write fail\n"); + + set_fs(old_fs); +} + +void mif_close_file(struct file *fp) +{ + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + filp_close(fp, NULL); + + set_fs(old_fs); +} + diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h index 7225ec2..15edb43 100644 --- a/drivers/misc/modem_if/modem_utils.h +++ b/drivers/misc/modem_if/modem_utils.h @@ -16,6 +16,7 @@ #define __MODEM_UTILS_H__ #include +#include "modem_prj.h" #define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type)) @@ -26,6 +27,8 @@ #define MAX_IPC_SKB_SIZE 4096 #define MAX_LOG_SIZE 64 +#define MIF_TAG "mif" + #define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE) #define MIF_ID_SIZE sizeof(enum mif_log_id) @@ -96,7 +99,19 @@ struct mif_time_block { char buff[MAX_TIM_LOG_SIZE]; }; -int mif_dump_dpram(struct io_device *); +static inline int ns2us(long ns) +{ + return (ns > 0) ? (ns / 1000) : 0; +} + +static inline int ns2ms(long ns) +{ + return (ns > 0) ? (ns / 1000000) : 0; +} + +void ts2utc(struct timespec *ts, struct utc_time *utc); +void get_utc_time(struct utc_time *utc); + int mif_dump_log(struct modem_shared *, struct io_device *); #define mif_irq_log(msd, map, data, len) \ @@ -141,7 +156,7 @@ static inline unsigned int countbits(unsigned int n) } /* print IPC message as hex string with UTC time */ -int pr_ipc(const char *str, const char *data, size_t len); +void pr_ipc(int level, const char *tag, const char *data, size_t len); /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, @@ -156,10 +171,13 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, pr_buffer(tag, (char *)((urb)->transfer_buffer), \ (size_t)((urb)->actual_length), (size_t)16) +/* Stop/wake all TX queues in network interfaces */ +void mif_netif_stop(struct link_device *ld); +void mif_netif_wake(struct link_device *ld); + /* flow control CMD from CP, it use in serial devices */ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len); - /* get iod from tree functions */ struct io_device *get_iod_with_format(struct modem_shared *msd, @@ -204,12 +222,14 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, void (*function)(unsigned long), unsigned long data); /* debug helper functions for sipc4, sipc5 */ -void mif_print_data(char *buf, int len); +void mif_print_data(const char *buff, int len); +void mif_dump2format16(const char *data, int len, char *buff, char *tag); +void mif_dump2format4(const char *data, int len, char *buff, char *tag); +void mif_print_dump(const char *data, int len, int width); void print_sipc4_hdlc_fmt_frame(const u8 *psrc); void print_sipc4_fmt_frame(const u8 *psrc); void print_sipc5_link_fmt_frame(const u8 *psrc); - /*--------------------------------------------------------------------------- IPv4 Header Format @@ -282,23 +302,17 @@ void print_sipc5_link_fmt_frame(const u8 *psrc); -------------------------------------------------------------------------*/ #define UDP_HDR_SIZE 8 -void print_ip4_packet(u8 *ip_pkt, bool tx); -bool is_dns_packet(u8 *ip_pkt); -bool is_syn_packet(u8 *ip_pkt); +void print_ip4_packet(const u8 *ip_pkt, bool tx); +bool is_dns_packet(const u8 *ip_pkt); +bool is_syn_packet(const u8 *ip_pkt); -int memcmp16_to_io(const void __iomem *to, void *from, int size); -int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size); +int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, + const char *name, void *data); +int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes); -static const inline char *get_dev_name(int dev) -{ - if (dev == IPC_FMT) - return "FMT"; - else if (dev == IPC_RAW) - return "RAW"; - else if (dev == IPC_RFS) - return "RFS"; - else - return "NONE"; -} +struct file *mif_open_file(const char *path); +void mif_save_file(struct file *fp, const char *buff, size_t size); +void mif_close_file(struct file *fp); #endif/*__MODEM_UTILS_H__*/ + diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h index b5ec61b..b314bb2 100644 --- a/drivers/misc/modem_if/modem_variation.h +++ b/drivers/misc/modem_if/modem_variation.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -16,7 +15,7 @@ #ifndef __MODEM_VARIATION_H__ #define __MODEM_VARIATION_H__ -#include +#include "modem.h" #define DECLARE_MODEM_INIT(type) \ int type ## _init_modemctl_device(struct modem_ctl *mc, \ @@ -61,12 +60,30 @@ DECLARE_MODEM_INIT(cbp72); DECLARE_MODEM_INIT_DUMMY(cbp72) #endif +#ifdef CONFIG_CDMA_MODEM_CBP82 +DECLARE_MODEM_INIT(cbp82); +#else +DECLARE_MODEM_INIT_DUMMY(cbp82) +#endif + +#ifdef CONFIG_LTE_MODEM_CMC220 +DECLARE_MODEM_INIT(cmc220); +#else +DECLARE_MODEM_INIT_DUMMY(cmc220) +#endif + #ifdef CONFIG_LTE_MODEM_CMC221 DECLARE_MODEM_INIT(cmc221); #else DECLARE_MODEM_INIT_DUMMY(cmc221) #endif +#ifdef CONFIG_UMTS_MODEM_SS222 +DECLARE_MODEM_INIT(ss222); +#else +DECLARE_MODEM_INIT_DUMMY(ss222) +#endif + #ifdef CONFIG_CDMA_MODEM_MDM6600 DECLARE_MODEM_INIT(mdm6600); #else @@ -79,6 +96,12 @@ DECLARE_MODEM_INIT(esc6270); DECLARE_MODEM_INIT_DUMMY(esc6270) #endif +#ifdef CONFIG_CDMA_MODEM_QSC6085 +DECLARE_MODEM_INIT(qsc6085); +#else +DECLARE_MODEM_INIT_DUMMY(qsc6085) +#endif + #ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 DECLARE_MODEM_INIT(sprd8803); #else @@ -94,6 +117,18 @@ DECLARE_LINK_INIT(mipi); DECLARE_LINK_INIT_DUMMY(mipi) #endif +#ifdef CONFIG_LINK_DEVICE_USB +DECLARE_LINK_INIT(usb); +#else +DECLARE_LINK_INIT_DUMMY(usb) +#endif + +#ifdef CONFIG_LINK_DEVICE_HSIC +DECLARE_LINK_INIT(hsic); +#else +DECLARE_LINK_INIT_DUMMY(hsic) +#endif + #ifdef CONFIG_LINK_DEVICE_DPRAM DECLARE_LINK_INIT(dpram); #else @@ -106,53 +141,52 @@ DECLARE_LINK_INIT(pld); DECLARE_LINK_INIT_DUMMY(pld) #endif -#ifdef CONFIG_LINK_DEVICE_SPI -DECLARE_LINK_INIT(spi); -#else -DECLARE_LINK_INIT_DUMMY(spi) -#endif - -#ifdef CONFIG_LINK_DEVICE_USB -DECLARE_LINK_INIT(usb); +#ifdef CONFIG_LINK_DEVICE_C2C +DECLARE_LINK_INIT(c2c); #else -DECLARE_LINK_INIT_DUMMY(usb) +DECLARE_LINK_INIT_DUMMY(c2c) #endif -#ifdef CONFIG_LINK_DEVICE_HSIC -DECLARE_LINK_INIT(hsic); +#ifdef CONFIG_LINK_DEVICE_SHMEM +DECLARE_LINK_INIT(shmem); #else -DECLARE_LINK_INIT_DUMMY(hsic) +DECLARE_LINK_INIT_DUMMY(shmem) #endif -#ifdef CONFIG_LINK_DEVICE_C2C -DECLARE_LINK_INIT(c2c); +#ifdef CONFIG_LINK_DEVICE_SPI +DECLARE_LINK_INIT(spi); #else -DECLARE_LINK_INIT_DUMMY(c2c) +DECLARE_LINK_INIT_DUMMY(spi) #endif typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *); -static modem_init_call modem_init_func[] = { - MODEM_INIT_CALL(xmm6260), - MODEM_INIT_CALL(xmm6262), - MODEM_INIT_CALL(cbp71), - MODEM_INIT_CALL(cbp72), - MODEM_INIT_CALL(cmc221), - MODEM_INIT_CALL(mdm6600), - MODEM_INIT_CALL(esc6270), - MODEM_INIT_CALL(sprd8803), - MODEM_INIT_CALL(dummy), +static modem_init_call modem_init_func[MAX_MODEM_TYPE] = { + [IMC_XMM6260] = MODEM_INIT_CALL(xmm6260), + [IMC_XMM6262] = MODEM_INIT_CALL(xmm6262), + [VIA_CBP71] = MODEM_INIT_CALL(cbp71), + [VIA_CBP72] = MODEM_INIT_CALL(cbp72), + [VIA_CBP82] = MODEM_INIT_CALL(cbp82), + [SEC_CMC220] = MODEM_INIT_CALL(cmc220), + [SEC_CMC221] = MODEM_INIT_CALL(cmc221), + [SEC_SS222] = MODEM_INIT_CALL(ss222), + [QC_MDM6600] = MODEM_INIT_CALL(mdm6600), + [QC_ESC6270] = MODEM_INIT_CALL(esc6270), + [QC_QSC6085] = MODEM_INIT_CALL(qsc6085), + [SPRD_SC8803] = MODEM_INIT_CALL(sprd8803), + [DUMMY] = MODEM_INIT_CALL(dummy), }; typedef struct link_device *(*link_init_call)(struct platform_device *); -static link_init_call link_init_func[] = { - LINK_INIT_CALL(undefined), - LINK_INIT_CALL(mipi), - LINK_INIT_CALL(dpram), - LINK_INIT_CALL(spi), - LINK_INIT_CALL(usb), - LINK_INIT_CALL(hsic), - LINK_INIT_CALL(c2c), - LINK_INIT_CALL(pld), +static link_init_call link_init_func[LINKDEV_MAX] = { + [LINKDEV_UNDEFINED] = LINK_INIT_CALL(undefined), + [LINKDEV_MIPI] = LINK_INIT_CALL(mipi), + [LINKDEV_USB] = LINK_INIT_CALL(usb), + [LINKDEV_HSIC] = LINK_INIT_CALL(hsic), + [LINKDEV_DPRAM] = LINK_INIT_CALL(dpram), + [LINKDEV_PLD] = LINK_INIT_CALL(pld), + [LINKDEV_C2C] = LINK_INIT_CALL(c2c), + [LINKDEV_SHMEM] = LINK_INIT_CALL(shmem), + [LINKDEV_SPI] = LINK_INIT_CALL(spi), }; static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata) diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c index 28f95f7..da1ea04 100644 --- a/drivers/misc/modem_if/sipc4_io_device.c +++ b/drivers/misc/modem_if/sipc4_io_device.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_io_device.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -26,10 +24,7 @@ #include #include -#include -#ifdef CONFIG_LINK_DEVICE_C2C -#include -#endif +#include "modem.h" #include "modem_prj.h" #include "modem_utils.h" @@ -40,7 +35,8 @@ * So, give restriction to allocation size below 1 page to prevent * big pages broken. */ -#define MAX_RXDATA_SIZE 0x0E00 /* 4 * 1024 - 512 */ +#define MAX_RXDATA_SIZE (4096 - 512) +#define MAX_BOOTDATA_SIZE 0x4008 /* EBL package format*/ #define MAX_MULTI_FMT_SIZE 0x4000 /* 16 * 1024 */ static const char hdlc_start[1] = { HDLC_START }; @@ -250,22 +246,6 @@ static int rx_hdlc_head_check(struct io_device *iod, struct link_device *ld, hdr->start = HDLC_START; hdr->len = 0; - /* debug print */ - switch (iod->format) { - case IPC_FMT: - case IPC_RAW: - case IPC_MULTI_RAW: - case IPC_RFS: - /* TODO: print buf... */ - break; - - case IPC_CMD: - case IPC_BOOT: - case IPC_RAMDUMP: - default: - break; - } - buf += len; done_len += len; rest -= len; /* rest, call by value */ @@ -409,12 +389,6 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, rcvd); - print_sipc4_fmt_frame(data); - mif_err("\n"); -#endif skb_queue_tail(&iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -449,12 +423,6 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_info("The Last (ID %d, %d bytes received)\n", id, skb->len); -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_fmt_frame(skb->data); - mif_err("\n"); -#endif skb_queue_tail(&iod->sk_rx_q, skb); iod->skb[id] = NULL; mif_info("wake up wq of %s\n", iod->name); @@ -491,12 +459,6 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, rcvd); - print_sipc4_fmt_frame(data); - mif_err("\n"); -#endif skb_queue_tail(&real_iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -530,12 +492,6 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_err("The Last (ID %d, %d bytes received)\n", id, skb->len); -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_fmt_frame(skb->data); - mif_err("\n"); -#endif skb_queue_tail(&real_iod->sk_rx_q, skb); real_iod->skb[id] = NULL; mif_info("wake up wq of %s\n", real_iod->name); @@ -819,55 +775,6 @@ exit: return err; } -static int rx_rfs_packet(struct io_device *iod, struct link_device *ld, - const char *data, unsigned size) -{ - int err = 0; - int pad = 0; - int rcvd = 0; - struct sk_buff *skb; - - if (data[0] != HDLC_START) { - mif_err("Dropping RFS packet ... " - "size = %d, start = %02X %02X %02X %02X\n", - size, - data[0], data[1], data[2], data[3]); - return -EINVAL; - } - - if (data[size-1] != HDLC_END) { - for (pad = 1; pad < 4; pad++) - if (data[(size-1)-pad] == HDLC_END) - break; - - if (pad >= 4) { - char *b = (char *)data; - unsigned sz = size; - mif_err("size %d, No END_FLAG!!!\n", size); - mif_err("end = %02X %02X %02X %02X\n", - b[sz-4], b[sz-3], b[sz-2], b[sz-1]); - return -EINVAL; - } else { - mif_info("padding = %d\n", pad); - } - } - - skb = rx_alloc_skb(size, iod, ld); - if (unlikely(!skb)) { - mif_err("alloc_skb fail\n"); - return -ENOMEM; - } - - /* copy the RFS haeder to skb->data */ - rcvd = size - sizeof(hdlc_start) - sizeof(hdlc_end) - pad; - memcpy(skb_put(skb, rcvd), ((char *)data + sizeof(hdlc_start)), rcvd); - - fragdata(iod, ld)->skb_recv = skb; - err = rx_iodev_skb(fragdata(iod, ld)->skb_recv); - - return err; -} - /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) @@ -891,14 +798,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, */ switch (iod->format) { - case IPC_RFS: -#ifdef CONFIG_IPC_CMC22x_OLD_RFS - err = rx_rfs_packet(iod, ld, data, len); - return err; -#endif - case IPC_FMT: case IPC_RAW: + case IPC_RFS: case IPC_MULTI_RAW: if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); @@ -961,12 +863,17 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - iod->mc->phone_state = state; - mif_err("modem state changed. (iod: %s, state: %d)\n", - iod->name, state); + struct modem_ctl *mc = iod->mc; + int old_state = mc->phone_state; - if ((state == STATE_CRASH_RESET) || (state == STATE_CRASH_EXIT) - || (state == STATE_NV_REBUILDING)) + if (old_state != state) { + mc->phone_state = state; + mif_err("%s state changed (%s -> %s)\n", mc->name, + get_cp_state_str(old_state), get_cp_state_str(state)); + } + + if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || + state == STATE_NV_REBUILDING) wake_up(&iod->wq); } @@ -977,10 +884,24 @@ static void io_dev_modem_state_changed(struct io_device *iod, */ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) { + +#if defined(CONFIG_MACH_KONA) && defined(CONFIG_UMTS_MODEM_XMM6262) + mif_err("modem_current_state is %d\n", iod->mc->phone_state); +#endif + if (atomic_read(&iod->opened) == 0) { - mif_err("iod is not opened: %s\n", - iod->name); - } else if (iod->mc->sim_state.online == sim_online) { + mif_err("iod is not opened: %s\n", iod->name); + /* update latest sim status */ + iod->mc->sim_state.online = sim_online; + } +#if defined(CONFIG_LINK_DEVICE_HSIC) && defined(CONFIG_UMTS_MODEM_XMM6262) // fixed modem unknown issue (kina 3G) + else if (iod->mc->phone_state == STATE_BOOTING) { + mif_err("modem_current_state is STATE_BOOTING\n"); + /* update latest sim status */ + iod->mc->sim_state.online = sim_online; + } +#endif + else if (iod->mc->sim_state.online == sim_online) { mif_err("sim state not changed.\n"); } else { iod->mc->sim_state.online = sim_online; @@ -995,6 +916,7 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) } } + static int misc_open(struct inode *inode, struct file *filp) { struct io_device *iod = to_io_device(filp->private_data); @@ -1078,32 +1000,32 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case IOCTL_MODEM_ON: - mif_debug("misc_ioctl : IOCTL_MODEM_ON\n"); + mif_debug("%s: IOCTL_MODEM_ON\n", iod->name); return iod->mc->ops.modem_on(iod->mc); case IOCTL_MODEM_OFF: - mif_debug("misc_ioctl : IOCTL_MODEM_OFF\n"); + mif_debug("%s: IOCTL_MODEM_OFF\n", iod->name); return iod->mc->ops.modem_off(iod->mc); case IOCTL_MODEM_RESET: - mif_debug("misc_ioctl : IOCTL_MODEM_RESET\n"); + mif_debug("%s: IOCTL_MODEM_RESET\n", iod->name); return iod->mc->ops.modem_reset(iod->mc); case IOCTL_MODEM_BOOT_ON: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_ON\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); return iod->mc->ops.modem_boot_on(iod->mc); case IOCTL_MODEM_BOOT_OFF: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_OFF\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); return iod->mc->ops.modem_boot_off(iod->mc); /* TODO - will remove this command after ril updated */ case IOCTL_MODEM_BOOT_DONE: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); return 0; case IOCTL_MODEM_STATUS: - mif_debug("misc_ioctl : IOCTL_MODEM_STATUS\n"); + mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); p_state = iod->mc->phone_state; if ((p_state == STATE_CRASH_RESET) || @@ -1126,7 +1048,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return p_state; case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_SUSPEND\n"); + mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1134,8 +1056,16 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) iodevs_for_each(iod->msd, iodev_netif_stop, 0); return 0; + case IOCTL_MODEM_DL_START: + mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); + return ld->dload_start(ld, iod); + + case IOCTL_MODEM_FW_UPDATE: + mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); + return ld->firm_update(ld, iod, arg); + case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_RESUME\n"); + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1144,21 +1074,29 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_START: - mif_err("misc_ioctl : IOCTL_MODEM_DUMP_START\n"); + mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + + case IOCTL_MODEM_RAMDUMP_START: + mif_err("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); return ld->dump_start(ld, iod); case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("misc_ioctl : IOCTL_MODEM_DUMP_UPDATE\n"); + mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); return ld->dump_update(ld, iod, arg); + case IOCTL_MODEM_RAMDUMP_STOP: + mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); + return ld->dump_finish(ld, iod, arg); + case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_debug("misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n"); + mif_debug("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); if (iod->mc->ops.modem_force_crash_exit) return iod->mc->ops.modem_force_crash_exit(iod->mc); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: - mif_err("misc_ioctl : IOCTL_MODEM_CP_UPLOAD\n"); + mif_err("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), (void __user *)arg, MAX_CPINFO_SIZE) != 0) panic("CP Crash"); @@ -1167,12 +1105,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_RESET: - mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n"); + mif_err("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); return iod->mc->ops.modem_dump_reset(iod->mc); #if defined(CONFIG_SEC_DUAL_MODEM_MODE) case IOCTL_MODEM_SWITCH_MODEM: - mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n"); + mif_err("%s: IOCTL_MODEM_SWITCH_MODEM\n", iod->name); iod->mc->phone_state = STATE_MODEM_SWITCH; wake_up(&iod->wq); return 0; @@ -1188,20 +1126,6 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_dump_log(iod->mc->msd, iod); return 0; - case IOCTL_MIF_DPRAM_DUMP: -#ifdef CONFIG_LINK_DEVICE_DPRAM - if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { - size = iod->mc->mdm_data->dpram_ctl->dp_size; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) - return -EFAULT; - mif_dump_dpram(iod); - return 0; - } -#endif - return -EINVAL; - default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1209,12 +1133,48 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_err("misc_ioctl : ioctl 0x%X is not defined.\n", cmd); + mif_err("%s: ioctl 0x%X is not defined\n", iod->name, cmd); return -EINVAL; } return 0; } +static size_t _boot_write(struct io_device *iod, const char __user *buf, + size_t count) +{ + int rest_len = count, frame_len = 0; + char *cur = (char *)buf; + struct sk_buff *skb = NULL; + struct link_device *ld = get_current_link(iod); + int ret; + + while (rest_len) { + frame_len = min(rest_len, MAX_BOOTDATA_SIZE); + skb = alloc_skb(frame_len, GFP_KERNEL); + if (!skb) { + mif_err("fail alloc skb (%d)\n", __LINE__); + return -ENOMEM; + } + if (copy_from_user( + skb_put(skb, frame_len), cur, frame_len) != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + rest_len -= frame_len; + cur += frame_len; + + skbpriv(skb)->iod = iod; + skbpriv(skb)->ld = ld; + + ret = ld->send(ld, iod, skb); + if (ret < 0) { + dev_kfree_skb_any(skb); + return ret; + } + } + return count; +} + static ssize_t misc_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { @@ -1237,6 +1197,10 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb = alloc_skb(frame_len, GFP_KERNEL); if (!skb) { + if (frame_len > MAX_BOOTDATA_SIZE && iod->format == IPC_BOOT) { + mif_info("large alloc fail\n"); + return _boot_write(iod, buf, count); + } mif_err("fail alloc skb (%d)\n", __LINE__); return -ENOMEM; } @@ -1278,31 +1242,6 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb_put(skb, calc_padding_size(iod, ld, skb->len)); -#if 0 - if (iod->format == IPC_FMT) { - mif_err("\n<%s> Tx HDLC FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_hdlc_fmt_frame(skb->data); - mif_err("\n"); - } -#endif -#if 0 - if (iod->format == IPC_RAW) { - mif_err("\n<%s> Tx HDLC RAW frame (len %d)\n", - iod->name, skb->len); - mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); - mif_err("\n"); - } -#endif -#if 0 - if (iod->format == IPC_RFS) { - mif_err("\n<%s> Tx HDLC RFS frame (len %d)\n", - iod->name, skb->len); - mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); - mif_err("\n"); - } -#endif - /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data */ @@ -1409,43 +1348,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return pktsize; } -#ifdef CONFIG_LINK_DEVICE_C2C -static int misc_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int r = 0; - unsigned long size = 0; - unsigned long pfn = 0; - unsigned long offset = 0; - struct io_device *iod = (struct io_device *)filp->private_data; - - if (!vma) - return -EFAULT; - - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { - mif_err("offset + size > C2C_CP_RGN_SIZE\n"); - return -EINVAL; - } - - /* Set the noncacheable property to the region */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_RESERVED | VM_IO; - - pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); - r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); - if (r) { - mif_err("Failed in remap_pfn_range()!!!\n"); - return -EAGAIN; - } - - mif_err("VA = 0x%08lx, offset = 0x%lx, size = %lu\n", - vma->vm_start, offset, size); - - return 0; -} -#endif - static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1454,9 +1356,6 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, -#ifdef CONFIG_LINK_DEVICE_C2C - .mmap = misc_mmap, -#endif }; static int vnet_open(struct net_device *ndev) diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c index 59e2de9..b4f9c2a 100644 --- a/drivers/misc/modem_if/sipc4_modem.c +++ b/drivers/misc/modem_if/sipc4_modem.c @@ -32,7 +32,7 @@ #include #include -#include +#include "modem.h" #include "modem_prj.h" #include "modem_variation.h" #include "modem_utils.h" @@ -328,9 +328,22 @@ static int modem_resume(struct device *pdev) return 0; } +#ifdef CONFIG_FAST_BOOT +static void modem_complete(struct device *pdev) +{ + struct modem_ctl *mc = dev_get_drvdata(pdev); + + if (mc->modem_complete) + mc->modem_complete(mc); +} +#endif + static const struct dev_pm_ops modem_pm_ops = { .suspend = modem_suspend, .resume = modem_resume, +#ifdef CONFIG_FAST_BOOT + .complete = modem_complete, +#endif }; static struct platform_driver modem_driver = { diff --git a/drivers/misc/modem_if/sipc5_common.c b/drivers/misc/modem_if/sipc5_common.c new file mode 100644 index 0000000..e8574c2 --- /dev/null +++ b/drivers/misc/modem_if/sipc5_common.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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 "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" + +/** + * sipc5_start_valid + * @cfg: configuration field of an SIPC5 link frame + * + * Returns TRUE if the start (configuration field) of an SIPC5 link frame + * is valid or returns FALSE if it is not valid. + * + */ +bool sipc5_start_valid(u8 *frm) +{ + return (*frm & SIPC5_START_MASK) == SIPC5_START_MASK; +} + +bool sipc5_padding_exist(u8 *frm) +{ + return (*frm & SIPC5_PADDING_EXIST) ? true : false; +} + +bool sipc5_multi_frame(u8 *frm) +{ + return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_CTL_FIELD_MASK; +} + +bool sipc5_ext_len(u8 *frm) +{ + return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_EXT_LENGTH_MASK; +} + +/** + * sipc5_get_hdr_len + * @cfg: configuration field of an SIPC5 link frame + * + * Returns the length of SIPC5 link layer header in an SIPC5 link frame + * + */ +int sipc5_get_hdr_len(u8 *frm) +{ + if (*frm & SIPC5_EXT_FIELD_EXIST) { + if (*frm & SIPC5_CTL_FIELD_EXIST) + return SIPC5_HEADER_SIZE_WITH_CTL_FLD; + else + return SIPC5_HEADER_SIZE_WITH_EXT_LEN; + } else { + return SIPC5_MIN_HEADER_SIZE; + } +} + +/** + * sipc5_get_ch_id + * @frm: pointer to an SIPC5 frame + * + * Returns the channel ID in an SIPC5 link frame + * + */ +u8 sipc5_get_ch_id(u8 *frm) +{ + return *(frm + SIPC5_CH_ID_OFFSET); +} + +/** + * sipc5_get_ctrl_field + * @frm: pointer to an SIPC5 frame + * + * Returns the control field in an SIPC5 link frame + * + */ +u8 sipc5_get_ctrl_field(u8 *frm) +{ + return *(frm + SIPC5_CTL_OFFSET); +} + +/** + * sipc5_get_frame_len + * @frm: pointer to an SIPC5 link frame + * + * Returns the length of an SIPC5 link frame + * + */ +int sipc5_get_frame_len(u8 *frm) +{ + u8 cfg = frm[0]; + u16 *sz16 = (u16 *)(frm + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(frm + SIPC5_LEN_OFFSET); + + if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { + if (cfg & SIPC5_CTL_FIELD_EXIST) + return (int)(*sz16); + else + return (int)(*sz32); + } else { + return (int)(*sz16); + } +} + +/** + * sipc5_calc_padding_size + * @len: length of an SIPC5 link frame + * + * Returns the padding size for an SIPC5 link frame + * + */ +int sipc5_calc_padding_size(int len) +{ + int residue = len & 0x3; + return residue ? (4 - residue) : 0; +} + +/** + * sipc5_get_total_len + * @frm: pointer to an SIPC5 link frame + * + * Returns the total length of an SIPC5 link frame with padding + * + */ +int sipc5_get_total_len(u8 *frm) +{ + int len = sipc5_get_frame_len(frm); + int pad = sipc5_padding_exist(frm) ? sipc5_calc_padding_size(len) : 0; + return len + pad; +} + +/** + * sipc5_build_config + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @count: length of the data to be transmitted + * + * Builds a config value for an SIPC5 link frame header + * + * Returns the config value for the header or 0 for non-SIPC formats + */ +u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count) +{ + u8 cfg = SIPC5_START_MASK; + + if (iod->format > IPC_MULTI_RAW && iod->id == 0) + return 0; + + if (ld->aligned) + cfg |= SIPC5_PADDING_EXIST; + +#if 0 + if ((count + SIPC5_MIN_HEADER_SIZE) > ld->mtu[dev]) + cfg |= SIPC5_CTL_FIELD_MASK; + else +#endif + if ((count + SIPC5_MIN_HEADER_SIZE) > 0xFFFF) + cfg |= SIPC5_EXT_LENGTH_MASK; + + return cfg; +} + +/** + * sipc5_build_header + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @buff: pointer to a buffer in which an SIPC5 link frame header will be stored + * @cfg: value for the config field in the header + * @ctrl: value for the control field in the header + * @count: length of data in the SIPC5 link frame to be transmitted + * + * Builds the link layer header of an SIPC5 frame + */ +void sipc5_build_header(struct io_device *iod, struct link_device *ld, + u8 *buff, u8 cfg, u8 ctrl, u32 count) +{ + u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + u32 hdr_len = sipc5_get_hdr_len(&cfg); + + /* Store the config field and the channel ID field */ + buff[SIPC5_CONFIG_OFFSET] = cfg; + buff[SIPC5_CH_ID_OFFSET] = iod->id; + + /* Store the frame length field */ + if (sipc5_ext_len(buff)) + *sz32 = (u32)(hdr_len + count); + else + *sz16 = (u16)(hdr_len + count); + + /* Store the control field */ + if (sipc5_multi_frame(buff)) + buff[SIPC5_CTL_OFFSET] = ctrl; +} + +/** + * std_udl_get_cmd + * @frm: pointer to an SIPC5 link frame + * + * Returns the standard BOOT/DUMP (STD_UDL) command in an SIPC5 BOOT/DUMP frame. + */ +u32 std_udl_get_cmd(u8 *frm) +{ + u8 *cmd = frm + sipc5_get_hdr_len(frm); + return *((u32 *)cmd); +} + +/** + * std_udl_with_payload + * @cmd: standard BOOT/DUMP command + * + * Returns true if the STD_UDL command has a payload. + */ +bool std_udl_with_payload(u32 cmd) +{ + u32 mask = cmd & STD_UDL_STEP_MASK; + return (mask && mask < STD_UDL_CRC) ? true : false; +} + diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c index a9932c1..71596ae 100644 --- a/drivers/misc/modem_if/sipc5_io_device.c +++ b/drivers/misc/modem_if/sipc5_io_device.c @@ -1,5 +1,4 @@ -/* /linux/drivers/misc/modem_if/sipc5_io_device.c - * +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -24,11 +23,9 @@ #include #include #include +#include -#include -#ifdef CONFIG_LINK_DEVICE_C2C -#include -#endif +#include "modem.h" #include "modem_prj.h" #include "modem_utils.h" @@ -104,7 +101,7 @@ static void iodev_showtxlink(struct io_device *iod, void *args) struct link_device *ld = get_current_link(iod); if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) - *p += sprintf(*p, "%s: %s\n", iod->name, ld->name); + *p += sprintf(*p, "%s<->%s\n", iod->name, ld->name); } static ssize_t show_txlink(struct device *dev, @@ -130,181 +127,107 @@ static ssize_t store_txlink(struct device *dev, static struct device_attribute attr_txlink = __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink); -/** - * rx_check_frame_cfg - * @cfg: configuration field of a link layer header - * @frm: pointer to the sipc5_frame_data buffer - * - * 1) Checks whether or not an extended field exists - * 2) Calculates the length of a link layer header - * - * Returns the size of a link layer header - * - * Must be invoked only when the configuration field of the link layer header - * is validated with sipc5_start_valid() function - */ -static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm) +static int netif_flow_ctrl(struct link_device *ld, struct sk_buff *skb) { - frm->config = cfg; - - if (likely(cfg & SIPC5_PADDING_EXIST)) - frm->padding = true; - - if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { - if (cfg & SIPC5_CTL_FIELD_EXIST) { - frm->ctl_fld = true; - frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; - } else { - frm->ext_len = true; - frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN; - } + u8 cmd = skb->data[0]; + + if (cmd == FLOW_CTRL_SUSPEND) { + if (ld->suspend_netif_tx) + goto exit; + ld->suspend_netif_tx = true; + mif_netif_stop(ld); + mif_info("%s: FLOW_CTRL_SUSPEND\n", ld->name); + } else if (cmd == FLOW_CTRL_RESUME) { + if (!ld->suspend_netif_tx) + goto exit; + ld->suspend_netif_tx = false; + mif_netif_wake(ld); + mif_info("%s: FLOW_CTRL_RESUME\n", ld->name); } else { - frm->hdr_len = SIPC5_MIN_HEADER_SIZE; + mif_info("%s: ERR! invalid command %02X\n", ld->name, cmd); } - return frm->hdr_len; -} - -/** - * rx_build_meta_data - * @ld: pointer to the link device - * @frm: pointer to the sipc5_frame_data buffer - * - * Fills each field of sipc5_frame_data from a link layer header - * 1) Extracts the channel ID - * 2) Calculates the length of a link layer frame - * 3) Extracts a control field if exists - * 4) Calculates the length of an IPC message packet in the link layer frame - * - */ -static void rx_build_meta_data(struct link_device *ld, - struct sipc5_frame_data *frm) -{ - u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET); - - frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET]; - - if (unlikely(frm->ext_len)) - frm->len = *sz32; - else - frm->len = *sz16; - - if (unlikely(frm->ctl_fld)) - frm->control = frm->hdr[SIPC5_CTL_OFFSET]; - - frm->data_len = frm->len - frm->hdr_len; - - mif_debug("%s: FRM ch:%d len:%d ctl:%02X data.len:%d\n", - ld->name, frm->ch_id, frm->len, frm->control, frm->data_len); +exit: + dev_kfree_skb_any(skb); + return 0; } -/** - * tx_build_link_header - * @frm: pointer to the sipc5_frame_data buffer - * @iod: pointer to the IO device - * @ld: pointer to the link device - * @count: length of the data to be transmitted - * - * Builds the meta data for an SIPC5 frame and the link layer header of it - * Returns the link layer header length for an SIPC5 frame or 0 for other frame - */ -static unsigned tx_build_link_header(struct sipc5_frame_data *frm, - struct io_device *iod, struct link_device *ld, ssize_t count) +static inline int queue_skb_to_iod(struct sk_buff *skb, struct io_device *iod) { - u8 *buff = frm->hdr; - u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + struct sk_buff_head *rxq = &iod->sk_rx_q; - memset(frm, 0, sizeof(struct sipc5_frame_data)); + skb_queue_tail(rxq, skb); - if (iod->format == IPC_CMD || - iod->format == IPC_BOOT || - iod->format == IPC_RAMDUMP) { - frm->len = count; + if (iod->format < IPC_MULTI_RAW && rxq->qlen > MAX_IOD_RXQ_LEN) { + struct sk_buff *victim = skb_dequeue(rxq); + mif_err("%s: %s application may be dead (rxq->qlen %d > %d)\n", + iod->name, iod->app ? iod->app : "corresponding", + rxq->qlen, MAX_IOD_RXQ_LEN); + if (victim) + dev_kfree_skb_any(victim); + return -ENOSPC; + } else { + mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); return 0; } +} - frm->config = SIPC5_START_MASK; +static int rx_drain(struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} - if (iod->format == IPC_FMT && count > 2048) { - frm->ctl_fld = true; - frm->config |= SIPC5_EXT_FIELD_EXIST; - frm->config |= SIPC5_CTL_FIELD_EXIST; - } +static int rx_loopback(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = skbpriv(skb)->ld; + int ret; - if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) { - frm->ext_len = true; - frm->config |= SIPC5_EXT_FIELD_EXIST; + ret = ld->send(ld, iod, skb); + if (ret < 0) { + mif_err("%s->%s: ERR! ld->send fail (err %d)\n", + iod->name, ld->name, ret); } - if (ld->aligned) - frm->config |= SIPC5_PADDING_EXIST; - - frm->ch_id = iod->id; - - frm->hdr_len = sipc5_get_hdr_len(frm->config); - frm->data_len = count; - frm->len = frm->hdr_len + frm->data_len; - - buff[SIPC5_CONFIG_OFFSET] = frm->config; - buff[SIPC5_CH_ID_OFFSET] = frm->ch_id; - - if (unlikely(frm->ext_len)) - *sz32 = (u32)frm->len; - else - *sz16 = (u16)frm->len; - - if (unlikely(frm->ctl_fld)) - buff[SIPC5_CTL_OFFSET] = frm->control; - - return frm->hdr_len; + return ret; } static int rx_fmt_frame(struct sk_buff *skb) { - struct io_device *iod = skbpriv(skb)->iod; struct link_device *ld = skbpriv(skb)->ld; - struct sk_buff_head *rxq = &iod->sk_rx_q; - struct sipc_fmt_hdr *fh; + struct io_device *iod = skbpriv(skb)->iod; struct sk_buff *rx_skb; - u8 ctrl = skbpriv(skb)->control; - unsigned id = ctrl & 0x7F; + int hdr_len = sipc5_get_hdr_len(skb->data); + u8 ctrl; + u8 id; - if (iod->skb[id] == NULL) { - /* - ** There has been no multiple frame with this ID. - */ - if ((ctrl & 0x80) == 0) { - /* - ** It is a single frame because the "more" bit is 0. - */ - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: WARNING! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen = %d\n", - iod->name, rxq->qlen); - } + if (!sipc5_multi_frame(skb->data)) { + skb_pull(skb, hdr_len); + queue_skb_to_iod(skb, iod); + wake_up(&iod->wq); + return 0; + } - wake_up(&iod->wq); - return 0; - } + /* Get the control field */ + ctrl = sipc5_get_ctrl_field(skb->data); - /* - ** The start of multiple frames - */ - fh = (struct sipc_fmt_hdr *)skb->data; - mif_debug("%s: start multi-frame (ID:%d len:%d)\n", - iod->name, id, fh->len); + /* Extract the control ID from the control field */ + id = ctrl & 0x7F; + + /* Remove SIPC5 link header */ + skb_pull(skb, hdr_len); + + /* If there has been no multiple frame with this ID, ... */ + if (iod->skb[id] == NULL) { + struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)skb->data; + + mif_err("%s->%s: start of multi-frame (ID:%d len:%d)\n", + ld->name, iod->name, id, fh->len); rx_skb = rx_alloc_skb(fh->len, iod, ld); if (!rx_skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name); + mif_err("%s: ERR! rx_alloc_skb fail\n", iod->name); return -ENOMEM; } @@ -313,31 +236,19 @@ static int rx_fmt_frame(struct sk_buff *skb) rx_skb = iod->skb[id]; } - /* - ** Start multi-frame processing - */ + /* Perform multi-frame processing */ memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); dev_kfree_skb_any(skb); if (ctrl & 0x80) { /* The last frame has not arrived yet. */ - mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n", - iod->name, id, rx_skb->len); + mif_info("%s->%s: recv multi-frame (ID:%d rcvd:%d)\n", + ld->name, iod->name, id, rx_skb->len); } else { /* It is the last frame because the "more" bit is 0. */ - mif_debug("%s: end multi-frame (ID:%d rcvd:%d)\n", - iod->name, id, rx_skb->len); - skb_queue_tail(rxq, rx_skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: WARNING! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); - } - + mif_err("%s->%s: end of multi-frame (ID:%d rcvd:%d)\n", + ld->name, iod->name, id, rx_skb->len); + queue_skb_to_iod(rx_skb, iod); iod->skb[id] = NULL; wake_up(&iod->wq); } @@ -345,76 +256,14 @@ static int rx_fmt_frame(struct sk_buff *skb) return 0; } -static int rx_rfs_frame(struct sk_buff *skb) -{ - struct io_device *iod = skbpriv(skb)->iod; - struct sk_buff_head *rxq = &iod->sk_rx_q; - - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); - } - - wake_up(&iod->wq); - - return 0; -} - -static int rx_loopback(struct sk_buff *skb) -{ - struct io_device *iod = skbpriv(skb)->iod; - struct link_device *ld = get_current_link(iod); - struct sipc5_frame_data frm; - unsigned headroom; - unsigned tailroom = 0; - int ret; - - headroom = tx_build_link_header(&frm, iod, ld, skb->len); - - if (ld->aligned) - tailroom = sipc5_calc_padding_size(headroom + skb->len); - - /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb) - * already alloc 32bytes padding in headroom. 32bytes are enough. - */ - - /* store IPC link header to start of skb - * this is skb_push not skb_put. different with misc_write. - */ - memcpy(skb_push(skb, headroom), frm.hdr, headroom); - - /* store padding */ - if (tailroom) - skb_put(skb, tailroom); - - /* forward */ - ret = ld->send(ld, iod, skb); - if (ret < 0) - mif_err("%s->%s: ld->send fail: %d\n", iod->name, - ld->name, ret); - return ret; -} - static int rx_raw_misc(struct sk_buff *skb) { - struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ - struct sk_buff_head *rxq = &iod->sk_rx_q; + struct io_device *iod = skbpriv(skb)->iod; - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); - } + /* Remove the SIPC5 link header */ + skb_pull(skb, sipc5_get_hdr_len(skb->data)); + queue_skb_to_iod(skb, iod); wake_up(&iod->wq); return 0; @@ -422,12 +271,11 @@ static int rx_raw_misc(struct sk_buff *skb) static int rx_multi_pdp(struct sk_buff *skb) { - struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ + struct link_device *ld = skbpriv(skb)->ld; + struct io_device *iod = skbpriv(skb)->iod; struct net_device *ndev; struct iphdr *iphdr; - struct ethhdr *ehdr; int ret; - const char source[ETH_ALEN] = SOURCE_MAC_ADDR; ndev = iod->ndev; if (!ndev) { @@ -435,6 +283,9 @@ static int rx_multi_pdp(struct sk_buff *skb) return -ENODEV; } + /* Remove the SIPC5 link header */ + skb_pull(skb, sipc5_get_hdr_len(skb->data)); + skb->dev = ndev; ndev->stats.rx_packets++; ndev->stats.rx_bytes += skb->len; @@ -447,14 +298,15 @@ static int rx_multi_pdp(struct sk_buff *skb) skb->protocol = htons(ETH_P_IP); if (iod->use_handover) { - skb_push(skb, sizeof(struct ethhdr)); - ehdr = (void *)skb->data; + struct ethhdr *ehdr; + const char source[ETH_ALEN] = SOURCE_MAC_ADDR; + + ehdr = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr)); memcpy(ehdr->h_dest, ndev->dev_addr, ETH_ALEN); memcpy(ehdr->h_source, source, ETH_ALEN); ehdr->h_proto = skb->protocol; skb->ip_summed = CHECKSUM_UNNECESSARY; skb_reset_mac_header(skb); - skb_pull(skb, sizeof(struct ethhdr)); } @@ -463,48 +315,82 @@ static int rx_multi_pdp(struct sk_buff *skb) else ret = netif_rx_ni(skb); - if (ret != NET_RX_SUCCESS) - mif_info("%s: ERR! netif_rx fail (err %d)\n", iod->name, ret); + if (ret != NET_RX_SUCCESS) { + mif_err("%s->%s: ERR! netif_rx fail (err %d)\n", + ld->name, iod->name, ret); + } return ret; } static int rx_demux(struct link_device *ld, struct sk_buff *skb) { - struct io_device *iod = NULL; - char *link = ld->name; - u8 ch = skbpriv(skb)->ch_id; + struct io_device *iod; + u8 ch = sipc5_get_ch_id(skb->data); +#ifdef DEBUG_MODEM_IF + struct modem_ctl *mc = ld->mc; + size_t len = (skb->len > 20) ? 20 : skb->len; + char tag[MIF_MAX_STR_LEN]; +#endif - if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) { - mif_info("%s: ERR! invalid ch# %d\n", link, ch); + if (unlikely(ch == 0)) { + mif_err("%s: ERR! invalid ch# %d\n", ld->name, ch); return -ENODEV; } + if (unlikely(ch == SIPC5_CH_ID_FLOW_CTRL)) + return netif_flow_ctrl(ld, skb); + /* IP loopback */ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr) ch = RMNET0_CH_ID; iod = link_get_iod_with_channel(ld, ch); if (unlikely(!iod)) { - mif_info("%s: ERR! no iod for ch# %d\n", link, ch); + mif_err("%s: ERR! no iod with ch# %d\n", ld->name, ch); return -ENODEV; } skbpriv(skb)->ld = ld; skbpriv(skb)->iod = iod; - skbpriv(skb)->real_iod = iod; - /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */ - if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL)) + /* Don't care whether or not DATA_DRAIN_CHANNEL is opened */ + if (iod->id == DATA_DRAIN_CHANNEL) + return rx_drain(skb); + + /* Don't care whether or not DATA_LOOPBACK_CHANNEL is opened */ + if (iod->id == DATA_LOOPBACK_CHANNEL) return rx_loopback(skb); +#ifdef DEBUG_MODEM_IF + snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", mc->name, iod->name); + if (unlikely(iod->format == IPC_FMT)) + pr_ipc(1, tag, skb->data, len); +#if 0 + if (iod->format == IPC_RAW) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (iod->format == IPC_BOOT) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (iod->format == IPC_RAMDUMP) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (ch == 28) + pr_ipc(0, tag, skb->data, len); +#endif +#endif /*DEBUG_MODEM_IF*/ + if (atomic_read(&iod->opened) <= 0) { - mif_info("%s: ERR! %s is not opened\n", link, iod->name); + mif_err("%s: ERR! %s is not opened\n", ld->name, iod->name); return -ENODEV; } if (ch >= SIPC5_CH_ID_RFS_0) - return rx_rfs_frame(skb); + return rx_raw_misc(skb); else if (ch >= SIPC5_CH_ID_FMT_0) return rx_fmt_frame(skb); else if (iod->io_typ == IODEV_MISC) @@ -513,342 +399,337 @@ static int rx_demux(struct link_device *ld, struct sk_buff *skb) return rx_multi_pdp(skb); } -/* Check and store link layer header, then alloc an skb */ -static int rx_header_from_serial(struct io_device *iod, struct link_device *ld, - u8 *buff, unsigned size, struct sipc5_frame_data *frm) +/** + * rx_frame_config + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Checks a config field + * 2) Calculates the length of link layer header in an incoming frame and stores + * the value to "frm->hdr_len" + * 3) Stores the config field to "frm->hdr" and add the size of config field to + * "frm->hdr_rcvd" + * + * Returns the length of a config field that was copied to "frm" + */ +static int rx_frame_config(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) { - char *link = ld->name; - struct sk_buff *skb; - int len; - u8 cfg = buff[0]; - - mif_debug("%s: size %d\n", link, size); - - if (!frm->config) { - if (unlikely(!sipc5_start_valid(cfg))) { - mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); - return -EBADMSG; - } - rx_check_frame_cfg(cfg, frm); - - /* Copy the link layer header to the header buffer */ - len = min(frm->hdr_len, size); - memcpy(frm->hdr, buff, len); - } else { - /* Copy the link layer header to the header buffer */ - len = min((frm->hdr_len - frm->hdr_rcvd), size); - memcpy((frm->hdr + frm->hdr_rcvd), buff, len); - } + int rest; + int rcvd; - frm->hdr_rcvd += len; - - mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n", - link, frm->hdr_len, frm->hdr_rcvd); - - if (frm->hdr_rcvd >= frm->hdr_len) { - rx_build_meta_data(ld, frm); - skb = rx_alloc_skb(frm->data_len, iod, ld); - fragdata(iod, ld)->skb_recv = skb; - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + if (unlikely(!sipc5_start_valid(buff))) { + mif_err("%s->%s: ERR! INVALID config 0x%02x\n", + ld->name, iod->name, buff[0]); + return -EBADMSG; } - return len; -} - -/* copy data to skb */ -static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld, - u8 *buff, unsigned size, struct sipc5_frame_data *frm) -{ - struct sk_buff *skb = fragdata(iod, ld)->skb_recv; - char *link = ld->name; - unsigned rest = frm->data_len - frm->data_rcvd; - unsigned len; - - /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */ - rest = frm->data_len - frm->data_rcvd; - mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n", - link, frm->data_len, frm->data_rcvd, rest, size); - - /* If there is no skb, data must be dropped. */ - len = min(rest, size); - if (skb) - memcpy(skb_put(skb, len), buff, len); + frm->hdr_len = sipc5_get_hdr_len(buff); - frm->data_rcvd += len; + /* Calculate the size of a segment that will be copied */ + rest = frm->hdr_len; + rcvd = SIPC5_CONFIG_SIZE; + mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, + rcvd); - mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n", - link, frm->data_len, frm->data_rcvd); + /* Copy the config field of an SIPC5 link header to the header buffer */ + memcpy(frm->hdr, buff, rcvd); + frm->hdr_rcvd += rcvd; - return len; + return rcvd; } -static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld, - const char *data, unsigned size) +/** + * rx_frame_prepare_skb + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Extracts the length of a link frame from the link header in "frm->hdr" + * 2) Allocates an skb + * 3) Calculates the payload size in the link frame + * 4) Calculates the padding size in the link frame + * + * Returns the pointer to an skb + */ +static struct sk_buff *rx_frame_prepare_skb(struct io_device *iod, + struct link_device *ld, struct sipc5_frame_data *frm) { - struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; - char *link = ld->name; - u8 *buff = (u8 *)data; - int rest = (int)size; - int err = 0; - int done = 0; - mif_debug("%s: size = %d\n", link, size); + /* Get the frame length */ + frm->len = sipc5_get_frame_len(frm->hdr); - if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) { - /* - ** There is an skb that is waiting for more SIPC5 data. - ** In this case, rx_header_from_serial() must be skipped. - */ - mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n", - link, frm->data_len, frm->data_rcvd); - goto recv_data; + /* Allocate an skb */ + skb = rx_alloc_skb(frm->len, iod, ld); + if (!skb) { + mif_err("%s->%s: ERR! rx_alloc_skb fail (size %d)\n", + ld->name, iod->name, frm->len); + return NULL; } -next_frame: - /* Receive and analyze header, then prepare an akb */ - err = done = rx_header_from_serial(iod, ld, buff, rest, frm); - if (err < 0) - goto err_exit; - - buff += done; - rest -= done; - mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest); - if (rest < 0) - goto err_range; + /* Calculates the payload size */ + frm->pay_len = frm->len - frm->hdr_len; - if (rest == 0) - return size; + /* Calculates the padding size */ + if (sipc5_padding_exist(frm->hdr)) + frm->pad_len = sipc5_calc_padding_size(frm->len); -recv_data: - err = 0; + mif_debug("%s->%s: size %d (header:%d payload:%d padding:%d)\n", + ld->name, iod->name, frm->len, frm->hdr_len, frm->pay_len, + frm->pad_len); - mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest); - - done = rx_payload_from_serial(iod, ld, buff, rest, frm); - buff += done; - rest -= done; + return skb; +} - mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest); +/** + * rx_frame_header + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Stores a link layer header to "frm->hdr" temporarily while "frm->hdr_rcvd" + * is less than "frm->hdr_len" + * 2) Then, + * Allocates an skb + * Copies the link header from "frm" to "skb" + * Register the skb to receive payload + * + * Returns the size of a segment that was copied to "frm" + */ +static int rx_frame_header(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb; + int rest; + int rcvd; - if (rest == 0 && frm->data_rcvd < frm->data_len) { - /* - Data is being received and more data will come within the next - frame from the link device. - */ - return size; - } + /* Calculate the size of a segment that will be copied */ + rest = frm->hdr_len - frm->hdr_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, + rcvd); - /* At this point, one complete link layer frame has been received. */ + /* Copy a segment of an SIPC5 link header to "frm" */ + memcpy((frm->hdr + frm->hdr_rcvd), buff, rcvd); + frm->hdr_rcvd += rcvd; - /* A padding size is applied to access the next IPC frame. */ - if (frm->padding) { - done = sipc5_calc_padding_size(frm->len); - if (done > rest) { - mif_info("%s: ERR! padding %d > rest %d\n", - link, done, rest); - goto err_exit; + if (frm->hdr_rcvd >= frm->hdr_len) { + /* Prepare an skb with the information in {iod, ld, frm} */ + skb = rx_frame_prepare_skb(iod, ld, frm); + if (!skb) { + mif_err("%s->%s: ERR! rx_frame_prepare_skb fail\n", + ld->name, iod->name); + return -ENOMEM; } - buff += done; - rest -= done; - - mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest); - - if (rest < 0) - goto err_range; - - } - - skb = fragdata(iod, ld)->skb_recv; - if (likely(skb)) { - mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); - err = rx_demux(ld, skb); - if (err < 0) - dev_kfree_skb_any(skb); - } else { - mif_debug("%s: len:%d -> drop\n", link, skb->len); - } - - /* initialize the skb_recv and the frame_data buffer */ - fragdata(iod, ld)->skb_recv = NULL; - memset(frm, 0, sizeof(struct sipc5_frame_data)); + /* Copy an SIPC5 link header from "frm" to "skb" */ + memcpy(skb_put(skb, frm->hdr_len), frm->hdr, frm->hdr_len); - if (rest > 0) - goto next_frame; - - if (rest <= 0) - return size; - -err_exit: - if (fragdata(iod, ld)->skb_recv && - frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd >= frm->data_len) { - dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); - memset(frm, 0, sizeof(struct sipc5_frame_data)); - fragdata(iod, ld)->skb_recv = NULL; - mif_info("%s: ERR! clear frag\n", link); + /* Register the skb to receive payload */ + fragdata(iod, ld)->skb_recv = skb; } - return err; -err_range: - mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest); - return size; + return rcvd; } /** - * rx_header_from_mem - * @ld: pointer to the link device - * @buff: pointer to the frame - * @rest: size of the frame - * @frm: pointer to the sipc5_frame_data buffer + * rx_frame_payload + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure * - * 1) Verifies a link layer header configuration of a frame - * 2) Stores the link layer header to the header buffer - * 3) Builds and stores the meta data of the frame into a meta data buffer - * 4) Verifies the length of the frame + * Stores a link layer payload to "skb" * - * Returns SIPC5 header length + * Returns the size of a segment that was copied to "skb" */ -static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest, - struct sipc5_frame_data *frm) +static int rx_frame_payload(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) { - char *link = ld->name; - u8 cfg = buff[0]; + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + int rest; + int rcvd; - /* Verify link layer header configuration */ - if (unlikely(!sipc5_start_valid(cfg))) { - mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); - return -EBADMSG; - } - rx_check_frame_cfg(cfg, frm); + /* Calculate the size of a segment that will be copied */ + rest = frm->pay_len - frm->pay_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: pay_len:%d pay_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->pay_len, frm->pay_rcvd, rest, size, + rcvd); - /* Store the link layer header to the header buffer */ - memcpy(frm->hdr, buff, frm->hdr_len); - frm->hdr_rcvd = frm->hdr_len; + /* Copy an SIPC5 link payload to "skb" */ + memcpy(skb_put(skb, rcvd), buff, rcvd); + frm->pay_rcvd += rcvd; - /* Build and store the meta data of this frame */ - rx_build_meta_data(ld, frm); + return rcvd; +} - /* Verify frame length */ - if (unlikely(frm->len > rest)) { - mif_info("%s: ERR! frame length %d > rest %d\n", - link, frm->len, rest); - return -EBADMSG; - } +static int rx_frame_padding(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + int rest; + int rcvd; + + /* Calculate the size of a segment that will be dropped as padding */ + rest = frm->pad_len - frm->pad_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: pad_len:%d pad_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->pad_len, frm->pad_rcvd, rest, size, + rcvd); - return frm->hdr_rcvd; + /* Copy an SIPC5 link padding to "skb" */ + memcpy(skb_put(skb, rcvd), buff, rcvd); + frm->pad_rcvd += rcvd; + + return rcvd; } -/* copy data to skb */ -static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len) +static int rx_frame_done(struct io_device *iod, struct link_device *ld, + struct sk_buff *skb) { - /* If there is no skb, data must be dropped. */ - if (skb) - memcpy(skb_put(skb, len), buff, len); - return len; + /* Cut off the padding of the current frame */ + skb_trim(skb, sipc5_get_frame_len(skb->data)); + mif_debug("%s->%s: frame length = %d\n", ld->name, iod->name, skb->len); + + return rx_demux(ld, skb); } -static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld, +static int recv_frame_from_buff(struct io_device *iod, struct link_device *ld, const char *data, unsigned size) { struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; - char *link = ld->name; u8 *buff = (u8 *)data; int rest = (int)size; - int len; - int done; + int done = 0; + int err = 0; - mif_debug("%s: size = %d\n", link, size); + mif_debug("%s->%s: size %d (RX state = %s)\n", ld->name, iod->name, + size, get_rx_state_str(iod->curr_rx_state)); while (rest > 0) { - /* Initialize the frame data buffer */ - memset(frm, 0, sizeof(struct sipc5_frame_data)); - skb = NULL; + switch (iod->curr_rx_state) { + case IOD_RX_ON_STANDBY: + fragdata(iod, ld)->skb_recv = NULL; + memset(frm, 0, sizeof(struct sipc5_frame_data)); + + done = rx_frame_config(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* Receive and analyze link layer header */ - done = rx_header_from_mem(ld, buff, rest, frm); - if (unlikely(done < 0)) - return -EBADMSG; + iod->next_rx_state = IOD_RX_HEADER; - /* Verify rest size */ - rest -= done; - if (rest < 0) { - mif_info("%s: ERR! rx_header -> rest %d\n", link, rest); - return -ERANGE; - } + break; - /* Move buff pointer to the payload */ - buff += done; + case IOD_RX_HEADER: + done = rx_frame_header(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* Prepare an akb */ - len = frm->data_len; - skb = rx_alloc_skb(len, iod, ld); + if (frm->hdr_rcvd >= frm->hdr_len) + iod->next_rx_state = IOD_RX_PAYLOAD; + else + iod->next_rx_state = IOD_RX_HEADER; - /* Store channel ID and control fields to the CB of the skb */ - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + break; - /* Receive payload */ - mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n", - link, done, rest, len); - done = rx_payload_from_mem(skb, buff, len); - rest -= done; - if (rest < 0) { - mif_info("%s: ERR! rx_payload() -> rest %d\n", - link, rest); - if (skb) - dev_kfree_skb_any(skb); - return -ERANGE; - } - buff += done; + case IOD_RX_PAYLOAD: + done = rx_frame_payload(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* A padding size is applied to access the next IPC frame. */ - if (frm->padding) { - done = sipc5_calc_padding_size(frm->len); - if (done > rest) { - mif_info("%s: ERR! padding %d > rest %d\n", - link, done, rest); - if (skb) - dev_kfree_skb_any(skb); - return -ERANGE; + if (frm->pay_rcvd >= frm->pay_len) { + if (frm->pad_len > 0) + iod->next_rx_state = IOD_RX_PADDING; + else + iod->next_rx_state = IOD_RX_ON_STANDBY; + } else { + iod->next_rx_state = IOD_RX_PAYLOAD; } - buff += done; - rest -= done; + + break; + + case IOD_RX_PADDING: + done = rx_frame_padding(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } + + if (frm->pad_rcvd >= frm->pad_len) + iod->next_rx_state = IOD_RX_ON_STANDBY; + else + iod->next_rx_state = IOD_RX_PADDING; + + break; + + default: + mif_err("%s->%s: ERR! INVALID RX state %d\n", + ld->name, iod->name, iod->curr_rx_state); + err = -EINVAL; + goto err_exit; } - if (likely(skb)) { - mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); - if (rx_demux(ld, skb) < 0) - dev_kfree_skb_any(skb); - } else { - mif_debug("%s: len:%d -> drop\n", link, skb->len); + if (iod->next_rx_state == IOD_RX_ON_STANDBY) { + /* + ** A complete frame is in fragdata(iod, ld)->skb_recv. + */ + skb = fragdata(iod, ld)->skb_recv; + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto err_exit; } + + buff += done; + rest -= done; + if (rest < 0) + goto err_range; + + iod->curr_rx_state = iod->next_rx_state; } - return 0; + return size; + +err_exit: + if (fragdata(iod, ld)->skb_recv) { + mif_err("%s->%s: ERR! clear frag (size:%d done:%d rest:%d)\n", + ld->name, iod->name, size, done, rest); + dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); + fragdata(iod, ld)->skb_recv = NULL; + } + iod->curr_rx_state = IOD_RX_ON_STANDBY; + return err; + +err_range: + mif_err("%s->%s: ERR! size:%d done:%d rest:%d\n", + ld->name, iod->name, size, done, rest); + iod->curr_rx_state = IOD_RX_ON_STANDBY; + return size; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) { - struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; - char *link = ld->name; int err; - if (!data) { - mif_info("%s: ERR! !data\n", link); - return -EINVAL; - } - - if (len <= 0) { - mif_info("%s: ERR! len %d <= 0\n", link, len); - return -EINVAL; - } - switch (iod->format) { case IPC_FMT: case IPC_RAW: @@ -857,97 +738,151 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - if (ld->link_type == LINKDEV_DPRAM && ld->aligned) - err = rx_frame_from_mem(iod, ld, data, len); - else - err = rx_frame_from_serial(iod, ld, data, len); - - if (err < 0) - mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n", - link, err); + err = recv_frame_from_buff(iod, ld, data, len); + if (err < 0) { + mif_err("%s->%s: ERR! recv_frame_from_buff fail " + "(err %d)\n", ld->name, iod->name, err); + } return err; - case IPC_CMD: - case IPC_BOOT: - case IPC_RAMDUMP: + default: + mif_debug("%s->%s: len %d\n", ld->name, iod->name, len); + /* save packet to sk_buff */ skb = rx_alloc_skb(len, iod, ld); if (!skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", link); + mif_info("%s->%s: ERR! rx_alloc_skb fail\n", + ld->name, iod->name); return -ENOMEM; } - mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name); - memcpy(skb_put(skb, len), data, len); - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: ERR! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } + + queue_skb_to_iod(skb, iod); + wake_up(&iod->wq); return len; - - default: - mif_info("%s: ERR! unknown format %d\n", link, iod->format); - return -EINVAL; } } -static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld, +static int recv_frame_from_skb(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { - struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; - u8 cfg = skb->data[0]; + struct sk_buff *clone; + unsigned int rest; + unsigned int rcvd; + int tot; /* total length including padding */ + int err = 0; - /* Initialize the frame data buffer */ - memset(frm, 0, sizeof(struct sipc5_frame_data)); + /* + ** If there is only one SIPC5 frame in @skb, receive the SIPC5 frame and + ** return immediately. In this case, the frame verification must already + ** have been done at the link device. + */ + if (skbpriv(skb)->single_frame) { + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto exit; + return 0; + } /* - ** The start of a link layer header has already been checked in the - ** link device. + ** The routine from here is used only if there may be multiple SIPC5 + ** frames in @skb. */ - /* Analyze the configuration of the link layer header */ - rx_check_frame_cfg(cfg, frm); + /* Check the config field of the first frame in @skb */ + if (!sipc5_start_valid(skb->data)) { + mif_err("%s->%s: ERR! INVALID config 0x%02X\n", + ld->name, iod->name, skb->data[0]); + err = -EINVAL; + goto exit; + } + + /* Get the total length of the frame with a padding */ + tot = sipc5_get_total_len(skb->data); - /* Store the link layer header to the header buffer */ - memcpy(frm->hdr, skb->data, frm->hdr_len); - frm->hdr_rcvd = frm->hdr_len; + /* Verify the total length of the first frame */ + rest = skb->len; + if (unlikely(tot > rest)) { + mif_err("%s->%s: ERR! tot %d > skb->len %d)\n", + ld->name, iod->name, tot, rest); + err = -EINVAL; + goto exit; + } - /* Build and store the meta data of this frame */ - rx_build_meta_data(ld, frm); + /* If there is only one SIPC5 frame in @skb, */ + if (likely(tot == rest)) { + /* Receive the SIPC5 frame and return immediately */ + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto exit; + return 0; + } /* - ** The length of the frame has already been checked in the link device. + ** This routine is used only if there are multiple SIPC5 frames in @skb. */ + rcvd = 0; + while (rest > 0) { + clone = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!clone)) { + mif_err("%s->%s: ERR! skb_clone fail\n", + ld->name, iod->name); + err = -ENOMEM; + goto exit; + } - /* Trim the link layer header off the frame */ - skb_pull(skb, frm->hdr_len); + /* Get the start of an SIPC5 frame */ + skb_pull(clone, rcvd); + if (!sipc5_start_valid(clone->data)) { + mif_err("%s->%s: ERR! INVALID config 0x%02X\n", + ld->name, iod->name, clone->data[0]); + dev_kfree_skb_any(clone); + err = -EINVAL; + goto exit; + } - /* Store channel ID and control fields to the CB of the skb */ - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + /* Get the total length of the current frame with a padding */ + tot = sipc5_get_total_len(clone->data); + if (unlikely(tot > rest)) { + mif_err("%s->%s: ERR! dirty frame (tot %d > rest %d)\n", + ld->name, iod->name, tot, rest); + dev_kfree_skb_any(clone); + err = -EINVAL; + goto exit; + } - /* Demux the frame */ - if (rx_demux(ld, skb) < 0) { - mif_err("%s: ERR! rx_demux fail\n", ld->name); - return -EINVAL; + /* Cut off the padding of the current frame */ + skb_trim(clone, sipc5_get_frame_len(clone->data)); + + /* Demux the frame */ + err = rx_demux(ld, clone); + if (err < 0) { + mif_err("%s->%s: ERR! rx_demux fail (err %d)\n", + ld->name, iod->name, err); + dev_kfree_skb_any(clone); + goto exit; + } + + /* Calculate the start of the next frame */ + rcvd += tot; + + /* Calculate the rest size of data in @skb */ + rest -= tot; } - return 0; +exit: + dev_kfree_skb_any(skb); + return err; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { - char *link = ld->name; enum dev_format dev = iod->format; int err; @@ -959,17 +894,35 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - err = rx_frame_from_skb(iod, ld, skb); + err = recv_frame_from_skb(iod, ld, skb); if (err < 0) { - dev_kfree_skb_any(skb); - mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n", - link, err); + mif_err("%s->%s: ERR! recv_frame_from_skb fail " + "(err %d)\n", ld->name, iod->name, err); + } + + return err; + + case IPC_BOOT: + case IPC_RAMDUMP: + if (!iod->id) { + mif_err("%s->%s: ERR! invalid iod\n", + ld->name, iod->name); + return -ENODEV; + } + + if (iod->waketime) + wake_lock_timeout(&iod->wakelock, iod->waketime); + + err = recv_frame_from_skb(iod, ld, skb); + if (err < 0) { + mif_err("%s->%s: ERR! recv_frame_from_skb fail " + "(err %d)\n", ld->name, iod->name, err); } return err; default: - mif_info("%s: ERR! unknown device %d\n", link, dev); + mif_err("%s->%s: ERR! invalid iod\n", ld->name, iod->name); return -EINVAL; } } @@ -980,10 +933,14 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - mif_info("%s: %s state changed (state %d)\n", - iod->name, iod->mc->name, state); + struct modem_ctl *mc = iod->mc; + int old_state = mc->phone_state; - iod->mc->phone_state = state; + if (old_state != state) { + mc->phone_state = state; + mif_err("%s state changed (%s -> %s)\n", mc->name, + get_cp_state_str(old_state), get_cp_state_str(state)); + } if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || state == STATE_NV_REBUILDING) @@ -1024,23 +981,27 @@ static int misc_open(struct inode *inode, struct file *filp) struct io_device *iod = to_io_device(filp->private_data); struct modem_shared *msd = iod->msd; struct link_device *ld; + int ref_cnt; int ret; filp->private_data = (void *)iod; - atomic_inc(&iod->opened); - list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->init_comm) { ret = ld->init_comm(ld, iod); if (ret < 0) { - mif_info("%s: init_comm fail(%d)\n", - ld->name, ret); + mif_err("%s<->%s: ERR! init_comm fail(%d)\n", + iod->name, ld->name, ret); return ret; } } } - mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + ref_cnt = atomic_inc_return(&iod->opened); + + if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) + mif_err("%s (opened %d)\n", iod->name, ref_cnt); + else + mif_info("%s (opened %d)\n", iod->name, ref_cnt); return 0; } @@ -1050,8 +1011,8 @@ static int misc_release(struct inode *inode, struct file *filp) struct io_device *iod = (struct io_device *)filp->private_data; struct modem_shared *msd = iod->msd; struct link_device *ld; + int ref_cnt; - atomic_dec(&iod->opened); skb_queue_purge(&iod->sk_rx_q); list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -1059,7 +1020,12 @@ static int misc_release(struct inode *inode, struct file *filp) ld->terminate_comm(ld, iod); } - mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + ref_cnt = atomic_dec_return(&iod->opened); + + if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) + mif_err("%s (opened %d)\n", iod->name, ref_cnt); + else + mif_info("%s (opened %d)\n", iod->name, ref_cnt); return 0; } @@ -1067,20 +1033,23 @@ static int misc_release(struct inode *inode, struct file *filp) static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) { struct io_device *iod = (struct io_device *)filp->private_data; + struct modem_ctl *mc = iod->mc; poll_wait(filp, &iod->wq, wait); - if (!skb_queue_empty(&iod->sk_rx_q) && - iod->mc->phone_state != STATE_OFFLINE) { + if (!skb_queue_empty(&iod->sk_rx_q) && mc->phone_state != STATE_OFFLINE) return POLLIN | POLLRDNORM; - } else if ((iod->mc->phone_state == STATE_CRASH_RESET) || - (iod->mc->phone_state == STATE_CRASH_EXIT) || - (iod->mc->phone_state == STATE_NV_REBUILDING) || - (iod->mc->sim_state.changed)) { + + if (mc->phone_state == STATE_CRASH_RESET + || mc->phone_state == STATE_CRASH_EXIT + || mc->phone_state == STATE_NV_REBUILDING + || mc->sim_state.changed) { if (iod->format == IPC_RAW) { msleep(20); return 0; } + if (iod->format == IPC_RAMDUMP) + return 0; return POLLHUP; } else { return 0; @@ -1089,132 +1058,191 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int p_state; struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); - char cpinfo_buf[530] = "CP Crash "; + struct modem_ctl *mc = iod->mc; + int p_state; + char *buff; + void __user *user_buff; unsigned long size; - int ret; switch (cmd) { case IOCTL_MODEM_ON: - mif_info("%s: IOCTL_MODEM_ON\n", iod->name); - return iod->mc->ops.modem_on(iod->mc); + if (mc->ops.modem_on) { + mif_err("%s: IOCTL_MODEM_ON\n", iod->name); + return mc->ops.modem_on(mc); + } + mif_err("%s: !mc->ops.modem_on\n", iod->name); + return -EINVAL; case IOCTL_MODEM_OFF: - mif_info("%s: IOCTL_MODEM_OFF\n", iod->name); - return iod->mc->ops.modem_off(iod->mc); + if (mc->ops.modem_off) { + mif_err("%s: IOCTL_MODEM_OFF\n", iod->name); + return mc->ops.modem_off(mc); + } + mif_err("%s: !mc->ops.modem_off\n", iod->name); + return -EINVAL; case IOCTL_MODEM_RESET: - mif_info("%s: IOCTL_MODEM_RESET\n", iod->name); - return iod->mc->ops.modem_reset(iod->mc); + if (mc->ops.modem_reset) { + mif_err("%s: IOCTL_MODEM_RESET\n", iod->name); + return mc->ops.modem_reset(mc); + } + mif_err("%s: !mc->ops.modem_reset\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_ON: - mif_info("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); - return iod->mc->ops.modem_boot_on(iod->mc); + if (mc->ops.modem_boot_on) { + mif_err("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); + return mc->ops.modem_boot_on(mc); + } + mif_err("%s: !mc->ops.modem_boot_on\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_OFF: - mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); - return iod->mc->ops.modem_boot_off(iod->mc); + if (mc->ops.modem_boot_off) { + mif_err("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); + return mc->ops.modem_boot_off(mc); + } + mif_err("%s: !mc->ops.modem_boot_off\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_DONE: mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); - if (iod->mc->ops.modem_boot_done) - return iod->mc->ops.modem_boot_done(iod->mc); - else - return 0; + if (mc->ops.modem_boot_done) + return mc->ops.modem_boot_done(mc); + return 0; case IOCTL_MODEM_STATUS: mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); - p_state = iod->mc->phone_state; + p_state = mc->phone_state; if ((p_state == STATE_CRASH_RESET) || (p_state == STATE_CRASH_EXIT)) { - mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", - iod->name, p_state); - } else if (iod->mc->sim_state.changed) { - int s_state = iod->mc->sim_state.online ? + mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", + iod->name, get_cp_state_str(p_state)); + } else if (mc->sim_state.changed) { + int s_state = mc->sim_state.online ? STATE_SIM_ATTACH : STATE_SIM_DETACH; - iod->mc->sim_state.changed = false; + mc->sim_state.changed = false; return s_state; } else if (p_state == STATE_NV_REBUILDING) { - mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", - iod->name, p_state); - iod->mc->phone_state = STATE_ONLINE; + mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", + iod->name, get_cp_state_str(p_state)); + mc->phone_state = STATE_ONLINE; } return p_state; - case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_debug("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", - iod->name); - - if (iod->format != IPC_MULTI_RAW) - return -EINVAL; + case IOCTL_MODEM_XMIT_BOOT: + if (ld->xmit_boot) { + mif_info("%s: IOCTL_MODEM_XMIT_BOOT\n", iod->name); + return ld->xmit_boot(ld, iod, arg); + } + mif_err("%s: !ld->xmit_boot\n", iod->name); + return -EINVAL; - iodevs_for_each(iod->msd, iodev_netif_stop, 0); - return 0; + case IOCTL_MODEM_DL_START: + if (ld->dload_start) { + mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); + return ld->dload_start(ld, iod); + } + mif_err("%s: !ld->dload_start\n", iod->name); + return -EINVAL; - case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", - iod->name); + case IOCTL_MODEM_FW_UPDATE: + if (ld->firm_update) { + mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); + return ld->firm_update(ld, iod, arg); + } + mif_err("%s: !ld->firm_update\n", iod->name); + return -EINVAL; - if (iod->format != IPC_MULTI_RAW) - return -EINVAL; + case IOCTL_MODEM_FORCE_CRASH_EXIT: + if (mc->ops.modem_force_crash_exit) { + mif_err("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", + iod->name); + return mc->ops.modem_force_crash_exit(mc); + } + mif_err("%s: !mc->ops.modem_force_crash_exit\n", iod->name); + return -EINVAL; - iodevs_for_each(iod->msd, iodev_netif_wake, 0); - return 0; + case IOCTL_MODEM_DUMP_RESET: + if (mc->ops.modem_dump_reset) { + mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); + return mc->ops.modem_dump_reset(mc); + } + mif_err("%s: !mc->ops.modem_dump_reset\n", iod->name); + return -EINVAL; case IOCTL_MODEM_DUMP_START: - mif_info("%s: IOCTL_MODEM_DUMP_START\n", iod->name); - return ld->dump_start(ld, iod); + if (ld->dump_start) { + mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + } + mif_err("%s: !ld->dump_start\n", iod->name); + return -EINVAL; + + case IOCTL_MODEM_RAMDUMP_START: + if (ld->dump_start) { + mif_info("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + } + mif_err("%s: !ld->dump_start\n", iod->name); + return -EINVAL; case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); - return ld->dump_update(ld, iod, arg); + if (ld->dump_update) { + mif_info("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); + return ld->dump_update(ld, iod, arg); + } + mif_err("%s: !ld->dump_update\n", iod->name); + return -EINVAL; - case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_info("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); - if (iod->mc->ops.modem_force_crash_exit) - return iod->mc->ops.modem_force_crash_exit(iod->mc); + case IOCTL_MODEM_RAMDUMP_STOP: + if (ld->dump_finish) { + mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); + return ld->dump_finish(ld, iod, arg); + } + mif_err("%s: !ld->dump_finish\n", iod->name); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: mif_info("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); - if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), - (void __user *)arg, MAX_CPINFO_SIZE) != 0) - return -EFAULT; - panic(cpinfo_buf); + strcpy(iod->msd->cp_crash_info, CP_CRASH_TAG); + if (arg) { + buff = iod->msd->cp_crash_info + strlen(CP_CRASH_TAG); + user_buff = (void __user *)arg; + if (copy_from_user(buff, user_buff, MAX_CPINFO_SIZE)) + return -EFAULT; + } + panic(iod->msd->cp_crash_info); return 0; - case IOCTL_MODEM_DUMP_RESET: - mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); - return iod->mc->ops.modem_dump_reset(iod->mc); + case IOCTL_MODEM_PROTOCOL_SUSPEND: + mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); + if (iod->format == IPC_MULTI_RAW) { + iodevs_for_each(iod->msd, iodev_netif_stop, 0); + return 0; + } + return -EINVAL; + + case IOCTL_MODEM_PROTOCOL_RESUME: + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); + if (iod->format != IPC_MULTI_RAW) { + iodevs_for_each(iod->msd, iodev_netif_wake, 0); + return 0; + } + return -EINVAL; case IOCTL_MIF_LOG_DUMP: iodevs_for_each(iod->msd, iodev_dump_status, 0); + user_buff = (void __user *)arg; size = MAX_MIF_BUFF_SIZE; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) + if (copy_to_user(user_buff, &size, sizeof(unsigned long))) return -EFAULT; - - mif_dump_log(iod->mc->msd, iod); + mif_dump_log(mc->msd, iod); return 0; - case IOCTL_MIF_DPRAM_DUMP: -#ifdef CONFIG_LINK_DEVICE_DPRAM - if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { - size = iod->mc->mdm_data->dpram_ctl->dp_size; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) - return -EFAULT; - mif_dump_dpram(iod); - return 0; - } -#endif - return -EINVAL; - default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1222,9 +1250,10 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_info("%s: ERR! cmd 0x%X not defined.\n", iod->name, cmd); + mif_info("%s: ERR! undefined cmd 0x%X\n", iod->name, cmd); return -EINVAL; } + return 0; } @@ -1234,49 +1263,76 @@ static ssize_t misc_write(struct file *filp, const char __user *data, struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); struct sk_buff *skb; + u8 *buff; int ret; - unsigned headroom = 0; - unsigned tailroom = 0; - size_t tx_size; - struct sipc5_frame_data frm; - struct timespec epoch; + size_t headroom; + size_t tailroom; + size_t tx_bytes; + u8 cfg; if (iod->format <= IPC_RFS && iod->id == 0) return -EINVAL; - headroom = tx_build_link_header(&frm, iod, ld, count); + cfg = sipc5_build_config(iod, ld, count); + + if (cfg) + headroom = sipc5_get_hdr_len(&cfg); + else + headroom = 0; if (ld->aligned) tailroom = sipc5_calc_padding_size(headroom + count); + else + tailroom = 0; - tx_size = headroom + count + tailroom; + tx_bytes = headroom + count + tailroom; - skb = alloc_skb(tx_size, GFP_KERNEL); + skb = alloc_skb(tx_bytes, GFP_KERNEL); if (!skb) { - mif_info("%s: ERR! alloc_skb fail (tx_size:%d)\n", - iod->name, tx_size); + mif_info("%s: ERR! alloc_skb fail (tx_bytes:%d)\n", + iod->name, tx_bytes); return -ENOMEM; } - /* store IPC link header*/ - memcpy(skb_put(skb, headroom), frm.hdr, headroom); + /* Build SIPC5 link header*/ + if (cfg) { + buff = skb_put(skb, headroom); + sipc5_build_header(iod, ld, buff, cfg, 0, count); + } - /* store IPC message */ - if (copy_from_user(skb_put(skb, count), data, count) != 0) { - if (skb) - dev_kfree_skb_any(skb); + /* Store IPC message */ + buff = skb_put(skb, count); + if (copy_from_user(buff, data, count)) { + mif_err("%s->%s: ERR! copy_from_user fail (count %d)\n", + iod->name, ld->name, count); + dev_kfree_skb_any(skb); return -EFAULT; } - if (iod->id == SIPC5_CH_ID_FMT_0) { + /* Apply padding */ + if (tailroom) + skb_put(skb, tailroom); + + if (iod->format == IPC_FMT) { + struct timespec epoch; + u8 *msg = (skb->data + headroom); +#if 0 + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: RIL2MIF", iod->mc->name); + pr_ipc(1, tag, msg, (count > 20 ? 20 : count)); +#endif getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); - mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len); + mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, msg, count); } - /* store padding */ - if (tailroom) - skb_put(skb, tailroom); +#if 0 + if (iod->format == IPC_RAMDUMP) { + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: DUMP2MIF", iod->name); + pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); + } +#endif /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data @@ -1286,13 +1342,15 @@ static ssize_t misc_write(struct file *filp, const char __user *data, ret = ld->send(ld, iod, skb); if (ret < 0) { - mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); + mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", + iod->name, ld->name, ret, tx_bytes); return ret; } - if (ret != tx_size) - mif_info("%s: wrong tx size (count:%d tx_size:%d ret:%d)\n", - iod->name, count, tx_size, ret); + if (ret != tx_bytes) { + mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", + iod->name, ld->name, ret, tx_bytes, count); + } return count; } @@ -1304,24 +1362,38 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; int copied = 0; - struct timespec epoch; - skb = skb_dequeue(rxq); - if (!skb) { + if (skb_queue_empty(rxq)) { mif_info("%s: ERR! no data in rxq\n", iod->name); return 0; } - if (iod->id == SIPC5_CH_ID_FMT_0) { + skb = skb_dequeue(rxq); + + if (iod->format == IPC_FMT) { + struct timespec epoch; +#if 0 + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2RIL", iod->mc->name); + pr_ipc(0, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); +#endif getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len); } +#if 0 + if (iod->format == IPC_RAMDUMP) { + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2DUMP", iod->name); + pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); + } +#endif + copied = skb->len > count ? count : skb->len; if (copy_to_user(buf, skb->data, copied)) { - mif_info("%s: ERR! copy_to_user fail\n", iod->name); + mif_err("%s: ERR! copy_to_user fail\n", iod->name); dev_kfree_skb_any(skb); return -EFAULT; } @@ -1339,43 +1411,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return copied; } -#ifdef CONFIG_LINK_DEVICE_C2C -static int misc_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int r = 0; - unsigned long size = 0; - unsigned long pfn = 0; - unsigned long offset = 0; - struct io_device *iod = (struct io_device *)filp->private_data; - - if (!vma) - return -EFAULT; - - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { - mif_info("ERR: offset + size > C2C_CP_RGN_SIZE\n"); - return -EINVAL; - } - - /* Set the noncacheable property to the region */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_RESERVED | VM_IO; - - pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); - r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); - if (r) { - mif_info("ERR: Failed in remap_pfn_range()!!!\n"); - return -EAGAIN; - } - - mif_info("%s: VA = 0x%08lx, offset = 0x%lx, size = %lu\n", - iod->name, vma->vm_start, offset, size); - - return 0; -} -#endif - static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1384,9 +1419,6 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, -#ifdef CONFIG_LINK_DEVICE_C2C - .mmap = misc_mmap, -#endif }; static int vnet_open(struct net_device *ndev) @@ -1419,11 +1451,13 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct link_device *ld = get_current_link(iod); struct sk_buff *skb_new; int ret; - unsigned headroom = 0; - unsigned tailroom = 0; - unsigned long tx_bytes = skb->len; + unsigned headroom; + unsigned tailroom; + size_t count; + size_t tx_bytes; struct iphdr *ip_header = NULL; - struct sipc5_frame_data frm; + u8 *buff; + u8 cfg; /* When use `handover' with Network Bridge, * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here. @@ -1436,19 +1470,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_pull(skb, sizeof(struct ethhdr)); } - headroom = tx_build_link_header(&frm, iod, ld, skb->len); + count = skb->len; - /* ip loop-back */ - ip_header = (struct iphdr *)skb->data; - if (iod->msd->loopback_ipaddr && - ip_header->daddr == iod->msd->loopback_ipaddr) { - swap(ip_header->saddr, ip_header->daddr); - frm.ch_id = DATA_LOOPBACK_CHANNEL; - frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; - } + cfg = sipc5_build_config(iod, ld, count); + + headroom = sipc5_get_hdr_len(&cfg); if (ld->aligned) - tailroom = sipc5_calc_padding_size(frm.len); + tailroom = sipc5_calc_padding_size(headroom + count); + else + tailroom = 0; + + tx_bytes = headroom + count + tailroom; if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) { mif_debug("%s: skb_copy_expand needed\n", iod->name); @@ -1463,7 +1496,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_new = skb; } - memcpy(skb_push(skb_new, headroom), frm.hdr, headroom); + /* Build SIPC5 link header*/ + buff = skb_push(skb_new, headroom); + sipc5_build_header(iod, ld, buff, cfg, 0, count); + + /* IP loop-back */ + ip_header = (struct iphdr *)skb->data; + if (iod->msd->loopback_ipaddr && + ip_header->daddr == iod->msd->loopback_ipaddr) { + swap(ip_header->saddr, ip_header->daddr); + buff[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; + } + if (tailroom) skb_put(skb_new, tailroom); @@ -1473,12 +1517,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) ret = ld->send(ld, iod, skb_new); if (ret < 0) { netif_stop_queue(ndev); - mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); + mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", + iod->name, ld->name, ret, tx_bytes); return NETDEV_TX_BUSY; } + if (ret != tx_bytes) { + mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", + iod->name, ld->name, ret, tx_bytes, count); + } + ndev->stats.tx_packets++; - ndev->stats.tx_bytes += tx_bytes; + ndev->stats.tx_bytes += count; return NETDEV_TX_OK; } @@ -1581,16 +1631,19 @@ int sipc5_init_io_device(struct io_device *iod) ret = misc_register(&iod->miscdev); if (ret) mif_info("%s: ERR! misc_register fail\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_waketime); if (ret) mif_info("%s: ERR! device_create_file fail\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_loopback); if (ret) mif_err("failed to create `loopback file' : %s\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_txlink); if (ret) diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c index f5e33d3..8a173d4 100644 --- a/drivers/misc/modem_if/sipc5_modem.c +++ b/drivers/misc/modem_if/sipc5_modem.c @@ -33,7 +33,8 @@ #include #include -#include +#include "modem.h" +#include #include "modem_prj.h" #include "modem_variation.h" #include "modem_utils.h" @@ -77,27 +78,31 @@ static struct modem_shared *create_modem_shared_data(void) static struct modem_ctl *create_modemctl_device(struct platform_device *pdev, struct modem_shared *msd) { - int ret = 0; - struct modem_data *pdata; + struct modem_data *pdata = pdev->dev.platform_data; struct modem_ctl *modemctl; - struct device *dev = &pdev->dev; + int ret; /* create modem control device */ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL); - if (!modemctl) + if (!modemctl) { + mif_err("%s: modemctl kzalloc fail\n", pdata->name); + mif_err("%s: xxx\n", pdata->name); return NULL; + } modemctl->msd = msd; - modemctl->dev = dev; + modemctl->dev = &pdev->dev; modemctl->phone_state = STATE_OFFLINE; - pdata = pdev->dev.platform_data; modemctl->mdm_data = pdata; modemctl->name = pdata->name; /* init modemctl device for getting modemctl operations */ ret = call_modem_init_func(modemctl, pdata); if (ret) { + mif_err("%s: call_modem_init_func fail (err %d)\n", + pdata->name, ret); + mif_err("%s: xxx\n", pdata->name); kfree(modemctl); return NULL; } @@ -111,8 +116,8 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, struct modem_shared *msd, struct modem_ctl *modemctl, struct modem_data *pdata) { - int ret = 0; - struct io_device *iod = NULL; + int ret; + struct io_device *iod; iod = kzalloc(sizeof(struct io_device), GFP_KERNEL); if (!iod) { @@ -128,6 +133,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, iod->format = io_t->format; iod->io_typ = io_t->io_type; iod->link_types = io_t->links; + iod->app = io_t->app; iod->net_typ = pdata->modem_net; iod->use_handover = pdata->use_handover; iod->ipc_version = pdata->ipc_version; @@ -139,7 +145,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, modemctl->iod = iod; if (iod->format == IPC_BOOT) { modemctl->bootd = iod; - mif_info("Bood device = %s\n", iod->name); + mif_err("BOOT device = %s\n", iod->name); } /* link between io device and modem shared */ @@ -160,7 +166,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, return NULL; } - mif_debug("%s is created!!!\n", iod->name); + mif_info("%s created\n", iod->name); return iod; } @@ -168,7 +174,6 @@ static int attach_devices(struct io_device *iod, enum modem_link tx_link) { struct modem_shared *msd = iod->msd; struct link_device *ld; - unsigned ch; /* find link type for this io device */ list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -232,36 +237,36 @@ static int __devinit modem_probe(struct platform_device *pdev) { int i; struct modem_data *pdata = pdev->dev.platform_data; - struct modem_shared *msd = NULL; - struct modem_ctl *modemctl = NULL; + struct modem_shared *msd; + struct modem_ctl *modemctl; struct io_device *iod[pdata->num_iodevs]; struct link_device *ld; - - mif_err("%s\n", pdev->name); - memset(iod, 0, sizeof(iod)); + mif_err("%s: +++\n", pdata->name); msd = create_modem_shared_data(); if (!msd) { - mif_err("msd == NULL\n"); - goto err_free_modemctl; + mif_err("%s: msd == NULL\n", pdata->name); + return -ENOMEM; } modemctl = create_modemctl_device(pdev, msd); if (!modemctl) { - mif_err("modemctl == NULL\n"); - goto err_free_modemctl; + mif_err("%s: modemctl == NULL\n", pdata->name); + kfree(msd); + return -ENOMEM; } /* create link device */ /* support multi-link device */ + memset(iod, 0, sizeof(iod)); for (i = 0; i < LINKDEV_MAX ; i++) { /* find matching link type */ if (pdata->link_types & LINKTYPE(i)) { ld = call_link_init_func(pdev, i); if (!ld) - goto err_free_modemctl; + goto free_mc; - mif_err("link created: %s\n", ld->name); + mif_err("%s: %s link created\n", pdata->name, ld->name); ld->link_type = i; ld->mc = modemctl; ld->msd = msd; @@ -274,8 +279,8 @@ static int __devinit modem_probe(struct platform_device *pdev) iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl, pdata); if (!iod[i]) { - mif_err("iod[%d] == NULL\n", i); - goto err_free_modemctl; + mif_err("%s: iod[%d] == NULL\n", pdata->name, i); + goto free_iod; } attach_devices(iod[i], pdata->iodevs[i].tx_link); @@ -283,21 +288,23 @@ static int __devinit modem_probe(struct platform_device *pdev) platform_set_drvdata(pdev, modemctl); - mif_err("Complete!!!\n"); - + mif_err("%s: ---\n", pdata->name); return 0; -err_free_modemctl: - for (i = 0; i < pdata->num_iodevs; i++) - if (iod[i] != NULL) +free_iod: + for (i = 0; i < pdata->num_iodevs; i++) { + if (iod[i]) kfree(iod[i]); + } - if (modemctl != NULL) +free_mc: + if (modemctl) kfree(modemctl); - if (msd != NULL) + if (msd) kfree(msd); + mif_err("%s: xxx\n", pdata->name); return -ENOMEM; } @@ -305,13 +312,19 @@ static void modem_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct modem_ctl *mc = dev_get_drvdata(dev); + struct utc_time utc; + mc->ops.modem_off(mc); mc->phone_state = STATE_OFFLINE; + + get_utc_time(&utc); + mif_info("%s: at [%02d:%02d:%02d.%03d]\n", + mc->name, utc.hour, utc.min, utc.sec, utc.msec); } static int modem_suspend(struct device *pdev) { -#ifndef CONFIG_LINK_DEVICE_HSIC +#if !defined(CONFIG_LINK_DEVICE_HSIC) struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -323,7 +336,7 @@ static int modem_suspend(struct device *pdev) static int modem_resume(struct device *pdev) { -#ifndef CONFIG_LINK_DEVICE_HSIC +#if !defined(CONFIG_LINK_DEVICE_HSIC) struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -334,8 +347,8 @@ static int modem_resume(struct device *pdev) } static const struct dev_pm_ops modem_pm_ops = { - .suspend = modem_suspend, - .resume = modem_resume, + .suspend = modem_suspend, + .resume = modem_resume, }; static struct platform_driver modem_driver = { @@ -343,7 +356,7 @@ static struct platform_driver modem_driver = { .shutdown = modem_shutdown, .driver = { .name = "mif_sipc5", - .pm = &modem_pm_ops, + .pm = &modem_pm_ops, }, }; -- cgit v1.1 From e359fdd28d3ed3984d1305b3e7ba80fdb1c6e003 Mon Sep 17 00:00:00 2001 From: RGIB Date: Sat, 28 May 2016 11:37:13 +0200 Subject: smdk4412 : local library for kona Change-Id: Icc141221dd9a2ff925935b211fe746d31d37ee2b --- arch/arm/mach-exynos/board-m0-modems.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-exynos/board-m0-modems.c b/arch/arm/mach-exynos/board-m0-modems.c index 4139c79..b920d17 100644 --- a/arch/arm/mach-exynos/board-m0-modems.c +++ b/arch/arm/mach-exynos/board-m0-modems.c @@ -28,7 +28,12 @@ #include #include +#ifdef CONFIG_MACH_KONA +#include "../../../drivers/misc/modem_if/modem.h" +#else #include +#endif + #include extern int s3c_gpio_slp_cfgpin(unsigned int pin, unsigned int config); -- cgit v1.1 From 1d1882a85994f5b466be01c07e8db902dae032b8 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:32:16 -0400 Subject: USB: usbfs: fix potential infoleak in devio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “ci” has a total size of 8 bytes. Its last 3 bytes are padding bytes which are not initialized and leaked to userland via “copy_to_user”. Change-Id: Icd49231ee1862682739a871ae78a5602ee104731 Signed-off-by: Kangjie Lu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 461e785..8aff636 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1012,10 +1012,11 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) static int proc_connectinfo(struct dev_state *ps, void __user *arg) { - struct usbdevfs_connectinfo ci = { - .devnum = ps->dev->devnum, - .slow = ps->dev->speed == USB_SPEED_LOW - }; + struct usbdevfs_connectinfo ci; + + memset(&ci, 0, sizeof(ci)); + ci.devnum = ps->dev->devnum; + ci.slow = ps->dev->speed == USB_SPEED_LOW; if (copy_to_user(arg, &ci, sizeof(ci))) return -EFAULT; -- cgit v1.1 From 48243d6f59dd8d1e4ddb4338fb235d8ddf7b516f Mon Sep 17 00:00:00 2001 From: RGIB Date: Sat, 28 May 2016 14:23:20 +0200 Subject: smdk4412 : expand library to M0 boards Change-Id: I49ea61cea82f64ac1a2cdeb538ee174882749033 --- arch/arm/mach-exynos/board-m0-modems.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/board-m0-modems.c b/arch/arm/mach-exynos/board-m0-modems.c index b920d17..50d475e 100644 --- a/arch/arm/mach-exynos/board-m0-modems.c +++ b/arch/arm/mach-exynos/board-m0-modems.c @@ -28,7 +28,7 @@ #include #include -#ifdef CONFIG_MACH_KONA +#ifdef CONFIG_SEC_MODEM_M0 #include "../../../drivers/misc/modem_if/modem.h" #else #include -- cgit v1.1 From 1b54924821bae6a41ae3c2ee6c7953b4270a8d53 Mon Sep 17 00:00:00 2001 From: RGIB Date: Wed, 1 Jun 2016 15:01:49 +0200 Subject: n51x0 : build gps Change-Id: I24a27b513464a09e23c2625afa0472101538da21 --- arch/arm/configs/cyanogenmod_n5100_defconfig | 4 ++-- arch/arm/configs/cyanogenmod_n5110_defconfig | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_n5100_defconfig b/arch/arm/configs/cyanogenmod_n5100_defconfig index ab51fd6..57d3ef0 100644 --- a/arch/arm/configs/cyanogenmod_n5100_defconfig +++ b/arch/arm/configs/cyanogenmod_n5100_defconfig @@ -474,7 +474,7 @@ CONFIG_KONA_01_BD=y # CONFIG_SLP is not set # CONFIG_MACH_REDWOOD is not set # CONFIG_GPS_BCM47511 is not set -# CONFIG_GPS_BCM4752 is not set +CONFIG_GPS_BCM4752=y # CONFIG_GPS_GSD4T is not set # CONFIG_GPIO_NAPLES_00_BD is not set # CONFIG_SLP_DISP_DEBUG is not set @@ -565,7 +565,7 @@ CONFIG_SEC_MODEM_M0=y # # Connectivity Feature # -# CONFIG_GPS_BRCM_475X is not set +CONFIG_GPS_BRCM_475X=y # CONFIG_BT_CSR8811 is not set # CONFIG_BT_BCM4330 is not set CONFIG_BT_BCM4334=y diff --git a/arch/arm/configs/cyanogenmod_n5110_defconfig b/arch/arm/configs/cyanogenmod_n5110_defconfig index 0e94537..1000a70 100644 --- a/arch/arm/configs/cyanogenmod_n5110_defconfig +++ b/arch/arm/configs/cyanogenmod_n5110_defconfig @@ -474,7 +474,7 @@ CONFIG_KONA_01_BD=y # CONFIG_SLP is not set # CONFIG_MACH_REDWOOD is not set # CONFIG_GPS_BCM47511 is not set -# CONFIG_GPS_BCM4752 is not set +CONFIG_GPS_BCM4752=y # CONFIG_GPS_GSD4T is not set # CONFIG_GPIO_NAPLES_00_BD is not set # CONFIG_SLP_DISP_DEBUG is not set @@ -566,7 +566,7 @@ CONFIG_SEC_MODEM_M0=y # # Connectivity Feature # -# CONFIG_GPS_BRCM_475X is not set +CONFIG_GPS_BRCM_475X=y # CONFIG_BT_CSR8811 is not set # CONFIG_BT_BCM4330 is not set CONFIG_BT_BCM4334=y -- cgit v1.1 From ba951bfe89181e1565eb2921e0ca545302840c0e Mon Sep 17 00:00:00 2001 From: RGIB Date: Sat, 4 Jun 2016 17:52:16 +0200 Subject: n5120 : qcom modem Change-Id: I59e572d5e9bd02fd73432dd847fc5c87792c2747 --- arch/arm/configs/cyanogenmod_n5120_defconfig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index 2b6784b..896bf0d 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -578,16 +578,16 @@ CONFIG_BT_MGMT=y # Qualcomm Modem Feature # CONFIG_QC_MODEM=y -# CONFIG_CPU_FREQ_TETHERING is not set -# CONFIG_MSM_SUBSYSTEM_RESTART is not set -# CONFIG_QC_MODEM_MDM9X15 is not set -# CONFIG_MDM_HSIC_PM is not set +CONFIG_MSM_RMNET_USB=y +CONFIG_SENSORS_HALL=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_QC_MODEM_MDM9X15=y +CONFIG_MDM_HSIC_PM=y # CONFIG_EMI_ERROR_RECOVERY is not set -CONFIG_SIM_DETECT=y +CONFIG_QC_MODEM_M3=y CONFIG_USB_CDFS_SUPPORT=y -# CONFIG_SAMSUNG_PRODUCT_SHIP is not set +CONFIG_SAMSUNG_PRODUCT_SHIP=y # CONFIG_CORESIGHT_ETM is not set -CONFIG_HSIC_EURONLY_APPLY=y # # Processor Type @@ -721,7 +721,7 @@ CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y # CONFIG_USE_OF is not set CONFIG_ZBOOT_ROM_TEXT=0 CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12" +CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12 androidboot.selinux=permissive" # CONFIG_CMDLINE_FROM_BOOTLOADER is not set CONFIG_CMDLINE_EXTEND=y # CONFIG_CMDLINE_FORCE is not set @@ -1676,6 +1676,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y # # DIAG traffic over USB # +CONFIG_DIAG_OVER_USB=y # # SDIO support for DIAG @@ -1684,6 +1685,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y # # HSIC support for DIAG # +CONFIG_DIAG_HSIC_PIPE=y # CONFIG_TTY_PRINTK is not set # CONFIG_HVC_DCC is not set # CONFIG_IPMI_HANDLER is not set @@ -2638,8 +2640,8 @@ CONFIG_USB_SERIAL_CSVT=y # CONFIG_USB_TEST is not set # CONFIG_USB_ISIGHTFW is not set # CONFIG_USB_YUREX is not set -# CONFIG_USB_QCOM_DIAG_BRIDGE is not set -# CONFIG_USB_QCOM_MDM_BRIDGE is not set +CONFIG_USB_QCOM_DIAG_BRIDGE=y +CONFIG_USB_QCOM_MDM_BRIDGE=y CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG is not set # CONFIG_USB_GADGET_DEBUG_FILES is not set @@ -3421,5 +3423,3 @@ CONFIG_HAS_DMA=y CONFIG_CPU_RMAP=y CONFIG_NLATTR=y # CONFIG_AVERAGE is not set - -CONFIG_SENSORS_HALL=y -- cgit v1.1 From d37f93f0308351614ce5bf06a2eb9fd655f96c5a Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Fri, 15 Apr 2016 12:57:05 -0700 Subject: net: wireless: bcmdhd: check privilege on priv cmd check net admin capability for ioctl calls BUG=26425765 Change-Id: Idae75c9fc530add3ead3508d25e994bbfec9a6de --- drivers/net/wireless/bcmdhd/wl_android.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 7ac8f56..12f7b1d 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -2906,6 +2906,10 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) ret = -EINVAL; goto exit; } + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto exit; + } if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { ret = -EFAULT; goto exit; -- cgit v1.1 From a0e971623856a8ebda21521e32f833e37b8f1462 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Sun, 12 Jun 2016 00:47:40 +0200 Subject: Makefile: remove hardcoded toolchain Change-Id: Ie72fd888e715b0f11b4247ba81fa09cd7450f99b --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 861fc30..5d78b85 100644 --- a/Makefile +++ b/Makefile @@ -192,8 +192,8 @@ 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 ?= arm -CROSS_COMPILE ?= ../../../prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi- +ARCH ?= $(SUBARCH) +CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) # Architecture as present in compile.h UTS_MACHINE := $(ARCH) -- cgit v1.1 From c47282825bbde9f692d61b50dfd9f0f8f51afdf2 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Sun, 12 Jun 2016 00:10:11 +0200 Subject: Revert "Add ZRAM_FOR_ANDROID" Change-Id: I6aff6a484dd94730f2032ceb838e0741ca6fa878 --- arch/arm/configs/cyanogenmod_i9100_defconfig | 1 - arch/arm/configs/cyanogenmod_i9300_defconfig | 1 - drivers/staging/android/lowmemorykiller.c | 254 --------------------------- drivers/staging/zram/Kconfig | 7 - drivers/staging/zram/zram_drv.c | 53 +----- drivers/staging/zram/zram_drv.h | 2 +- drivers/staging/zram/zram_sysfs.c | 31 +--- include/linux/mm_types.h | 6 - kernel/power/earlysuspend.c | 13 -- mm/shmem.c | 9 - mm/swapfile.c | 153 ---------------- mm/vmscan.c | 78 -------- 12 files changed, 10 insertions(+), 598 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index bf8eac8..cfacb41 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -2694,7 +2694,6 @@ CONFIG_XVMALLOC=y CONFIG_ZRAM=y CONFIG_ZCACHE=y # CONFIG_ZRAM_DEBUG is not set -CONFIG_ZRAM_FOR_ANDROID=y # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 0239633..3d793b4 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2875,7 +2875,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_XVMALLOC=y CONFIG_ZRAM=y # CONFIG_ZRAM_DEBUG is not set -CONFIG_ZRAM_FOR_ANDROID=y # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 15bbcd3..d412581 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -35,12 +35,6 @@ #include #include #include -#ifdef CONFIG_ZRAM_FOR_ANDROID -#include -#include -#include -#include -#endif /* CONFIG_ZRAM_FOR_ANDROID */ #define ENHANCED_LMK_ROUTINE #ifdef ENHANCED_LMK_ROUTINE @@ -62,35 +56,6 @@ static size_t lowmem_minfree[6] = { 16 * 1024, /* 64MB */ }; static int lowmem_minfree_size = 4; -#ifdef CONFIG_ZRAM_FOR_ANDROID -static struct class *lmk_class; -static struct device *lmk_dev; -static int lmk_kill_pid = 0; -static int lmk_kill_ok = 0; - -extern atomic_t optimize_comp_on; - -extern int isolate_lru_page_compcache(struct page *page); -extern void putback_lru_page(struct page *page); -extern unsigned int zone_id_shrink_pagelist(struct zone *zone_id,struct list_head *page_list); - -#define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru)) - -#define SWAP_PROCESS_DEBUG_LOG 0 -/* free RAM 8M(2048 pages) */ -#define CHECK_FREE_MEMORY 2048 -/* free swap (10240 pages) */ -#define CHECK_FREE_SWAPSPACE 10240 - -static unsigned int check_free_memory = 0; - -enum pageout_io { - PAGEOUT_IO_ASYNC, - PAGEOUT_IO_SYNC, -}; - - -#endif /* CONFIG_ZRAM_FOR_ANDROID */ #ifdef ENHANCED_LMK_ROUTINE static struct task_struct *lowmem_deathpending[LOWMEM_DEATHPENDING_DEPTH] = {NULL,}; @@ -323,229 +288,10 @@ static struct shrinker lowmem_shrinker = { .seeks = DEFAULT_SEEKS * 16 }; -#ifdef CONFIG_ZRAM_FOR_ANDROID -/* - * zone_id_shrink_pagelist() clear page flags, - * update the memory zone status, and swap pagelist - */ - -static unsigned int shrink_pages(struct mm_struct *mm, - struct list_head *zone0_page_list, - struct list_head *zone1_page_list, - unsigned int num_to_scan) -{ - unsigned long addr; - unsigned int isolate_pages_countter = 0; - - struct vm_area_struct *vma = mm->mmap; - while (vma != NULL) { - - for (addr = vma->vm_start; addr < vma->vm_end; - addr += PAGE_SIZE) { - struct page *page; - /*get the page address from virtual memory address */ - page = follow_page(vma, addr, FOLL_GET); - - if (page && !IS_ERR(page)) { - - put_page(page); - /* only moveable, anonymous and not dirty pages can be swapped */ - if ((!PageUnevictable(page)) - && (!PageDirty(page)) && ((PageAnon(page))) - && (0 == page_is_file_cache(page))) { - switch (page_zone_id(page)) { - case 0: - if (!isolate_lru_page_compcache(page)) { - /* isolate page from LRU and add to temp list */ - /*create new page list, it will be used in shrink_page_list */ - list_add_tail(&page->lru, zone0_page_list); - isolate_pages_countter++; - } - break; - case 1: - if (!isolate_lru_page_compcache(page)) { - /* isolate page from LRU and add to temp list */ - /*create new page list, it will be used in shrink_page_list */ - list_add_tail(&page->lru, zone1_page_list); - isolate_pages_countter++; - } - break; - default: - break; - } - } - } - - if (isolate_pages_countter >= num_to_scan) { - return isolate_pages_countter; - } - } - - vma = vma->vm_next; - } - - return isolate_pages_countter; -} - -/* - * swap_application_pages() will search the - * pages which can be swapped, then call - * zone_id_shrink_pagelist to update zone - * status - */ -static unsigned int swap_pages(struct list_head *zone0_page_list, - struct list_head *zone1_page_list) -{ - struct zone *zone_id_0 = &NODE_DATA(0)->node_zones[0]; - struct zone *zone_id_1 = &NODE_DATA(0)->node_zones[1]; - unsigned int pages_counter = 0; - - /*if the page list is not empty, call zone_id_shrink_pagelist to update zone status */ - if ((zone_id_0) && (!list_empty(zone0_page_list))) { - pages_counter += - zone_id_shrink_pagelist(zone_id_0, zone0_page_list); - } - if ((zone_id_1) && (!list_empty(zone1_page_list))) { - pages_counter += - zone_id_shrink_pagelist(zone_id_1, zone1_page_list); - } - return pages_counter; -} - -static ssize_t lmk_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d,%d\n", lmk_kill_pid, lmk_kill_ok); -} - -/* - * lmk_state_store() will called by framework, - * the framework will send the pid of process that need to be swapped - */ -static ssize_t lmk_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - sscanf(buf, "%d,%d", &lmk_kill_pid, &lmk_kill_ok); - - /* if the screen on, the optimized compcache will stop */ - if (atomic_read(&optimize_comp_on) != 1) - return size; - - if (lmk_kill_ok == 1) { - struct task_struct *p; - struct task_struct *selected = NULL; - struct sysinfo ramzswap_info = { 0 }; - struct mm_struct *mm_scan = NULL; - - /* - * check the free RAM and swap area, - * stop the optimized compcache in cpu idle case; - * leave some swap area for using in low memory case - */ - si_swapinfo(&ramzswap_info); - si_meminfo(&ramzswap_info); - - if ((ramzswap_info.freeswap < CHECK_FREE_SWAPSPACE) || - (ramzswap_info.freeram < check_free_memory)) { -#if SWAP_PROCESS_DEBUG_LOG > 0 - printk(KERN_INFO "idletime compcache is ignored : free RAM %lu, free swap %lu\n", - ramzswap_info.freeram, ramzswap_info.freeswap); -#endif - lmk_kill_ok = 0; - return size; - } - - read_lock(&tasklist_lock); - for_each_process(p) { - if ((p->pid == lmk_kill_pid) && - (__task_cred(p)->uid > 10000)) { - task_lock(p); - selected = p; - if (!selected->mm || !selected->signal) { - task_unlock(p); - selected = NULL; - break; - } - mm_scan = selected->mm; - if (mm_scan) { - if (selected->flags & PF_KTHREAD) - mm_scan = NULL; - else - atomic_inc(&mm_scan->mm_users); - } - task_unlock(selected); - -#if SWAP_PROCESS_DEBUG_LOG > 0 - printk(KERN_INFO "idle time compcache: swap process pid %d, name %s, oom %d, task size %ld\n", - p->pid, p->comm, - p->signal->oom_adj, - get_mm_rss(p->mm)); -#endif - break; - } - } - read_unlock(&tasklist_lock); - - if (mm_scan) { - LIST_HEAD(zone0_page_list); - LIST_HEAD(zone1_page_list); - int pages_tofree = 0, pages_freed = 0; - - down_read(&mm_scan->mmap_sem); - pages_tofree = - shrink_pages(mm_scan, &zone0_page_list, - &zone1_page_list, 0x7FFFFFFF); - up_read(&mm_scan->mmap_sem); - mmput(mm_scan); - pages_freed = - swap_pages(&zone0_page_list, - &zone1_page_list); - lmk_kill_ok = 0; - - } - } - - return size; -} - -static DEVICE_ATTR(lmk_state, 0664, lmk_state_show, lmk_state_store); - -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - static int __init lowmem_init(void) { -#ifdef CONFIG_ZRAM_FOR_ANDROID - struct zone *zone; - unsigned int high_wmark = 0; -#endif task_free_register(&task_nb); register_shrinker(&lowmem_shrinker); - -#ifdef CONFIG_ZRAM_FOR_ANDROID - for_each_zone(zone) { - if (high_wmark < zone->watermark[WMARK_HIGH]) - high_wmark = zone->watermark[WMARK_HIGH]; - } - check_free_memory = (high_wmark != 0) ? high_wmark : CHECK_FREE_MEMORY; - - lmk_class = class_create(THIS_MODULE, "lmk"); - if (IS_ERR(lmk_class)) { - printk(KERN_ERR "Failed to create class(lmk)\n"); - return 0; - } - lmk_dev = device_create(lmk_class, NULL, 0, NULL, "lowmemorykiller"); - if (IS_ERR(lmk_dev)) { - printk(KERN_ERR - "Failed to create device(lowmemorykiller)!= %ld\n", - IS_ERR(lmk_dev)); - return 0; - } - if (device_create_file(lmk_dev, &dev_attr_lmk_state) < 0) - printk(KERN_ERR "Failed to create device file(%s)!\n", - dev_attr_lmk_state.attr.name); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - return 0; } diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index 06f741a..3bec4db 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -28,10 +28,3 @@ config ZRAM_DEBUG help This option adds additional debugging code to the compressed RAM block device driver. - -config ZRAM_FOR_ANDROID - bool "Optimize zram behavior for android" - depends on ZRAM && ANDROID - default n - help - This option enables modified zram behavior optimized for android diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 5258c78..aab4ec4 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -32,16 +32,12 @@ #include #include #include -#ifdef CONFIG_ZRAM_FOR_ANDROID -#include -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - #include "zram_drv.h" /* Globals */ static int zram_major; -struct zram *zram_devices; +struct zram *devices; /* Module params (documentation at end) */ unsigned int num_devices; @@ -137,22 +133,6 @@ static void zram_set_disksize(struct zram *zram, size_t totalram_bytes) zram->disksize &= PAGE_MASK; } -#ifdef CONFIG_ZRAM_FOR_ANDROID -/* - * Swap header (1st page of swap device) contains information - * about a swap file/partition. Prepare such a header for the - * given ramzswap device so that swapon can identify it as a - * swap partition. - */ -static void setup_swap_header(struct zram *zram, union swap_header *s) -{ - s->info.version = 1; - s->info.last_page = (zram->disksize >> PAGE_SHIFT) - 1; - s->info.nr_badpages = 0; - memcpy(s->magic.magic, "SWAPSPACE2", 10); -} -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - static void zram_free_page(struct zram *zram, size_t index) { u32 clen; @@ -521,10 +501,6 @@ int zram_init_device(struct zram *zram) { int ret; size_t num_pages; -#ifdef CONFIG_ZRAM_FOR_ANDROID - struct page *page; - union swap_header *swap_header; -#endif /* CONFIG_ZRAM_FOR_ANDROID */ mutex_lock(&zram->init_lock); @@ -559,19 +535,6 @@ int zram_init_device(struct zram *zram) goto fail; } -#ifdef CONFIG_ZRAM_FOR_ANDROID - page = alloc_page(__GFP_ZERO); - if (!page) { - pr_err("Error allocating swap header page\n"); - ret = -ENOMEM; - goto fail; - } - zram->table[0].page = page; - zram_set_flag(zram, 0, ZRAM_UNCOMPRESSED); - swap_header = kmap(page); - setup_swap_header(zram, swap_header); - kunmap(page); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); /* zram devices sort of resembles non-rotational disks */ @@ -715,14 +678,14 @@ static int __init zram_init(void) /* Allocate the device array and initialize each one */ pr_info("Creating %u devices ...\n", num_devices); - zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); - if (!zram_devices) { + devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); + if (!devices) { ret = -ENOMEM; goto unregister; } for (dev_id = 0; dev_id < num_devices; dev_id++) { - ret = create_device(&zram_devices[dev_id], dev_id); + ret = create_device(&devices[dev_id], dev_id); if (ret) goto free_devices; } @@ -731,8 +694,8 @@ static int __init zram_init(void) free_devices: while (dev_id) - destroy_device(&zram_devices[--dev_id]); - kfree(zram_devices); + destroy_device(&devices[--dev_id]); + kfree(devices); unregister: unregister_blkdev(zram_major, "zram"); out: @@ -745,7 +708,7 @@ static void __exit zram_exit(void) struct zram *zram; for (i = 0; i < num_devices; i++) { - zram = &zram_devices[i]; + zram = &devices[i]; destroy_device(zram); if (zram->init_done) @@ -754,7 +717,7 @@ static void __exit zram_exit(void) unregister_blkdev(zram_major, "zram"); - kfree(zram_devices); + kfree(devices); pr_debug("Cleanup done!\n"); } diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 3ad9486..408b2c0 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -120,7 +120,7 @@ struct zram { struct zram_stats stats; }; -extern struct zram *zram_devices; +extern struct zram *devices; extern unsigned int num_devices; #ifdef CONFIG_SYSFS extern struct attribute_group zram_disk_attr_group; diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 8a23554..6996eb3 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -35,7 +35,7 @@ static struct zram *dev_to_zram(struct device *dev) struct zram *zram = NULL; for (i = 0; i < num_devices; i++) { - zram = &zram_devices[i]; + zram = &devices[i]; if (disk_to_dev(zram->disk) == dev) break; } @@ -80,41 +80,12 @@ static ssize_t initstate_show(struct device *dev, return sprintf(buf, "%u\n", zram->init_done); } -#ifdef CONFIG_ZRAM_FOR_ANDROID -extern int swapon(const char*specialfile, int swap_flags); - -static ssize_t initstate_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t len) -{ - int ret; - unsigned long do_init; - struct zram *zram = dev_to_zram(dev); - - if (zram->init_done) { - pr_info("the device is initialized device\n"); - return -EBUSY; - } - - ret = strict_strtoul(buf, 10, &do_init); - if (ret) - return ret; - if (!do_init) - return -EINVAL; - - zram_init_device(zram); - swapon("/dev/block/zram0", 0); - return len; -} -#else static inline ssize_t initstate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { return 0; } -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 0f230ce..059839c 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -198,9 +198,6 @@ struct vm_area_struct { #ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* NUMA policy for the VMA */ #endif -#ifdef CONFIG_ZRAM_FOR_ANDROID - int vma_swap_done; -#endif /* CONFIG_ZRAM_FOR_ANDROID */ }; struct core_thread { @@ -333,9 +330,6 @@ struct mm_struct { #ifdef CONFIG_CPUMASK_OFFSTACK struct cpumask cpumask_allocation; #endif -#ifdef CONFIG_ZRAM_FOR_ANDROID - int mm_swap_done; -#endif /* CONFIG_ZRAM_FOR_ANDROID */ }; static inline void mm_init_cpumask(struct mm_struct *mm) diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c index e6303fd..f0fb629 100644 --- a/kernel/power/earlysuspend.c +++ b/kernel/power/earlysuspend.c @@ -20,9 +20,6 @@ #include /* sys_sync */ #include #include -#ifdef CONFIG_ZRAM_FOR_ANDROID -#include -#endif /* CONFIG_ZRAM_FOR_ANDROID */ #include "power.h" @@ -32,10 +29,6 @@ enum { DEBUG_VERBOSE = 1U << 3, }; static int debug_mask = DEBUG_USER_STATE; -#ifdef CONFIG_ZRAM_FOR_ANDROID -atomic_t optimize_comp_on = ATOMIC_INIT(0); -EXPORT_SYMBOL(optimize_comp_on); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); @@ -102,9 +95,6 @@ static void early_suspend(struct work_struct *work) mutex_lock(&early_suspend_lock); spin_lock_irqsave(&state_lock, irqflags); -#ifdef CONFIG_ZRAM_FOR_ANDROID - atomic_set(&optimize_comp_on, 1); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ if (state == SUSPEND_REQUESTED) state |= SUSPENDED; else @@ -156,9 +146,6 @@ static void late_resume(struct work_struct *work) mutex_lock(&early_suspend_lock); spin_lock_irqsave(&state_lock, irqflags); -#ifdef CONFIG_ZRAM_FOR_ANDROID - atomic_set(&optimize_comp_on, 0); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ if (state == SUSPENDED) state &= ~SUSPENDED; else diff --git a/mm/shmem.c b/mm/shmem.c index a858b67..bcfa97d 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1060,17 +1060,8 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) info = SHMEM_I(inode); if (info->flags & VM_LOCKED) goto redirty; -#ifdef CONFIG_ZRAM_FOR_ANDROID - /* - * Modification for compcache - * shmem_writepage can be reason of kernel panic when using swap. - * This modification prevent using swap by shmem. - */ - goto redirty; -#else if (!total_swap_pages) goto redirty; -#endif /* * shmem_backing_dev_info's capabilities prevent regular writeback or diff --git a/mm/swapfile.c b/mm/swapfile.c index 3e5a3a7..c8f4338 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2012,159 +2012,6 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p, return nr_extents; } -#ifdef CONFIG_ZRAM_FOR_ANDROID -int swapon(char *name, int swap_flags) -{ - struct swap_info_struct *p; - - struct file *swap_file = NULL; - struct address_space *mapping; - int i; - int prio; - int error; - union swap_header *swap_header; - int nr_extents; - sector_t span; - unsigned long maxpages; - unsigned char *swap_map = NULL; - struct page *page = NULL; - struct inode *inode = NULL; - - p = alloc_swap_info(); - if (IS_ERR(p)) - return PTR_ERR(p); - - swap_file = filp_open(name, O_RDWR | O_LARGEFILE, 0); - if (IS_ERR(swap_file)) { - error = PTR_ERR(swap_file); - swap_file = NULL; - printk("zfqin, filp_open failed\n"); - goto bad_swap; - } - - printk("zfqin, filp_open succeeded\n"); - p->swap_file = swap_file; - mapping = swap_file->f_mapping; - - for (i = 0; i < nr_swapfiles; i++) { - struct swap_info_struct *q = swap_info[i]; - - if (q == p || !q->swap_file) - continue; - if (mapping == q->swap_file->f_mapping) { - error = -EBUSY; - goto bad_swap; - } - } - - inode = mapping->host; - /* If S_ISREG(inode->i_mode) will do mutex_lock(&inode->i_mutex); */ - error = claim_swapfile(p, inode); - if (unlikely(error)) - goto bad_swap; - - /* - * Read the swap header. - */ - if (!mapping->a_ops->readpage) { - error = -EINVAL; - goto bad_swap; - } - page = read_mapping_page(mapping, 0, swap_file); - if (IS_ERR(page)) { - error = PTR_ERR(page); - goto bad_swap; - } - swap_header = kmap(page); - - maxpages = read_swap_header(p, swap_header, inode); - if (unlikely(!maxpages)) { - error = -EINVAL; - goto bad_swap; - } - - /* OK, set up the swap map and apply the bad block list */ - swap_map = vzalloc(maxpages); - if (!swap_map) { - error = -ENOMEM; - goto bad_swap; - } - - error = swap_cgroup_swapon(p->type, maxpages); - if (error) - goto bad_swap; - - nr_extents = setup_swap_map_and_extents(p, swap_header, swap_map, - maxpages, &span); - if (unlikely(nr_extents < 0)) { - error = nr_extents; - goto bad_swap; - } - - if (p->bdev) { - if (blk_queue_nonrot(bdev_get_queue(p->bdev))) { - p->flags |= SWP_SOLIDSTATE; - p->cluster_next = 1 + (random32() % p->highest_bit); - } - if (discard_swap(p) == 0 && (swap_flags & SWAP_FLAG_DISCARD)) - p->flags |= SWP_DISCARDABLE; - } - - mutex_lock(&swapon_mutex); - prio = -1; - if (swap_flags & SWAP_FLAG_PREFER) - prio = - (swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT; - enable_swap_info(p, prio, swap_map); - - printk(KERN_INFO "Adding %uk swap on %s. " - "Priority:%d extents:%d across:%lluk %s%s\n", - p->pages << (PAGE_SHIFT - 10), name, p->prio, - nr_extents, (unsigned long long)span << (PAGE_SHIFT - 10), - (p->flags & SWP_SOLIDSTATE) ? "SS" : "", - (p->flags & SWP_DISCARDABLE) ? "D" : ""); - - mutex_unlock(&swapon_mutex); - atomic_inc(&proc_poll_event); - wake_up_interruptible(&proc_poll_wait); - - if (S_ISREG(inode->i_mode)) - inode->i_flags |= S_SWAPFILE; - error = 0; - goto out; - bad_swap: - if (inode && S_ISBLK(inode->i_mode) && p->bdev) { - set_blocksize(p->bdev, p->old_block_size); - blkdev_put(p->bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); - } - destroy_swap_extents(p); - swap_cgroup_swapoff(p->type); - spin_lock(&swap_lock); - p->swap_file = NULL; - p->flags = 0; - spin_unlock(&swap_lock); - vfree(swap_map); - if (swap_file) { - if (inode && S_ISREG(inode->i_mode)) { - mutex_unlock(&inode->i_mutex); - inode = NULL; - } - filp_close(swap_file, NULL); - } - out: - if (page && !IS_ERR(page)) { - kunmap(page); - page_cache_release(page); - } - - if (inode && S_ISREG(inode->i_mode)) - mutex_unlock(&inode->i_mutex); - return error; -} - -EXPORT_SYMBOL(swapon); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) { struct swap_info_struct *p; diff --git a/mm/vmscan.c b/mm/vmscan.c index 99082fa..08f11e2 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -770,9 +770,6 @@ static noinline_for_stack void free_page_list(struct list_head *free_pages) /* * shrink_page_list() returns the number of reclaimed pages */ -#ifndef CONFIG_ZRAM_FOR_ANDROID -static -#endif /* CONFIG_ZRAM_FOR_ANDROID */ unsigned long shrink_page_list(struct list_head *page_list, struct zone *zone, struct scan_control *sc) @@ -1279,9 +1276,6 @@ static unsigned long isolate_pages_global(unsigned long nr, * clear_active_flags() is a helper for shrink_active_list(), clearing * any active bits from the pages in the list. */ -#ifndef CONFIG_ZRAM_FOR_ANDROID -static -#endif /* CONFIG_ZRAM_FOR_ANDROID */ unsigned long clear_active_flags(struct list_head *page_list, unsigned int *count) { @@ -1352,40 +1346,6 @@ int isolate_lru_page(struct page *page) return ret; } -#ifdef CONFIG_ZRAM_FOR_ANDROID -/** - * isolate_lru_page_compcache - tries to isolate a page for compcache - * @page: page to isolate from its LRU list - * - * Isolates a @page from an LRU list, clears PageLRU,but - * does not adjusts the vmstat statistic - * Returns 0 if the page was removed from an LRU list. - * Returns -EBUSY if the page was not on an LRU list. - */ -int isolate_lru_page_compcache(struct page *page) -{ - int ret = -EBUSY; - - VM_BUG_ON(!page_count(page)); - - if (PageLRU(page)) { - struct zone *zone = page_zone(page); - - spin_lock_irq(&zone->lru_lock); - if (PageLRU(page)) { - int lru = page_lru(page); - ret = 0; - get_page(page); - ClearPageLRU(page); - list_del(&page->lru); - mem_cgroup_del_lru_list(page, lru); - } - spin_unlock_irq(&zone->lru_lock); - } - return ret; -} -#endif - /* * Are there way too many processes in the direct reclaim path already? */ @@ -1622,44 +1582,6 @@ shrink_inactive_list(unsigned long nr_to_scan, struct zone *zone, return nr_reclaimed; } -#ifdef CONFIG_ZRAM_FOR_ANDROID -unsigned long -zone_id_shrink_pagelist(struct zone *zone, struct list_head *page_list) -{ - unsigned long nr_reclaimed = 0; - unsigned long nr_anon; - unsigned long nr_file; - - struct scan_control sc = { - .gfp_mask = GFP_USER, - .may_writepage = 1, - .nr_to_reclaim = SWAP_CLUSTER_MAX, - .may_unmap = 1, - .may_swap = 1, - .swappiness = vm_swappiness, - .order = 0, - .mem_cgroup = NULL, - .nodemask = NULL, - }; - - spin_lock_irq(&zone->lru_lock); - - update_isolated_counts(zone, &sc, &nr_anon, &nr_file, page_list); - - spin_unlock_irq(&zone->lru_lock); - - nr_reclaimed = shrink_page_list(page_list, zone, &sc); - - __count_zone_vm_events(PGSTEAL, zone, nr_reclaimed); - - putback_lru_pages(zone, &sc, nr_anon, nr_file, page_list); - - return nr_reclaimed; -} - -EXPORT_SYMBOL(zone_id_shrink_pagelist); -#endif /* CONFIG_ZRAM_FOR_ANDROID */ - /* * This moves pages from the active list to the inactive list. * -- cgit v1.1 From dc3b0f18a7172f56a5ba1375c3e2f15fed6a9b25 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Mon, 14 May 2012 11:19:40 -0500 Subject: Staging: zram: Refactor zram_read/write() functions This patch refactor the code of zram_read/write() functions. It does not removes a lot of duplicate code alone, but is mostly a helper for the third patch of this series (Staging: zram: allow partial page operations). Change-Id: Iaa0d88d443361b58776977a78e25eaab60a5b9e3 Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Hrycay Reviewed-on: http://gerrit.pcs.mot.com/414991 Tested-by: Jira Key Reviewed-by: Christopher Fries Reviewed-by: Neil Patel Reviewed-by: David Ding --- drivers/staging/zram/zram_drv.c | 315 ++++++++++++++++++++-------------------- 1 file changed, 155 insertions(+), 160 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index aab4ec4..9b31163 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -204,196 +204,199 @@ static void handle_uncompressed_page(struct zram *zram, flush_dcache_page(page); } -static void zram_read(struct zram *zram, struct bio *bio) +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, struct bio *bio) { + int ret; + size_t clen; + struct page *page; + struct zobj_header *zheader; + unsigned char *user_mem, *cmem; - int i; - u32 index; - struct bio_vec *bvec; - - zram_stat64_inc(zram, &zram->stats.num_reads); - index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; - - bio_for_each_segment(bvec, bio, i) { - int ret; - size_t clen; - struct page *page; - struct zobj_header *zheader; - unsigned char *user_mem, *cmem; - - page = bvec->bv_page; - - if (zram_test_flag(zram, index, ZRAM_ZERO)) { - handle_zero_page(page); - index++; - continue; - } + page = bvec->bv_page; - /* Requested page is not present in compressed area */ - if (unlikely(!zram->table[index].page)) { - pr_debug("Read before write: sector=%lu, size=%u", - (ulong)(bio->bi_sector), bio->bi_size); - handle_zero_page(page); - index++; - continue; - } + if (zram_test_flag(zram, index, ZRAM_ZERO)) { + handle_zero_page(page); + return 0; + } - /* Page is stored uncompressed since it's incompressible */ - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - handle_uncompressed_page(zram, page, index); - index++; - continue; - } + /* Requested page is not present in compressed area */ + if (unlikely(!zram->table[index].page)) { + pr_debug("Read before write: sector=%lu, size=%u", + (ulong)(bio->bi_sector), bio->bi_size); + handle_zero_page(page); + return 0; + } - user_mem = kmap_atomic(page, KM_USER0); - clen = PAGE_SIZE; + /* Page is stored uncompressed since it's incompressible */ + if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { + handle_uncompressed_page(zram, page, index); + return 0; + } - cmem = kmap_atomic(zram->table[index].page, KM_USER1) + - zram->table[index].offset; + user_mem = kmap_atomic(page, KM_USER0); + clen = PAGE_SIZE; - ret = lzo1x_decompress_safe( - cmem + sizeof(*zheader), - xv_get_object_size(cmem) - sizeof(*zheader), - user_mem, &clen); + cmem = kmap_atomic(zram->table[index].page, KM_USER1) + + zram->table[index].offset; - kunmap_atomic(user_mem, KM_USER0); - kunmap_atomic(cmem, KM_USER1); + ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), + xv_get_object_size(cmem) - sizeof(*zheader), + user_mem, &clen); - /* Should NEVER happen. Return bio error if it does. */ - if (unlikely(ret != LZO_E_OK)) { - pr_err("Decompression failed! err=%d, page=%u\n", - ret, index); - zram_stat64_inc(zram, &zram->stats.failed_reads); - goto out; - } + kunmap_atomic(user_mem, KM_USER0); + kunmap_atomic(cmem, KM_USER1); - flush_dcache_page(page); - index++; + /* Should NEVER happen. Return bio error if it does. */ + if (unlikely(ret != LZO_E_OK)) { + pr_err("Decompression failed! err=%d, page=%u\n", ret, index); + zram_stat64_inc(zram, &zram->stats.failed_reads); + return ret; } - set_bit(BIO_UPTODATE, &bio->bi_flags); - bio_endio(bio, 0); - return; + flush_dcache_page(page); -out: - bio_io_error(bio); + return 0; } -static void zram_write(struct zram *zram, struct bio *bio) +static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) { - int i; - u32 index; - struct bio_vec *bvec; + int ret; + u32 offset; + size_t clen; + struct zobj_header *zheader; + struct page *page, *page_store; + unsigned char *user_mem, *cmem, *src; - zram_stat64_inc(zram, &zram->stats.num_writes); - index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; + page = bvec->bv_page; + src = zram->compress_buffer; - bio_for_each_segment(bvec, bio, i) { - int ret; - u32 offset; - size_t clen; - struct zobj_header *zheader; - struct page *page, *page_store; - unsigned char *user_mem, *cmem, *src; + /* + * System overwrites unused sectors. Free memory associated + * with this sector now. + */ + if (zram->table[index].page || + zram_test_flag(zram, index, ZRAM_ZERO)) + zram_free_page(zram, index); - page = bvec->bv_page; - src = zram->compress_buffer; + mutex_lock(&zram->lock); - /* - * System overwrites unused sectors. Free memory associated - * with this sector now. - */ - if (zram->table[index].page || - zram_test_flag(zram, index, ZRAM_ZERO)) - zram_free_page(zram, index); + user_mem = kmap_atomic(page, KM_USER0); + if (page_zero_filled(user_mem)) { + kunmap_atomic(user_mem, KM_USER0); + mutex_unlock(&zram->lock); + zram_stat_inc(&zram->stats.pages_zero); + zram_set_flag(zram, index, ZRAM_ZERO); + return 0; + } - mutex_lock(&zram->lock); + ret = lzo1x_1_compress(user_mem, PAGE_SIZE, src, &clen, + zram->compress_workmem); - user_mem = kmap_atomic(page, KM_USER0); - if (page_zero_filled(user_mem)) { - kunmap_atomic(user_mem, KM_USER0); - mutex_unlock(&zram->lock); - zram_stat_inc(&zram->stats.pages_zero); - zram_set_flag(zram, index, ZRAM_ZERO); - index++; - continue; - } - - ret = lzo1x_1_compress(user_mem, PAGE_SIZE, src, &clen, - zram->compress_workmem); + kunmap_atomic(user_mem, KM_USER0); - kunmap_atomic(user_mem, KM_USER0); + if (unlikely(ret != LZO_E_OK)) { + mutex_unlock(&zram->lock); + pr_err("Compression failed! err=%d\n", ret); + zram_stat64_inc(zram, &zram->stats.failed_writes); + return ret; + } - if (unlikely(ret != LZO_E_OK)) { + /* + * Page is incompressible. Store it as-is (uncompressed) + * since we do not want to return too many disk write + * errors which has side effect of hanging the system. + */ + if (unlikely(clen > max_zpage_size)) { + clen = PAGE_SIZE; + page_store = alloc_page(GFP_NOIO | __GFP_HIGHMEM); + if (unlikely(!page_store)) { mutex_unlock(&zram->lock); - pr_err("Compression failed! err=%d\n", ret); + pr_info("Error allocating memory for " + "incompressible page: %u\n", index); zram_stat64_inc(zram, &zram->stats.failed_writes); - goto out; - } - - /* - * Page is incompressible. Store it as-is (uncompressed) - * since we do not want to return too many disk write - * errors which has side effect of hanging the system. - */ - if (unlikely(clen > max_zpage_size)) { - clen = PAGE_SIZE; - page_store = alloc_page(GFP_NOIO | __GFP_HIGHMEM); - if (unlikely(!page_store)) { - mutex_unlock(&zram->lock); - pr_info("Error allocating memory for " - "incompressible page: %u\n", index); - zram_stat64_inc(zram, - &zram->stats.failed_writes); - goto out; + return -ENOMEM; } - offset = 0; - zram_set_flag(zram, index, ZRAM_UNCOMPRESSED); - zram_stat_inc(&zram->stats.pages_expand); - zram->table[index].page = page_store; - src = kmap_atomic(page, KM_USER0); - goto memstore; - } + offset = 0; + zram_set_flag(zram, index, ZRAM_UNCOMPRESSED); + zram_stat_inc(&zram->stats.pages_expand); + zram->table[index].page = page_store; + src = kmap_atomic(page, KM_USER0); + goto memstore; + } - if (xv_malloc(zram->mem_pool, clen + sizeof(*zheader), - &zram->table[index].page, &offset, - GFP_NOIO | __GFP_HIGHMEM)) { - mutex_unlock(&zram->lock); - pr_info("Error allocating memory for compressed " - "page: %u, size=%zu\n", index, clen); - zram_stat64_inc(zram, &zram->stats.failed_writes); - goto out; - } + if (xv_malloc(zram->mem_pool, clen + sizeof(*zheader), + &zram->table[index].page, &offset, + GFP_NOIO | __GFP_HIGHMEM)) { + mutex_unlock(&zram->lock); + pr_info("Error allocating memory for compressed " + "page: %u, size=%zu\n", index, clen); + zram_stat64_inc(zram, &zram->stats.failed_writes); + return -ENOMEM; + } memstore: - zram->table[index].offset = offset; + zram->table[index].offset = offset; - cmem = kmap_atomic(zram->table[index].page, KM_USER1) + - zram->table[index].offset; + cmem = kmap_atomic(zram->table[index].page, KM_USER1) + + zram->table[index].offset; #if 0 - /* Back-reference needed for memory defragmentation */ - if (!zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)) { - zheader = (struct zobj_header *)cmem; - zheader->table_idx = index; - cmem += sizeof(*zheader); - } + /* Back-reference needed for memory defragmentation */ + if (!zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)) { + zheader = (struct zobj_header *)cmem; + zheader->table_idx = index; + cmem += sizeof(*zheader); + } #endif - memcpy(cmem, src, clen); + memcpy(cmem, src, clen); - kunmap_atomic(cmem, KM_USER1); - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) - kunmap_atomic(src, KM_USER0); + kunmap_atomic(cmem, KM_USER1); + if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) + kunmap_atomic(src, KM_USER0); - /* Update stats */ - zram_stat64_add(zram, &zram->stats.compr_size, clen); - zram_stat_inc(&zram->stats.pages_stored); - if (clen <= PAGE_SIZE / 2) - zram_stat_inc(&zram->stats.good_compress); + /* Update stats */ + zram_stat64_add(zram, &zram->stats.compr_size, clen); + zram_stat_inc(&zram->stats.pages_stored); + if (clen <= PAGE_SIZE / 2) + zram_stat_inc(&zram->stats.good_compress); - mutex_unlock(&zram->lock); + mutex_unlock(&zram->lock); + + return 0; +} + +static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, + struct bio *bio, int rw) +{ + if (rw == READ) + return zram_bvec_read(zram, bvec, index, bio); + + return zram_bvec_write(zram, bvec, index); +} + +static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) +{ + int i; + u32 index; + struct bio_vec *bvec; + + switch (rw) { + case READ: + zram_stat64_inc(zram, &zram->stats.num_reads); + break; + case WRITE: + zram_stat64_inc(zram, &zram->stats.num_writes); + break; + } + + index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; + + bio_for_each_segment(bvec, bio, i) { + if (zram_bvec_rw(zram, bvec, index, bio, rw) < 0) + goto out; index++; } @@ -440,15 +443,7 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) return 0; } - switch (bio_data_dir(bio)) { - case READ: - zram_read(zram, bio); - break; - - case WRITE: - zram_write(zram, bio); - break; - } + __zram_make_request(zram, bio, bio_data_dir(bio)); return 0; } -- cgit v1.1 From f526a35a01423d3760e773604b763e8a7aeeb6c1 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Mon, 14 May 2012 11:21:07 -0500 Subject: Staging: zram: Replace mutex lock by a R/W semaphore Currently, nothing protects zram table from concurrent access. For instance, ZRAM_UNCOMPRESSED bit can be cleared by zram_free_page() called from a concurrent write between the time ZRAM_UNCOMPRESSED has been set and the time it is tested to unmap KM_USER0 in zram_bvec_write(). This ultimately leads to kernel panic. Also, a read request can occurs when the page has been freed by a running write request and before it has been updated, leading to zero filled block being incorrectly read and "Read before write" error message. This patch replace the current mutex by a rw_semaphore. It extends the protection to zram table (currently, only compression buffers are protected) and read requests (currently, only write requests are protected). Change-Id: Ie2b2608ff55dd655037b48a6f7370a819c9b581a Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Hrycay Reviewed-on: http://gerrit.pcs.mot.com/414992 Tested-by: Jira Key Reviewed-by: Christopher Fries Reviewed-by: Neil Patel Reviewed-by: David Ding --- drivers/staging/zram/zram_drv.c | 25 +++++++++++++------------ drivers/staging/zram/zram_drv.h | 4 ++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 9b31163..2095478 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -279,12 +279,9 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) zram_test_flag(zram, index, ZRAM_ZERO)) zram_free_page(zram, index); - mutex_lock(&zram->lock); - user_mem = kmap_atomic(page, KM_USER0); if (page_zero_filled(user_mem)) { kunmap_atomic(user_mem, KM_USER0); - mutex_unlock(&zram->lock); zram_stat_inc(&zram->stats.pages_zero); zram_set_flag(zram, index, ZRAM_ZERO); return 0; @@ -296,7 +293,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) kunmap_atomic(user_mem, KM_USER0); if (unlikely(ret != LZO_E_OK)) { - mutex_unlock(&zram->lock); pr_err("Compression failed! err=%d\n", ret); zram_stat64_inc(zram, &zram->stats.failed_writes); return ret; @@ -311,7 +307,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) clen = PAGE_SIZE; page_store = alloc_page(GFP_NOIO | __GFP_HIGHMEM); if (unlikely(!page_store)) { - mutex_unlock(&zram->lock); pr_info("Error allocating memory for " "incompressible page: %u\n", index); zram_stat64_inc(zram, &zram->stats.failed_writes); @@ -329,7 +324,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) if (xv_malloc(zram->mem_pool, clen + sizeof(*zheader), &zram->table[index].page, &offset, GFP_NOIO | __GFP_HIGHMEM)) { - mutex_unlock(&zram->lock); pr_info("Error allocating memory for compressed " "page: %u, size=%zu\n", index, clen); zram_stat64_inc(zram, &zram->stats.failed_writes); @@ -363,18 +357,25 @@ memstore: if (clen <= PAGE_SIZE / 2) zram_stat_inc(&zram->stats.good_compress); - mutex_unlock(&zram->lock); - return 0; } static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, struct bio *bio, int rw) { - if (rw == READ) - return zram_bvec_read(zram, bvec, index, bio); + int ret; - return zram_bvec_write(zram, bvec, index); + if (rw == READ) { + down_read(&zram->lock); + ret = zram_bvec_read(zram, bvec, index, bio); + up_read(&zram->lock); + } else { + down_write(&zram->lock); + ret = zram_bvec_write(zram, bvec, index); + up_write(&zram->lock); + } + + return ret; } static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) @@ -574,7 +575,7 @@ static int create_device(struct zram *zram, int device_id) { int ret = 0; - mutex_init(&zram->lock); + init_rwsem(&zram->lock); mutex_init(&zram->init_lock); spin_lock_init(&zram->stat64_lock); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 408b2c0..1db8f19 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -104,8 +104,8 @@ struct zram { void *compress_buffer; struct table *table; spinlock_t stat64_lock; /* protect 64-bit stats */ - struct mutex lock; /* protect compression buffers against - * concurrent writes */ + struct rw_semaphore lock; /* protect compression buffers against + * concurrent writes */ struct request_queue *queue; struct gendisk *disk; int init_done; -- cgit v1.1 From 828121d02455e8c6c4574a7ff4d9ac8f803dcc17 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Mon, 14 May 2012 11:46:34 -0500 Subject: staging: zram: fix zram locking Currently init_lock only prevents concurrent execution of zram_init_device() and zram_reset_device() but not zram_make_request() nor sysfs store functions. This patch changes init_lock into a rw_semaphore. A write lock is taken by init, reset and store functions, a read lock is taken by zram_make_request(). Also, avoids to release the lock before calling __zram_reset_device() for cleaning after a failed init, thus preventing any concurrent task to see an inconsistent state of zram. Change-Id: Ia74662a6d25ed280438a7c726005c17ebd3f7ff9 Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jason Hrycay Reviewed-on: http://gerrit.pcs.mot.com/414993 Tested-by: Jira Key Reviewed-by: Christopher Fries Reviewed-by: Neil Patel Reviewed-by: David Ding --- drivers/staging/zram/zram_drv.c | 46 ++++++++++++++++++++++++--------------- drivers/staging/zram/zram_drv.h | 6 ++--- drivers/staging/zram/zram_sysfs.c | 18 ++++++++++----- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 2095478..b9d4fd1 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -433,27 +433,34 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) { struct zram *zram = queue->queuedata; + if (unlikely(!zram->init_done) && zram_init_device(zram)) + goto error; + + down_read(&zram->init_lock); + if (unlikely(!zram->init_done)) + goto error_unlock; + if (!valid_io_request(zram, bio)) { zram_stat64_inc(zram, &zram->stats.invalid_io); - bio_io_error(bio); - return 0; - } - - if (unlikely(!zram->init_done) && zram_init_device(zram)) { - bio_io_error(bio); - return 0; + goto error_unlock; } __zram_make_request(zram, bio, bio_data_dir(bio)); + up_read(&zram->init_lock); return 0; + +error_unlock: + up_read(&zram->init_lock); +error: + bio_io_error(bio); + return 0; } -void zram_reset_device(struct zram *zram) +void __zram_reset_device(struct zram *zram) { size_t index; - mutex_lock(&zram->init_lock); zram->init_done = 0; /* Free various per-device buffers */ @@ -490,7 +497,13 @@ void zram_reset_device(struct zram *zram) memset(&zram->stats, 0, sizeof(zram->stats)); zram->disksize = 0; - mutex_unlock(&zram->init_lock); +} + +void zram_reset_device(struct zram *zram) +{ + down_write(&zram->init_lock); + __zram_reset_device(zram); + up_write(&zram->init_lock); } int zram_init_device(struct zram *zram) @@ -498,10 +511,10 @@ int zram_init_device(struct zram *zram) int ret; size_t num_pages; - mutex_lock(&zram->init_lock); + down_write(&zram->init_lock); if (zram->init_done) { - mutex_unlock(&zram->init_lock); + up_write(&zram->init_lock); return 0; } @@ -544,15 +557,14 @@ int zram_init_device(struct zram *zram) } zram->init_done = 1; - mutex_unlock(&zram->init_lock); + up_write(&zram->init_lock); pr_debug("Initialization done!\n"); return 0; fail: - mutex_unlock(&zram->init_lock); - zram_reset_device(zram); - + __zram_reset_device(zram); + up_write(&zram->init_lock); pr_err("Initialization failed: err=%d\n", ret); return ret; } @@ -576,7 +588,7 @@ static int create_device(struct zram *zram, int device_id) int ret = 0; init_rwsem(&zram->lock); - mutex_init(&zram->init_lock); + init_rwsem(&zram->init_lock); spin_lock_init(&zram->stat64_lock); zram->queue = blk_alloc_queue(GFP_KERNEL); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 1db8f19..f8e2654 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -109,8 +109,8 @@ struct zram { struct request_queue *queue; struct gendisk *disk; int init_done; - /* Prevent concurrent execution of device init and reset */ - struct mutex init_lock; + /* Prevent concurrent execution of device init, reset and R/W request */ + struct rw_semaphore init_lock; /* * This is the limit on amount of *uncompressed* worth of data * we can store in a disk. @@ -127,6 +127,6 @@ extern struct attribute_group zram_disk_attr_group; #endif extern int zram_init_device(struct zram *zram); -extern void zram_reset_device(struct zram *zram); +extern void __zram_reset_device(struct zram *zram); #endif diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 6996eb3..bbcde10 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -55,19 +55,23 @@ static ssize_t disksize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int ret; + u64 disksize; struct zram *zram = dev_to_zram(dev); + ret = strict_strtoull(buf, 10, &disksize); + if (ret) + return ret; + + down_write(&zram->init_lock); if (zram->init_done) { + up_write(&zram->init_lock); pr_info("Cannot change disksize for initialized device\n"); return -EBUSY; } - ret = strict_strtoull(buf, 10, &zram->disksize); - if (ret) - return ret; - - zram->disksize = PAGE_ALIGN(zram->disksize); + zram->disksize = PAGE_ALIGN(disksize); set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + up_write(&zram->init_lock); return len; } @@ -113,8 +117,10 @@ static ssize_t reset_store(struct device *dev, if (bdev) fsync_bdev(bdev); + down_write(&zram->init_lock); if (zram->init_done) - zram_reset_device(zram); + __zram_reset_device(zram); + up_write(&zram->init_lock); return len; } -- cgit v1.1 From 848d1bbacdfe06844f68e8b75ca666ff4604c80c Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Tue, 6 Sep 2011 15:02:12 +0200 Subject: staging: zram: prevent accessing an unallocated table when init fails early When the allocation of zram->table fails, we set zram->disksize to zero to prevent accessing the unallocated table entries during cleanup. However, we currently don't take this precaution when the initialization fails earlier. Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index b9d4fd1..58829ae 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -524,24 +524,22 @@ int zram_init_device(struct zram *zram) if (!zram->compress_workmem) { pr_err("Error allocating compressor working memory!\n"); ret = -ENOMEM; - goto fail; + goto fail_no_table; } zram->compress_buffer = (void *)__get_free_pages(__GFP_ZERO, 1); if (!zram->compress_buffer) { pr_err("Error allocating compressor buffer space\n"); ret = -ENOMEM; - goto fail; + goto fail_no_table; } num_pages = zram->disksize >> PAGE_SHIFT; zram->table = vzalloc(num_pages * sizeof(*zram->table)); if (!zram->table) { pr_err("Error allocating zram address table\n"); - /* To prevent accessing table entries during cleanup */ - zram->disksize = 0; ret = -ENOMEM; - goto fail; + goto fail_no_table; } set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); @@ -562,6 +560,9 @@ int zram_init_device(struct zram *zram) pr_debug("Initialization done!\n"); return 0; +fail_no_table: + /* To prevent accessing table entries during cleanup */ + zram->disksize = 0; fail: __zram_reset_device(zram); up_write(&zram->init_lock); -- cgit v1.1 From c3fb096d4a4de7eedaafcce4b5c7c387467913a2 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Fri, 9 Sep 2011 19:01:00 -0400 Subject: zram: Fix sparse warnings Fixes sparse warning: zram_drv.c:666:6: warning: symbol 'zram_slot_free_notify' was not declared. Should it be static? Also, max_zpage_size is now size_t just to be consistent with data-type of other variables maintaining sizes of various kinds. Signed-off-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 3 ++- drivers/staging/zram/zram_drv.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 58829ae..372bd51 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -570,7 +570,8 @@ fail: return ret; } -void zram_slot_free_notify(struct block_device *bdev, unsigned long index) +static void zram_slot_free_notify(struct block_device *bdev, + unsigned long index) { struct zram *zram; diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index f8e2654..5b628c8 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -47,7 +47,7 @@ static const unsigned default_disksize_perc_ram = 25; * Pages that compress to size greater than this are stored * uncompressed in memory. */ -static const unsigned max_zpage_size = PAGE_SIZE / 4 * 3; +static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; /* * NOTE: max_zpage_size must be less than or equal to: -- cgit v1.1 From b6eba99afe2c3c70c68fce5237d9c50c865839d7 Mon Sep 17 00:00:00 2001 From: Sergey Datsevich Date: Wed, 7 Dec 2011 19:06:37 +0100 Subject: Staging: zram/zram_sysfs.c: Fixed call of obsolete function strict_strtoX As reported by checkpatch.pl strict_strtoX is obsolet and should be replaced by kstrtoX. Signed-off-by: Sergey Datsevich Signed-off-by: Bjoern Meier Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index bbcde10..c070ea9 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -58,7 +58,7 @@ static ssize_t disksize_store(struct device *dev, u64 disksize; struct zram *zram = dev_to_zram(dev); - ret = strict_strtoull(buf, 10, &disksize); + ret = kstrtoull(buf, 10, &disksize); if (ret) return ret; @@ -95,7 +95,7 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int ret; - unsigned long do_reset; + unsigned short do_reset; struct zram *zram; struct block_device *bdev; @@ -106,7 +106,7 @@ static ssize_t reset_store(struct device *dev, if (bdev->bd_holders) return -EBUSY; - ret = strict_strtoul(buf, 10, &do_reset); + ret = kstrtou16(buf, 10, &do_reset); if (ret) return ret; -- cgit v1.1 From 3735d9424d15ae778ea6b7a748a6e7bab19d4372 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Wed, 30 Nov 2011 14:16:16 +0100 Subject: Staging: zram: Add a missing GFP_KERNEL specifier in zram_init_device() The allocation of zram->compress_buffer is misssing a GFP_* specifier. This is equivalent to GFP_NOWAIT but it is more likely a omission. Since the allocation just above it uses GFP_KERNEL, there is no reason to use GFP_NOWAIT here. Therefore, add GFP_KERNEL. Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 372bd51..3f2514d 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -527,7 +527,8 @@ int zram_init_device(struct zram *zram) goto fail_no_table; } - zram->compress_buffer = (void *)__get_free_pages(__GFP_ZERO, 1); + zram->compress_buffer = + (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); if (!zram->compress_buffer) { pr_err("Error allocating compressor buffer space\n"); ret = -ENOMEM; -- cgit v1.1 From bbf4585ce61d29d363b83efb76b4748c144ab12b Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Fri, 10 Jun 2011 15:28:46 +0200 Subject: Staging: zram: Remove useless offset calculation in handle_uncompressed_page() The offset of uncompressed page is always zero: handle_uncompressed_page() doesn't have to care about it. Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 3f2514d..803a4cc 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -194,8 +194,7 @@ static void handle_uncompressed_page(struct zram *zram, unsigned char *user_mem, *cmem; user_mem = kmap_atomic(page, KM_USER0); - cmem = kmap_atomic(zram->table[index].page, KM_USER1) + - zram->table[index].offset; + cmem = kmap_atomic(zram->table[index].page, KM_USER1); memcpy(user_mem, cmem, PAGE_SIZE); kunmap_atomic(user_mem, KM_USER0); -- cgit v1.1 From 16993cd17643490403e1800ab37f2d6a4acc62ae Mon Sep 17 00:00:00 2001 From: Noah Watkins Date: Wed, 20 Jul 2011 17:05:57 -0600 Subject: staging: zram: make global var "devices" use unique name The global variable "devices" is too general to be global. This patch switches the name to be "zram_devices". Signed-off-by: Noah Watkins Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 16 ++++++++-------- drivers/staging/zram/zram_drv.h | 2 +- drivers/staging/zram/zram_sysfs.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 803a4cc..c329815 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -37,7 +37,7 @@ /* Globals */ static int zram_major; -struct zram *devices; +struct zram *zram_devices; /* Module params (documentation at end) */ unsigned int num_devices; @@ -688,14 +688,14 @@ static int __init zram_init(void) /* Allocate the device array and initialize each one */ pr_info("Creating %u devices ...\n", num_devices); - devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); - if (!devices) { + zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); + if (!zram_devices) { ret = -ENOMEM; goto unregister; } for (dev_id = 0; dev_id < num_devices; dev_id++) { - ret = create_device(&devices[dev_id], dev_id); + ret = create_device(&zram_devices[dev_id], dev_id); if (ret) goto free_devices; } @@ -704,8 +704,8 @@ static int __init zram_init(void) free_devices: while (dev_id) - destroy_device(&devices[--dev_id]); - kfree(devices); + destroy_device(&zram_devices[--dev_id]); + kfree(zram_devices); unregister: unregister_blkdev(zram_major, "zram"); out: @@ -718,7 +718,7 @@ static void __exit zram_exit(void) struct zram *zram; for (i = 0; i < num_devices; i++) { - zram = &devices[i]; + zram = &zram_devices[i]; destroy_device(zram); if (zram->init_done) @@ -727,7 +727,7 @@ static void __exit zram_exit(void) unregister_blkdev(zram_major, "zram"); - kfree(devices); + kfree(zram_devices); pr_debug("Cleanup done!\n"); } diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 5b628c8..36fad9a 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -120,7 +120,7 @@ struct zram { struct zram_stats stats; }; -extern struct zram *devices; +extern struct zram *zram_devices; extern unsigned int num_devices; #ifdef CONFIG_SYSFS extern struct attribute_group zram_disk_attr_group; diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index c070ea9..a4a33f6 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -35,7 +35,7 @@ static struct zram *dev_to_zram(struct device *dev) struct zram *zram = NULL; for (i = 0; i < num_devices; i++) { - zram = &devices[i]; + zram = &zram_devices[i]; if (disk_to_dev(zram->disk) == dev) break; } -- cgit v1.1 From 42d1b3d95c6eb88da560a9259a3eb9074cb09ea2 Mon Sep 17 00:00:00 2001 From: Noah Watkins Date: Wed, 20 Jul 2011 17:06:08 -0600 Subject: staging: zram: make global var "num_devices" use unique name The global variable "num_devices" is too general to be global. This patch switches the name to be "zram_num_devices". Signed-off-by: Noah Watkins Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 22 +++++++++++----------- drivers/staging/zram/zram_drv.h | 2 +- drivers/staging/zram/zram_sysfs.c | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index c329815..412b312 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -40,7 +40,7 @@ static int zram_major; struct zram *zram_devices; /* Module params (documentation at end) */ -unsigned int num_devices; +unsigned int zram_num_devices; static void zram_stat_inc(u32 *v) { @@ -667,9 +667,9 @@ static int __init zram_init(void) { int ret, dev_id; - if (num_devices > max_num_devices) { + if (zram_num_devices > max_num_devices) { pr_warning("Invalid value for num_devices: %u\n", - num_devices); + zram_num_devices); ret = -EINVAL; goto out; } @@ -681,20 +681,20 @@ static int __init zram_init(void) goto out; } - if (!num_devices) { + if (!zram_num_devices) { pr_info("num_devices not specified. Using default: 1\n"); - num_devices = 1; + zram_num_devices = 1; } /* Allocate the device array and initialize each one */ - pr_info("Creating %u devices ...\n", num_devices); - zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); + pr_info("Creating %u devices ...\n", zram_num_devices); + zram_devices = kzalloc(zram_num_devices * sizeof(struct zram), GFP_KERNEL); if (!zram_devices) { ret = -ENOMEM; goto unregister; } - for (dev_id = 0; dev_id < num_devices; dev_id++) { + for (dev_id = 0; dev_id < zram_num_devices; dev_id++) { ret = create_device(&zram_devices[dev_id], dev_id); if (ret) goto free_devices; @@ -717,7 +717,7 @@ static void __exit zram_exit(void) int i; struct zram *zram; - for (i = 0; i < num_devices; i++) { + for (i = 0; i < zram_num_devices; i++) { zram = &zram_devices[i]; destroy_device(zram); @@ -731,8 +731,8 @@ static void __exit zram_exit(void) pr_debug("Cleanup done!\n"); } -module_param(num_devices, uint, 0); -MODULE_PARM_DESC(num_devices, "Number of zram devices"); +module_param(zram_num_devices, uint, 0); +MODULE_PARM_DESC(zram_num_devices, "Number of zram devices"); module_init(zram_init); module_exit(zram_exit); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 36fad9a..9124e34 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -121,7 +121,7 @@ struct zram { }; extern struct zram *zram_devices; -extern unsigned int num_devices; +extern unsigned int zram_num_devices; #ifdef CONFIG_SYSFS extern struct attribute_group zram_disk_attr_group; #endif diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index a4a33f6..6eecf1d 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -34,7 +34,7 @@ static struct zram *dev_to_zram(struct device *dev) int i; struct zram *zram = NULL; - for (i = 0; i < num_devices; i++) { + for (i = 0; i < zram_num_devices; i++) { zram = &zram_devices[i]; if (disk_to_dev(zram->disk) == dev) break; -- cgit v1.1 From 3165e8cd2023f7f722e781180a5a187bef18141a Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Mon, 9 Jan 2012 16:52:00 -0600 Subject: staging: zram: remove xvmalloc Removes the xvmalloc allocator code from the zram driver Signed-off-by: Nitin Gupta Acked-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Makefile | 1 - drivers/staging/zram/xvmalloc.c | 510 ------------------------------------ drivers/staging/zram/xvmalloc.h | 30 --- drivers/staging/zram/xvmalloc_int.h | 95 ------- 4 files changed, 636 deletions(-) delete mode 100644 drivers/staging/zram/xvmalloc.c delete mode 100644 drivers/staging/zram/xvmalloc.h delete mode 100644 drivers/staging/zram/xvmalloc_int.h diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ae62e92..c7d5f89 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio/ obj-$(CONFIG_ZRAM) += zram/ -obj-$(CONFIG_XVMALLOC) += zram/ obj-$(CONFIG_ZCACHE) += zcache/ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c deleted file mode 100644 index 1f9c508..0000000 --- a/drivers/staging/zram/xvmalloc.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * xvmalloc memory allocator - * - * Copyright (C) 2008, 2009, 2010 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the licence that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - */ - -#ifdef CONFIG_ZRAM_DEBUG -#define DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xvmalloc.h" -#include "xvmalloc_int.h" - -static void stat_inc(u64 *value) -{ - *value = *value + 1; -} - -static void stat_dec(u64 *value) -{ - *value = *value - 1; -} - -static int test_flag(struct block_header *block, enum blockflags flag) -{ - return block->prev & BIT(flag); -} - -static void set_flag(struct block_header *block, enum blockflags flag) -{ - block->prev |= BIT(flag); -} - -static void clear_flag(struct block_header *block, enum blockflags flag) -{ - block->prev &= ~BIT(flag); -} - -/* - * Given pair, provide a dereferencable pointer. - * This is called from xv_malloc/xv_free path, so it - * needs to be fast. - */ -static void *get_ptr_atomic(struct page *page, u16 offset, enum km_type type) -{ - unsigned char *base; - - base = kmap_atomic(page, type); - return base + offset; -} - -static void put_ptr_atomic(void *ptr, enum km_type type) -{ - kunmap_atomic(ptr, type); -} - -static u32 get_blockprev(struct block_header *block) -{ - return block->prev & PREV_MASK; -} - -static void set_blockprev(struct block_header *block, u16 new_offset) -{ - block->prev = new_offset | (block->prev & FLAGS_MASK); -} - -static struct block_header *BLOCK_NEXT(struct block_header *block) -{ - return (struct block_header *) - ((char *)block + block->size + XV_ALIGN); -} - -/* - * Get index of free list containing blocks of maximum size - * which is less than or equal to given size. - */ -static u32 get_index_for_insert(u32 size) -{ - if (unlikely(size > XV_MAX_ALLOC_SIZE)) - size = XV_MAX_ALLOC_SIZE; - size &= ~FL_DELTA_MASK; - return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT; -} - -/* - * Get index of free list having blocks of size greater than - * or equal to requested size. - */ -static u32 get_index(u32 size) -{ - if (unlikely(size < XV_MIN_ALLOC_SIZE)) - size = XV_MIN_ALLOC_SIZE; - size = ALIGN(size, FL_DELTA); - return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT; -} - -/** - * find_block - find block of at least given size - * @pool: memory pool to search from - * @size: size of block required - * @page: page containing required block - * @offset: offset within the page where block is located. - * - * Searches two level bitmap to locate block of at least - * the given size. If such a block is found, it provides - * to identify this block and returns index - * in freelist where we found this block. - * Otherwise, returns 0 and params are not touched. - */ -static u32 find_block(struct xv_pool *pool, u32 size, - struct page **page, u32 *offset) -{ - ulong flbitmap, slbitmap; - u32 flindex, slindex, slbitstart; - - /* There are no free blocks in this pool */ - if (!pool->flbitmap) - return 0; - - /* Get freelist index correspoding to this size */ - slindex = get_index(size); - slbitmap = pool->slbitmap[slindex / BITS_PER_LONG]; - slbitstart = slindex % BITS_PER_LONG; - - /* - * If freelist is not empty at this index, we found the - * block - head of this list. This is approximate best-fit match. - */ - if (test_bit(slbitstart, &slbitmap)) { - *page = pool->freelist[slindex].page; - *offset = pool->freelist[slindex].offset; - return slindex; - } - - /* - * No best-fit found. Search a bit further in bitmap for a free block. - * Second level bitmap consists of series of 32-bit chunks. Search - * further in the chunk where we expected a best-fit, starting from - * index location found above. - */ - slbitstart++; - slbitmap >>= slbitstart; - - /* Skip this search if we were already at end of this bitmap chunk */ - if ((slbitstart != BITS_PER_LONG) && slbitmap) { - slindex += __ffs(slbitmap) + 1; - *page = pool->freelist[slindex].page; - *offset = pool->freelist[slindex].offset; - return slindex; - } - - /* Now do a full two-level bitmap search to find next nearest fit */ - flindex = slindex / BITS_PER_LONG; - - flbitmap = (pool->flbitmap) >> (flindex + 1); - if (!flbitmap) - return 0; - - flindex += __ffs(flbitmap) + 1; - slbitmap = pool->slbitmap[flindex]; - slindex = (flindex * BITS_PER_LONG) + __ffs(slbitmap); - *page = pool->freelist[slindex].page; - *offset = pool->freelist[slindex].offset; - - return slindex; -} - -/* - * Insert block at in freelist of given pool. - * freelist used depends on block size. - */ -static void insert_block(struct xv_pool *pool, struct page *page, u32 offset, - struct block_header *block) -{ - u32 flindex, slindex; - struct block_header *nextblock; - - slindex = get_index_for_insert(block->size); - flindex = slindex / BITS_PER_LONG; - - block->link.prev_page = NULL; - block->link.prev_offset = 0; - block->link.next_page = pool->freelist[slindex].page; - block->link.next_offset = pool->freelist[slindex].offset; - pool->freelist[slindex].page = page; - pool->freelist[slindex].offset = offset; - - if (block->link.next_page) { - nextblock = get_ptr_atomic(block->link.next_page, - block->link.next_offset, KM_USER1); - nextblock->link.prev_page = page; - nextblock->link.prev_offset = offset; - put_ptr_atomic(nextblock, KM_USER1); - /* If there was a next page then the free bits are set. */ - return; - } - - __set_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]); - __set_bit(flindex, &pool->flbitmap); -} - -/* - * Remove block from freelist. Index 'slindex' identifies the freelist. - */ -static void remove_block(struct xv_pool *pool, struct page *page, u32 offset, - struct block_header *block, u32 slindex) -{ - u32 flindex = slindex / BITS_PER_LONG; - struct block_header *tmpblock; - - if (block->link.prev_page) { - tmpblock = get_ptr_atomic(block->link.prev_page, - block->link.prev_offset, KM_USER1); - tmpblock->link.next_page = block->link.next_page; - tmpblock->link.next_offset = block->link.next_offset; - put_ptr_atomic(tmpblock, KM_USER1); - } - - if (block->link.next_page) { - tmpblock = get_ptr_atomic(block->link.next_page, - block->link.next_offset, KM_USER1); - tmpblock->link.prev_page = block->link.prev_page; - tmpblock->link.prev_offset = block->link.prev_offset; - put_ptr_atomic(tmpblock, KM_USER1); - } - - /* Is this block is at the head of the freelist? */ - if (pool->freelist[slindex].page == page - && pool->freelist[slindex].offset == offset) { - - pool->freelist[slindex].page = block->link.next_page; - pool->freelist[slindex].offset = block->link.next_offset; - - if (pool->freelist[slindex].page) { - struct block_header *tmpblock; - tmpblock = get_ptr_atomic(pool->freelist[slindex].page, - pool->freelist[slindex].offset, - KM_USER1); - tmpblock->link.prev_page = NULL; - tmpblock->link.prev_offset = 0; - put_ptr_atomic(tmpblock, KM_USER1); - } else { - /* This freelist bucket is empty */ - __clear_bit(slindex % BITS_PER_LONG, - &pool->slbitmap[flindex]); - if (!pool->slbitmap[flindex]) - __clear_bit(flindex, &pool->flbitmap); - } - } - - block->link.prev_page = NULL; - block->link.prev_offset = 0; - block->link.next_page = NULL; - block->link.next_offset = 0; -} - -/* - * Allocate a page and add it to freelist of given pool. - */ -static int grow_pool(struct xv_pool *pool, gfp_t flags) -{ - struct page *page; - struct block_header *block; - - page = alloc_page(flags); - if (unlikely(!page)) - return -ENOMEM; - - stat_inc(&pool->total_pages); - - spin_lock(&pool->lock); - block = get_ptr_atomic(page, 0, KM_USER0); - - block->size = PAGE_SIZE - XV_ALIGN; - set_flag(block, BLOCK_FREE); - clear_flag(block, PREV_FREE); - set_blockprev(block, 0); - - insert_block(pool, page, 0, block); - - put_ptr_atomic(block, KM_USER0); - spin_unlock(&pool->lock); - - return 0; -} - -/* - * Create a memory pool. Allocates freelist, bitmaps and other - * per-pool metadata. - */ -struct xv_pool *xv_create_pool(void) -{ - u32 ovhd_size; - struct xv_pool *pool; - - ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); - pool = kzalloc(ovhd_size, GFP_KERNEL); - if (!pool) - return NULL; - - spin_lock_init(&pool->lock); - - return pool; -} -EXPORT_SYMBOL_GPL(xv_create_pool); - -void xv_destroy_pool(struct xv_pool *pool) -{ - kfree(pool); -} -EXPORT_SYMBOL_GPL(xv_destroy_pool); - -/** - * xv_malloc - Allocate block of given size from pool. - * @pool: pool to allocate from - * @size: size of block to allocate - * @page: page no. that holds the object - * @offset: location of object within page - * - * On success, identifies block allocated - * and 0 is returned. On failure, is set to - * 0 and -ENOMEM is returned. - * - * Allocation requests with size > XV_MAX_ALLOC_SIZE will fail. - */ -int xv_malloc(struct xv_pool *pool, u32 size, struct page **page, - u32 *offset, gfp_t flags) -{ - int error; - u32 index, tmpsize, origsize, tmpoffset; - struct block_header *block, *tmpblock; - - *page = NULL; - *offset = 0; - origsize = size; - - if (unlikely(!size || size > XV_MAX_ALLOC_SIZE)) - return -ENOMEM; - - size = ALIGN(size, XV_ALIGN); - - spin_lock(&pool->lock); - - index = find_block(pool, size, page, offset); - - if (!*page) { - spin_unlock(&pool->lock); - if (flags & GFP_NOWAIT) - return -ENOMEM; - error = grow_pool(pool, flags); - if (unlikely(error)) - return error; - - spin_lock(&pool->lock); - index = find_block(pool, size, page, offset); - } - - if (!*page) { - spin_unlock(&pool->lock); - return -ENOMEM; - } - - block = get_ptr_atomic(*page, *offset, KM_USER0); - - remove_block(pool, *page, *offset, block, index); - - /* Split the block if required */ - tmpoffset = *offset + size + XV_ALIGN; - tmpsize = block->size - size; - tmpblock = (struct block_header *)((char *)block + size + XV_ALIGN); - if (tmpsize) { - tmpblock->size = tmpsize - XV_ALIGN; - set_flag(tmpblock, BLOCK_FREE); - clear_flag(tmpblock, PREV_FREE); - - set_blockprev(tmpblock, *offset); - if (tmpblock->size >= XV_MIN_ALLOC_SIZE) - insert_block(pool, *page, tmpoffset, tmpblock); - - if (tmpoffset + XV_ALIGN + tmpblock->size != PAGE_SIZE) { - tmpblock = BLOCK_NEXT(tmpblock); - set_blockprev(tmpblock, tmpoffset); - } - } else { - /* This block is exact fit */ - if (tmpoffset != PAGE_SIZE) - clear_flag(tmpblock, PREV_FREE); - } - - block->size = origsize; - clear_flag(block, BLOCK_FREE); - - put_ptr_atomic(block, KM_USER0); - spin_unlock(&pool->lock); - - *offset += XV_ALIGN; - - return 0; -} -EXPORT_SYMBOL_GPL(xv_malloc); - -/* - * Free block identified with - */ -void xv_free(struct xv_pool *pool, struct page *page, u32 offset) -{ - void *page_start; - struct block_header *block, *tmpblock; - - offset -= XV_ALIGN; - - spin_lock(&pool->lock); - - page_start = get_ptr_atomic(page, 0, KM_USER0); - block = (struct block_header *)((char *)page_start + offset); - - /* Catch double free bugs */ - BUG_ON(test_flag(block, BLOCK_FREE)); - - block->size = ALIGN(block->size, XV_ALIGN); - - tmpblock = BLOCK_NEXT(block); - if (offset + block->size + XV_ALIGN == PAGE_SIZE) - tmpblock = NULL; - - /* Merge next block if its free */ - if (tmpblock && test_flag(tmpblock, BLOCK_FREE)) { - /* - * Blocks smaller than XV_MIN_ALLOC_SIZE - * are not inserted in any free list. - */ - if (tmpblock->size >= XV_MIN_ALLOC_SIZE) { - remove_block(pool, page, - offset + block->size + XV_ALIGN, tmpblock, - get_index_for_insert(tmpblock->size)); - } - block->size += tmpblock->size + XV_ALIGN; - } - - /* Merge previous block if its free */ - if (test_flag(block, PREV_FREE)) { - tmpblock = (struct block_header *)((char *)(page_start) + - get_blockprev(block)); - offset = offset - tmpblock->size - XV_ALIGN; - - if (tmpblock->size >= XV_MIN_ALLOC_SIZE) - remove_block(pool, page, offset, tmpblock, - get_index_for_insert(tmpblock->size)); - - tmpblock->size += block->size + XV_ALIGN; - block = tmpblock; - } - - /* No used objects in this page. Free it. */ - if (block->size == PAGE_SIZE - XV_ALIGN) { - put_ptr_atomic(page_start, KM_USER0); - spin_unlock(&pool->lock); - - __free_page(page); - stat_dec(&pool->total_pages); - return; - } - - set_flag(block, BLOCK_FREE); - if (block->size >= XV_MIN_ALLOC_SIZE) - insert_block(pool, page, offset, block); - - if (offset + block->size + XV_ALIGN != PAGE_SIZE) { - tmpblock = BLOCK_NEXT(block); - set_flag(tmpblock, PREV_FREE); - set_blockprev(tmpblock, offset); - } - - put_ptr_atomic(page_start, KM_USER0); - spin_unlock(&pool->lock); -} -EXPORT_SYMBOL_GPL(xv_free); - -u32 xv_get_object_size(void *obj) -{ - struct block_header *blk; - - blk = (struct block_header *)((char *)(obj) - XV_ALIGN); - return blk->size; -} -EXPORT_SYMBOL_GPL(xv_get_object_size); - -/* - * Returns total memory used by allocator (userdata + metadata) - */ -u64 xv_get_total_size_bytes(struct xv_pool *pool) -{ - return pool->total_pages << PAGE_SHIFT; -} -EXPORT_SYMBOL_GPL(xv_get_total_size_bytes); diff --git a/drivers/staging/zram/xvmalloc.h b/drivers/staging/zram/xvmalloc.h deleted file mode 100644 index 5b1a81a..0000000 --- a/drivers/staging/zram/xvmalloc.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * xvmalloc memory allocator - * - * Copyright (C) 2008, 2009, 2010 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the licence that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - */ - -#ifndef _XV_MALLOC_H_ -#define _XV_MALLOC_H_ - -#include - -struct xv_pool; - -struct xv_pool *xv_create_pool(void); -void xv_destroy_pool(struct xv_pool *pool); - -int xv_malloc(struct xv_pool *pool, u32 size, struct page **page, - u32 *offset, gfp_t flags); -void xv_free(struct xv_pool *pool, struct page *page, u32 offset); - -u32 xv_get_object_size(void *obj); -u64 xv_get_total_size_bytes(struct xv_pool *pool); - -#endif diff --git a/drivers/staging/zram/xvmalloc_int.h b/drivers/staging/zram/xvmalloc_int.h deleted file mode 100644 index b5f1f7f..0000000 --- a/drivers/staging/zram/xvmalloc_int.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * xvmalloc memory allocator - * - * Copyright (C) 2008, 2009, 2010 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the licence that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - */ - -#ifndef _XV_MALLOC_INT_H_ -#define _XV_MALLOC_INT_H_ - -#include -#include - -/* User configurable params */ - -/* Must be power of two */ -#ifdef CONFIG_64BIT -#define XV_ALIGN_SHIFT 3 -#else -#define XV_ALIGN_SHIFT 2 -#endif -#define XV_ALIGN (1 << XV_ALIGN_SHIFT) -#define XV_ALIGN_MASK (XV_ALIGN - 1) - -/* This must be greater than sizeof(link_free) */ -#define XV_MIN_ALLOC_SIZE 32 -#define XV_MAX_ALLOC_SIZE (PAGE_SIZE - XV_ALIGN) - -/* - * Free lists are separated by FL_DELTA bytes - * This value is 3 for 4k pages and 4 for 64k pages, for any - * other page size, a conservative (PAGE_SHIFT - 9) is used. - */ -#if PAGE_SHIFT == 16 -#define FL_DELTA_SHIFT 4 -#else -#define FL_DELTA_SHIFT (PAGE_SHIFT - 9) -#endif -#define FL_DELTA (1 << FL_DELTA_SHIFT) -#define FL_DELTA_MASK (FL_DELTA - 1) -#define NUM_FREE_LISTS ((XV_MAX_ALLOC_SIZE - XV_MIN_ALLOC_SIZE) \ - / FL_DELTA + 1) - -#define MAX_FLI DIV_ROUND_UP(NUM_FREE_LISTS, BITS_PER_LONG) - -/* End of user params */ - -enum blockflags { - BLOCK_FREE, - PREV_FREE, - __NR_BLOCKFLAGS, -}; - -#define FLAGS_MASK XV_ALIGN_MASK -#define PREV_MASK (~FLAGS_MASK) - -struct freelist_entry { - struct page *page; - u16 offset; - u16 pad; -}; - -struct link_free { - struct page *prev_page; - struct page *next_page; - u16 prev_offset; - u16 next_offset; -}; - -struct block_header { - union { - /* This common header must be XV_ALIGN bytes */ - u8 common[XV_ALIGN]; - struct { - u16 size; - u16 prev; - }; - }; - struct link_free link; -}; - -struct xv_pool { - ulong flbitmap; - ulong slbitmap[MAX_FLI]; - u64 total_pages; /* stats */ - struct freelist_entry freelist[NUM_FREE_LISTS]; - spinlock_t lock; -}; - -#endif -- cgit v1.1 From 9e0c774e32396f1750e8c8d37ed151ed4f6f8729 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Sun, 12 Feb 2012 23:04:45 -0500 Subject: staging: zram: Rename module parameter zram accepts number of devices to be created as a module parameter. This was renamed from num_devices to zram_num_devices (without updating the documentation!) since num_devices was declared as a non-static global variable, polluting the global namespace. Now, we declare it as a static variable and revert back the name change. The documentation (zram.txt) already mentions num_devices as the module parameter name. Signed-off-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 27 ++++++++++++++++----------- drivers/staging/zram/zram_drv.h | 2 +- drivers/staging/zram/zram_sysfs.c | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 412b312..f986d2c 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -40,7 +40,7 @@ static int zram_major; struct zram *zram_devices; /* Module params (documentation at end) */ -unsigned int zram_num_devices; +static unsigned int num_devices; static void zram_stat_inc(u32 *v) { @@ -663,13 +663,18 @@ static void destroy_device(struct zram *zram) blk_cleanup_queue(zram->queue); } +unsigned int zram_get_num_devices(void) +{ + return num_devices; +} + static int __init zram_init(void) { int ret, dev_id; - if (zram_num_devices > max_num_devices) { + if (num_devices > max_num_devices) { pr_warning("Invalid value for num_devices: %u\n", - zram_num_devices); + num_devices); ret = -EINVAL; goto out; } @@ -681,20 +686,20 @@ static int __init zram_init(void) goto out; } - if (!zram_num_devices) { + if (!num_devices) { pr_info("num_devices not specified. Using default: 1\n"); - zram_num_devices = 1; + num_devices = 1; } /* Allocate the device array and initialize each one */ - pr_info("Creating %u devices ...\n", zram_num_devices); - zram_devices = kzalloc(zram_num_devices * sizeof(struct zram), GFP_KERNEL); + pr_info("Creating %u devices ...\n", num_devices); + zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); if (!zram_devices) { ret = -ENOMEM; goto unregister; } - for (dev_id = 0; dev_id < zram_num_devices; dev_id++) { + for (dev_id = 0; dev_id < num_devices; dev_id++) { ret = create_device(&zram_devices[dev_id], dev_id); if (ret) goto free_devices; @@ -717,7 +722,7 @@ static void __exit zram_exit(void) int i; struct zram *zram; - for (i = 0; i < zram_num_devices; i++) { + for (i = 0; i < num_devices; i++) { zram = &zram_devices[i]; destroy_device(zram); @@ -731,8 +736,8 @@ static void __exit zram_exit(void) pr_debug("Cleanup done!\n"); } -module_param(zram_num_devices, uint, 0); -MODULE_PARM_DESC(zram_num_devices, "Number of zram devices"); +module_param(num_devices, uint, 0); +MODULE_PARM_DESC(num_devices, "Number of zram devices"); module_init(zram_init); module_exit(zram_exit); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 9124e34..d0b2e03 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -121,7 +121,7 @@ struct zram { }; extern struct zram *zram_devices; -extern unsigned int zram_num_devices; +unsigned int zram_get_num_devices(void); #ifdef CONFIG_SYSFS extern struct attribute_group zram_disk_attr_group; #endif diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 6eecf1d..b03ac18 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -34,7 +34,7 @@ static struct zram *dev_to_zram(struct device *dev) int i; struct zram *zram = NULL; - for (i = 0; i < zram_num_devices; i++) { + for (i = 0; i < zram_get_num_devices(); i++) { zram = &zram_devices[i]; if (disk_to_dev(zram->disk) == dev) break; -- cgit v1.1 From 6300749d0009cb3b326a24e81f39cc36c46c20d8 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Fri, 10 Jun 2011 15:28:48 +0200 Subject: Staging: zram: allow partial page operations Commit 7b19b8d45b216ff3186f066b31937bdbde066f08 (zram: Prevent overflow in logical block size) introduced ZRAM_LOGICAL_BLOCK_SIZE constant to prevent overflow of logical block size on 64k page kernel. However, the current implementation of zram only allow operation on block of the same size as a page. That makes theorically legit 4k requests fail on 64k page kernel. This patch makes zram allow operation on partial pages. Basically, it means we still do operations on full pages internally, but only copy the relevent segments from/to the user memory. Signed-off-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zram/zram_drv.c Change-Id: I2ebdfa2642feea970b8dc343380062aa593a8a05 --- drivers/staging/zram/zram_drv.c | 198 +++++++++++++++++++++++++++++++++------- drivers/staging/zram/zram_drv.h | 5 +- 2 files changed, 167 insertions(+), 36 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index f986d2c..7f7ba2f 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -177,45 +177,52 @@ out: zram->table[index].offset = 0; } -static void handle_zero_page(struct page *page) +static void handle_zero_page(struct bio_vec *bvec) { + struct page *page = bvec->bv_page; void *user_mem; user_mem = kmap_atomic(page, KM_USER0); - memset(user_mem, 0, PAGE_SIZE); + memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); kunmap_atomic(user_mem, KM_USER0); flush_dcache_page(page); } -static void handle_uncompressed_page(struct zram *zram, - struct page *page, u32 index) +static void handle_uncompressed_page(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset) { + struct page *page = bvec->bv_page; unsigned char *user_mem, *cmem; user_mem = kmap_atomic(page, KM_USER0); cmem = kmap_atomic(zram->table[index].page, KM_USER1); - memcpy(user_mem, cmem, PAGE_SIZE); + memcpy(user_mem + bvec->bv_offset, cmem + offset, bvec->bv_len); kunmap_atomic(user_mem, KM_USER0); kunmap_atomic(cmem, KM_USER1); flush_dcache_page(page); } +static inline int is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} + static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, - u32 index, struct bio *bio) + u32 index, int offset, struct bio *bio) { int ret; size_t clen; struct page *page; struct zobj_header *zheader; - unsigned char *user_mem, *cmem; + unsigned char *user_mem, *cmem, *uncmem = NULL; page = bvec->bv_page; if (zram_test_flag(zram, index, ZRAM_ZERO)) { - handle_zero_page(page); + handle_zero_page(bvec); return 0; } @@ -223,17 +230,28 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, if (unlikely(!zram->table[index].page)) { pr_debug("Read before write: sector=%lu, size=%u", (ulong)(bio->bi_sector), bio->bi_size); - handle_zero_page(page); + handle_zero_page(bvec); return 0; } /* Page is stored uncompressed since it's incompressible */ if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - handle_uncompressed_page(zram, page, index); + handle_uncompressed_page(zram, bvec, index, offset); return 0; } + if (is_partial_io(bvec)) { + /* Use a temporary buffer to decompress the page */ + uncmem = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!uncmem) { + pr_info("Error allocating temp memory!\n"); + return -ENOMEM; + } + } + user_mem = kmap_atomic(page, KM_USER0); + if (!is_partial_io(bvec)) + uncmem = user_mem; clen = PAGE_SIZE; cmem = kmap_atomic(zram->table[index].page, KM_USER1) + @@ -241,7 +259,13 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), xv_get_object_size(cmem) - sizeof(*zheader), - user_mem, &clen); + uncmem, &clen); + + if (is_partial_io(bvec)) { + memcpy(user_mem + bvec->bv_offset, uncmem + offset, + bvec->bv_len); + kfree(uncmem); + } kunmap_atomic(user_mem, KM_USER0); kunmap_atomic(cmem, KM_USER1); @@ -258,18 +282,75 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, return 0; } -static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) +static int zram_read_before_write(struct zram *zram, char *mem, u32 index) +{ + int ret; + size_t clen = PAGE_SIZE; + struct zobj_header *zheader; + unsigned char *cmem; + + if (zram_test_flag(zram, index, ZRAM_ZERO) || + !zram->table[index].page) { + memset(mem, 0, PAGE_SIZE); + return 0; + } + + cmem = kmap_atomic(zram->table[index].page, KM_USER0) + + zram->table[index].offset; + + /* Page is stored uncompressed since it's incompressible */ + if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { + memcpy(mem, cmem, PAGE_SIZE); + kunmap_atomic(cmem, KM_USER0); + return 0; + } + + ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), + xv_get_object_size(cmem) - sizeof(*zheader), + mem, &clen); + kunmap_atomic(cmem, KM_USER0); + + /* Should NEVER happen. Return bio error if it does. */ + if (unlikely(ret != LZO_E_OK)) { + pr_err("Decompression failed! err=%d, page=%u\n", ret, index); + zram_stat64_inc(zram, &zram->stats.failed_reads); + return ret; + } + + return 0; +} + +static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset) { int ret; - u32 offset; + u32 store_offset; size_t clen; struct zobj_header *zheader; struct page *page, *page_store; - unsigned char *user_mem, *cmem, *src; + unsigned char *user_mem, *cmem, *src, *uncmem = NULL; page = bvec->bv_page; src = zram->compress_buffer; + if (is_partial_io(bvec)) { + /* + * This is a partial IO. We need to read the full page + * before to write the changes. + */ + uncmem = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!uncmem) { + pr_info("Error allocating temp memory!\n"); + ret = -ENOMEM; + goto out; + } + ret = zram_read_before_write(zram, uncmem, index); + if (ret) { + kfree(uncmem); + goto out; + } + } + /* * System overwrites unused sectors. Free memory associated * with this sector now. @@ -279,22 +360,33 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) zram_free_page(zram, index); user_mem = kmap_atomic(page, KM_USER0); - if (page_zero_filled(user_mem)) { + + if (is_partial_io(bvec)) + memcpy(uncmem + offset, user_mem + bvec->bv_offset, + bvec->bv_len); + else + uncmem = user_mem; + + if (page_zero_filled(uncmem)) { kunmap_atomic(user_mem, KM_USER0); + if (is_partial_io(bvec)) + kfree(uncmem); zram_stat_inc(&zram->stats.pages_zero); zram_set_flag(zram, index, ZRAM_ZERO); - return 0; + ret = 0; + goto out; } - ret = lzo1x_1_compress(user_mem, PAGE_SIZE, src, &clen, + ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, zram->compress_workmem); kunmap_atomic(user_mem, KM_USER0); + if (is_partial_io(bvec)) + kfree(uncmem); if (unlikely(ret != LZO_E_OK)) { pr_err("Compression failed! err=%d\n", ret); - zram_stat64_inc(zram, &zram->stats.failed_writes); - return ret; + goto out; } /* @@ -308,11 +400,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) if (unlikely(!page_store)) { pr_info("Error allocating memory for " "incompressible page: %u\n", index); - zram_stat64_inc(zram, &zram->stats.failed_writes); - return -ENOMEM; - } + ret = -ENOMEM; + goto out; + } - offset = 0; + store_offset = 0; zram_set_flag(zram, index, ZRAM_UNCOMPRESSED); zram_stat_inc(&zram->stats.pages_expand); zram->table[index].page = page_store; @@ -321,16 +413,16 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index) } if (xv_malloc(zram->mem_pool, clen + sizeof(*zheader), - &zram->table[index].page, &offset, + &zram->table[index].page, &store_offset, GFP_NOIO | __GFP_HIGHMEM)) { pr_info("Error allocating memory for compressed " "page: %u, size=%zu\n", index, clen); - zram_stat64_inc(zram, &zram->stats.failed_writes); - return -ENOMEM; + ret = -ENOMEM; + goto out; } memstore: - zram->table[index].offset = offset; + zram->table[index].offset = store_offset; cmem = kmap_atomic(zram->table[index].page, KM_USER1) + zram->table[index].offset; @@ -357,10 +449,15 @@ memstore: zram_stat_inc(&zram->stats.good_compress); return 0; + +out: + if (ret) + zram_stat64_inc(zram, &zram->stats.failed_writes); + return ret; } static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, - struct bio *bio, int rw) + int offset, struct bio *bio, int rw) { int ret; @@ -377,9 +474,16 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, return ret; } +static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +{ + if (*offset + bvec->bv_len >= PAGE_SIZE) + (*index)++; + *offset = (*offset + bvec->bv_len) % PAGE_SIZE; +} + static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) { - int i; + int i, offset; u32 index; struct bio_vec *bvec; @@ -393,11 +497,35 @@ static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) } index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; bio_for_each_segment(bvec, bio, i) { - if (zram_bvec_rw(zram, bvec, index, bio, rw) < 0) - goto out; - index++; + int max_transfer_size = PAGE_SIZE - offset; + + if (bvec->bv_len > max_transfer_size) { + /* + * zram_bvec_rw() can only make operation on a single + * zram page. Split the bio vector. + */ + struct bio_vec bv; + + bv.bv_page = bvec->bv_page; + bv.bv_len = max_transfer_size; + bv.bv_offset = bvec->bv_offset; + + if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0) + goto out; + + bv.bv_len = bvec->bv_len - max_transfer_size; + bv.bv_offset += max_transfer_size; + if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0) + goto out; + } else + if (zram_bvec_rw(zram, bvec, index, offset, bio, rw) + < 0) + goto out; + + update_position(&index, &offset, bvec); } set_bit(BIO_UPTODATE, &bio->bi_flags); @@ -409,14 +537,14 @@ out: } /* - * Check if request is within bounds and page aligned. + * Check if request is within bounds and aligned on zram logical blocks. */ static inline int valid_io_request(struct zram *zram, struct bio *bio) { if (unlikely( (bio->bi_sector >= (zram->disksize >> SECTOR_SHIFT)) || - (bio->bi_sector & (SECTORS_PER_PAGE - 1)) || - (bio->bi_size & (PAGE_SIZE - 1)))) { + (bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)) || + (bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))) { return 0; } diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index d0b2e03..77035b6 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -61,7 +61,10 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; #define SECTOR_SIZE (1 << SECTOR_SHIFT) #define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) -#define ZRAM_LOGICAL_BLOCK_SIZE 4096 +#define ZRAM_LOGICAL_BLOCK_SHIFT 12 +#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT) +#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ + (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) /* Flags for zram pages (table[page_no].flags) */ enum zram_pageflags { -- cgit v1.1 From b87df97fb3e963b2f816fe9b355fbb3e0463e7d7 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Mon, 9 Jan 2012 16:51:59 -0600 Subject: staging: zram: replace xvmalloc with zsmalloc Replaces xvmalloc with zsmalloc as the compressed page allocator for zram Signed-off-by: Nitin Gupta Acked-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zram/zram_drv.c Change-Id: I16edd3a5635bbe4ae69e871d47024c53067d7dd6 --- drivers/staging/zram/Kconfig | 6 +-- drivers/staging/zram/Makefile | 1 - drivers/staging/zram/zram_drv.c | 89 +++++++++++++++++---------------------- drivers/staging/zram/zram_drv.h | 10 ++--- drivers/staging/zram/zram_sysfs.c | 2 +- 5 files changed, 46 insertions(+), 62 deletions(-) diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index 3bec4db..ee23a86 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -1,11 +1,7 @@ -config XVMALLOC - bool - default n - config ZRAM tristate "Compressed RAM block device support" depends on BLOCK && SYSFS - select XVMALLOC + select ZSMALLOC select LZO_COMPRESS select LZO_DECOMPRESS default n diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile index 2a6d321..7f4a301 100644 --- a/drivers/staging/zram/Makefile +++ b/drivers/staging/zram/Makefile @@ -1,4 +1,3 @@ zram-y := zram_drv.o zram_sysfs.o obj-$(CONFIG_ZRAM) += zram.o -obj-$(CONFIG_XVMALLOC) += xvmalloc.o \ No newline at end of file diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 7f7ba2f..dadb75b 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -135,13 +135,9 @@ static void zram_set_disksize(struct zram *zram, size_t totalram_bytes) static void zram_free_page(struct zram *zram, size_t index) { - u32 clen; - void *obj; + void *handle = zram->table[index].handle; - struct page *page = zram->table[index].page; - u32 offset = zram->table[index].offset; - - if (unlikely(!page)) { + if (unlikely(!handle)) { /* * No memory is allocated for zero filled pages. * Simply clear zero page flag. @@ -154,27 +150,24 @@ static void zram_free_page(struct zram *zram, size_t index) } if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - clen = PAGE_SIZE; - __free_page(page); + __free_page(handle); zram_clear_flag(zram, index, ZRAM_UNCOMPRESSED); zram_stat_dec(&zram->stats.pages_expand); goto out; } - obj = kmap_atomic(page, KM_USER0) + offset; - clen = xv_get_object_size(obj) - sizeof(struct zobj_header); - kunmap_atomic(obj, KM_USER0); + zs_free(zram->mem_pool, handle); - xv_free(zram->mem_pool, page, offset); - if (clen <= PAGE_SIZE / 2) + if (zram->table[index].size <= PAGE_SIZE / 2) zram_stat_dec(&zram->stats.good_compress); out: - zram_stat64_sub(zram, &zram->stats.compr_size, clen); + zram_stat64_sub(zram, &zram->stats.compr_size, + zram->table[index].size); zram_stat_dec(&zram->stats.pages_stored); - zram->table[index].page = NULL; - zram->table[index].offset = 0; + zram->table[index].handle = NULL; + zram->table[index].size = 0; } static void handle_zero_page(struct bio_vec *bvec) @@ -196,7 +189,7 @@ static void handle_uncompressed_page(struct zram *zram, struct bio_vec *bvec, unsigned char *user_mem, *cmem; user_mem = kmap_atomic(page, KM_USER0); - cmem = kmap_atomic(zram->table[index].page, KM_USER1); + cmem = kmap_atomic(zram->table[index].handle, KM_USER1); memcpy(user_mem + bvec->bv_offset, cmem + offset, bvec->bv_len); kunmap_atomic(user_mem, KM_USER0); @@ -227,7 +220,7 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, } /* Requested page is not present in compressed area */ - if (unlikely(!zram->table[index].page)) { + if (unlikely(!zram->table[index].handle)) { pr_debug("Read before write: sector=%lu, size=%u", (ulong)(bio->bi_sector), bio->bi_size); handle_zero_page(bvec); @@ -254,11 +247,10 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, uncmem = user_mem; clen = PAGE_SIZE; - cmem = kmap_atomic(zram->table[index].page, KM_USER1) + - zram->table[index].offset; + cmem = zs_map_object(zram->mem_pool, zram->table[index].handle); ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), - xv_get_object_size(cmem) - sizeof(*zheader), + zram->table[index].size, uncmem, &clen); if (is_partial_io(bvec)) { @@ -267,8 +259,8 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, kfree(uncmem); } + zs_unmap_object(zram->mem_pool, zram->table[index].handle); kunmap_atomic(user_mem, KM_USER0); - kunmap_atomic(cmem, KM_USER1); /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret != LZO_E_OK)) { @@ -290,13 +282,12 @@ static int zram_read_before_write(struct zram *zram, char *mem, u32 index) unsigned char *cmem; if (zram_test_flag(zram, index, ZRAM_ZERO) || - !zram->table[index].page) { + !zram->table[index].handle) { memset(mem, 0, PAGE_SIZE); return 0; } - cmem = kmap_atomic(zram->table[index].page, KM_USER0) + - zram->table[index].offset; + cmem = zs_map_object(zram->mem_pool, zram->table[index].handle); /* Page is stored uncompressed since it's incompressible */ if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { @@ -306,9 +297,9 @@ static int zram_read_before_write(struct zram *zram, char *mem, u32 index) } ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), - xv_get_object_size(cmem) - sizeof(*zheader), + zram->table[index].size, mem, &clen); - kunmap_atomic(cmem, KM_USER0); + zs_unmap_object(zram->mem_pool, zram->table[index].handle); /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret != LZO_E_OK)) { @@ -326,6 +317,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, int ret; u32 store_offset; size_t clen; + void *handle; struct zobj_header *zheader; struct page *page, *page_store; unsigned char *user_mem, *cmem, *src, *uncmem = NULL; @@ -355,7 +347,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, * System overwrites unused sectors. Free memory associated * with this sector now. */ - if (zram->table[index].page || + if (zram->table[index].handle || zram_test_flag(zram, index, ZRAM_ZERO)) zram_free_page(zram, index); @@ -407,26 +399,22 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, store_offset = 0; zram_set_flag(zram, index, ZRAM_UNCOMPRESSED); zram_stat_inc(&zram->stats.pages_expand); - zram->table[index].page = page_store; + handle = page_store; src = kmap_atomic(page, KM_USER0); + cmem = kmap_atomic(page_store, KM_USER1); goto memstore; } - if (xv_malloc(zram->mem_pool, clen + sizeof(*zheader), - &zram->table[index].page, &store_offset, - GFP_NOIO | __GFP_HIGHMEM)) { + handle = zs_malloc(zram->mem_pool, clen + sizeof(*zheader)); + if (!handle) { pr_info("Error allocating memory for compressed " "page: %u, size=%zu\n", index, clen); ret = -ENOMEM; goto out; } + cmem = zs_map_object(zram->mem_pool, handle); memstore: - zram->table[index].offset = store_offset; - - cmem = kmap_atomic(zram->table[index].page, KM_USER1) + - zram->table[index].offset; - #if 0 /* Back-reference needed for memory defragmentation */ if (!zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)) { @@ -438,9 +426,15 @@ memstore: memcpy(cmem, src, clen); - kunmap_atomic(cmem, KM_USER1); - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) + if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { + kunmap_atomic(cmem, KM_USER1); kunmap_atomic(src, KM_USER0); + } else { + zs_unmap_object(zram->mem_pool, handle); + } + + zram->table[index].handle = handle; + zram->table[index].size = clen; /* Update stats */ zram_stat64_add(zram, &zram->stats.compr_size, clen); @@ -599,25 +593,20 @@ void __zram_reset_device(struct zram *zram) /* Free all pages that are still in this zram device */ for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { - struct page *page; - u16 offset; - - page = zram->table[index].page; - offset = zram->table[index].offset; - - if (!page) + void *handle = zram->table[index].handle; + if (!handle) continue; if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) - __free_page(page); + __free_page(handle); else - xv_free(zram->mem_pool, page, offset); + zs_free(zram->mem_pool, handle); } vfree(zram->table); zram->table = NULL; - xv_destroy_pool(zram->mem_pool); + zs_destroy_pool(zram->mem_pool); zram->mem_pool = NULL; /* Reset stats */ @@ -675,7 +664,7 @@ int zram_init_device(struct zram *zram) /* zram devices sort of resembles non-rotational disks */ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); - zram->mem_pool = xv_create_pool(); + zram->mem_pool = zs_create_pool("zram", GFP_NOIO | __GFP_HIGHMEM); if (!zram->mem_pool) { pr_err("Error creating memory pool\n"); ret = -ENOMEM; diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 77035b6..880f013 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -18,7 +18,7 @@ #include #include -#include "xvmalloc.h" +#include "../zsmalloc/zsmalloc.h" /* * Some arbitrary value. This is just to catch @@ -51,7 +51,7 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; /* * NOTE: max_zpage_size must be less than or equal to: - * XV_MAX_ALLOC_SIZE - sizeof(struct zobj_header) + * ZS_MAX_ALLOC_SIZE - sizeof(struct zobj_header) * otherwise, xv_malloc() would always return failure. */ @@ -81,8 +81,8 @@ enum zram_pageflags { /* Allocated for each disk page */ struct table { - struct page *page; - u16 offset; + void *handle; + u16 size; /* object size (excluding header) */ u8 count; /* object ref count (not yet used) */ u8 flags; } __attribute__((aligned(4))); @@ -102,7 +102,7 @@ struct zram_stats { }; struct zram { - struct xv_pool *mem_pool; + struct zs_pool *mem_pool; void *compress_workmem; void *compress_buffer; struct table *table; diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index b03ac18..5142d07 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -194,7 +194,7 @@ static ssize_t mem_used_total_show(struct device *dev, struct zram *zram = dev_to_zram(dev); if (zram->init_done) { - val = xv_get_total_size_bytes(zram->mem_pool) + + val = zs_get_total_size_bytes(zram->mem_pool) + ((u64)(zram->stats.pages_expand) << PAGE_SHIFT); } -- cgit v1.1 From fc7e733d52309daa17ffed203f3e177fcc2ea255 Mon Sep 17 00:00:00 2001 From: Emerson Pinter Date: Mon, 9 Sep 2013 11:36:49 -0300 Subject: zram: fix merge Change-Id: I4da7d373844a6615ff3ca53bec1da63080955a70 --- drivers/staging/zram/Kconfig | 4 +++- drivers/staging/zram/zram_drv.c | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index ee23a86..9d11a4c 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -1,6 +1,8 @@ config ZRAM tristate "Compressed RAM block device support" - depends on BLOCK && SYSFS + # X86 dependency is because zsmalloc uses non-portable pte/tlb + # functions + depends on BLOCK && SYSFS && X86 select ZSMALLOC select LZO_COMPRESS select LZO_DECOMPRESS diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index dadb75b..df26f6c 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -457,11 +457,11 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, if (rw == READ) { down_read(&zram->lock); - ret = zram_bvec_read(zram, bvec, index, bio); + ret = zram_bvec_read(zram, bvec, index, offset, bio); up_read(&zram->lock); } else { down_write(&zram->lock); - ret = zram_bvec_write(zram, bvec, index); + ret = zram_bvec_write(zram, bvec, index, offset); up_write(&zram->lock); } -- cgit v1.1 From da55e1b8ab1b32978beaaffe9c18e367da161966 Mon Sep 17 00:00:00 2001 From: Dan Magenheimer Date: Thu, 7 Jul 2011 07:37:19 -0700 Subject: staging: zcache: support multiple clients, prep for KVM and RAMster This is version 3 of an update to zcache, incorporating feedback from the list. This patch adds support to the in-kernel transcendent memory ("tmem") code and the zcache driver for multiple clients, which will be needed for both RAMster and KVM support. It also adds additional tmem callbacks to support RAMster and corresponding no-op stubs in the zcache driver. In v2, I've also taken the liberty of adding some additional sysfs variables to both surface information and allow policy control. Those experimenting with zcache should find them useful. V3 clarifies some code walking and declaring arrays. Signed-off-by: Dan Magenheimer [v3: error27@gmail.com: fix array bounds/walking] [v2: konrad.wilk@oracle.com: fix bools, add check for NULL, fix a comment] [v2: sjenning@linux.vnet.ibm.com: add info/tunables for poor compression] [v2: marcusklemm@googlemail.com: add tunable for max persistent pages] Acked-by: Dan Carpenter Cc: Nitin Gupta Cc: linux-mm@kvack.org Cc: kvm@vger.kernel.org Signed-off-by: Greg Kroah-Hartman staging: fix zcache building zcache is only building tmem.c and not building zcache.c. To keep the module name, zcache.c must be renamed if symbols from tmem.c are to remain unexported. Signed-off-by: Thadeu Lima de Souza Cascardo Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: module is GPL This avoids tainting the kernel as if a proprietary module was loaded. The kernel will still be tainted because this is a staging driver. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Greg Kroah-Hartman staging: zcache: include module.h for MODULE_LICENSE The oncoming cleanup of module.h usage requires the explicit inclusion of module.h when it was otherwise being included indirectly. Otherwise, building zcache will fail. Reported-by: Stephen Rothwell Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Greg Kroah-Hartman zcache: Use div_u64 for 64-bit division xv_get_total_size_bytes returns a u64 value and it's used in a division. This causes build failures in 32-bit architectures, as reported by Randy Dunlap. Reported-by: Randy Dunlap Signed-off-by: Thadeu Lima de Souza Cascardo Cc: Stephen Rothwell Cc: Dan Magenheimer Cc: Nitin Gupta Acked-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman zcache: Fix build error when sysfs is not defined Signed-off-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman staging: zcache: fix possible sleep under lock zcache_new_pool() calls kmalloc() with GFP_KERNEL which has __GFP_WAIT set. However, zcache_new_pool() gets called on a stack that holds the swap_lock spinlock, leading to a possible sleep-with-lock situation. The lock is obtained in enable_swap_info(). The patch replaces GFP_KERNEL with GFP_ATOMIC. v2: replace with GFP_ATOMIC, not GFP_IOFS Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: fix typos The patch fixes two typos in zcache-main.c Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: fix crash on high memory swap zcache_put_page() was modified to pass page_address(page) instead of the actual page structure. In combination with the function signature changes to tmem_put() and zcache_pampd_create(), zcache_pampd_create() tries to (re)derive the page structure from the virtual address. However, if the original page is a high memory page (or any unmapped page), this virt_to_page() fails because the page_address() in zcache_put_page() returned NULL. This patch changes zcache_put_page() and zcache_get_page() to pass the page structure instead of the page's virtual address, which may or may not exist. Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman Staging: zcache: signedness bug in tmem_get() "ret" needs to be signed for the error handling to work properly. Signed-off-by: Dan Carpenter Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: fix cleancache crash After commit c5f5c4db3938 ("staging: zcache: fix crash on high memory swap") cleancache crashes on the first successful get. This was caused by a remaining virt_to_page() call in zcache_pampd_get_data_and_free() that only gets run in the cleancache path. The patch converts the virt_to_page() to struct page casting like was done for other instances in c5f5c4db3938. Signed-off-by: Seth Jennings Tested-By: Valdis Kletnieks Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds staging: zcache: fix crash on cpu remove In the case that a cpu is taken offline before zcache_do_preload() is ever called on the cpu, the per-cpu zcache_preloads structure will be uninitialized. In the CPU_DEAD case for zcache_cpu_notifier(), kp->obj is not checked before calling kmem_cache_free() on it. If it is NULL, a crash results. This patch ensures that both kp->obj and kp->page are not NULL before calling the respective free functions. In practice, just checking one or the other should be sufficient since they are assigned together in zcache_do_preload(), but I check both for safety. Signed-off-by: Seth Jennings Acked-by: Dave Hansen Signed-off-by: Greg Kroah-Hartman staging: zcache: reduce tmem bucket lock contention tmem uses hash buckets each with their own rbtree and lock to quickly lookup tmem objects. tmem has TMEM_HASH_BUCKETS (256) buckets per pool. However, because of the way the tmem_oid is generated for frontswap pages, only 16 unique tmem_oids are being generated, resulting in only 16 of the 256 buckets being used. This cause high lock contention for the per bucket locks. This patch changes SWIZ_BITS to include more bits of the offset. The result is that all 256 hash buckets are potentially used resulting in a 95% drop in hash bucket lock contention. Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: remove zcache_direct_reclaim_lock zcache_do_preload() currently does a spin_trylock() on the zcache_direct_reclaim_lock. Holding this lock intends to prevent shrink_zcache_memory() from evicting zbud pages as a result of a preload. However, it also prevents two threads from executing zcache_do_preload() at the same time. The first thread will obtain the lock and the second thread's spin_trylock() will fail (an aborted preload) causing the page to be either lost (cleancache) or pushed out to the swap device (frontswap). It also doesn't ensure that the call to shrink_zcache_memory() is on the same thread as the call to zcache_do_preload(). Additional, there is no need for this mechanism because all zcache_do_preload() calls that come down from cleancache already have PF_MEMALLOC set in the process flags which prevents direct reclaim in the memory manager. If the zcache_do_preload() call is done from the frontswap path, we _want_ reclaim to be done (which it isn't right now). This patch removes the zcache_direct_reclaim_lock and related statistics in zcache. Based on v3.1-rc8 Signed-off-by: Seth Jennings Reviewed-by: Dave Hansen Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman Staging: zcache: Fix calls to obsolete function Function "strict_strtol" replaced by "kstrtol" as suggested by the checkpatch script Signed-off-by: Bernhard Heinloth Signed-off-by: Greg Kroah-Hartman zcache: fix deadlock condition I discovered this deadlock condition awhile ago working on RAMster but it affects zcache as well. The list spinlock must be locked prior to the page spinlock and released after. As a result, the page copy must also be done while the locks are held. Applies to 3.2. Konrad, please push (via GregKH?)... this is definitely a bug fix so need not be pushed during a -rc0 window. Signed-off-by: Dan Magenheimer Acked-by: Konrad Rzeszutek Wilk Cc: stable Signed-off-by: Greg Kroah-Hartman zcache: Set SWIZ_BITS to 8 to reduce tmem bucket lock contention. SWIZ_BITS > 8 results in a much larger number of "tmem_obj" allocations, likely one per page-placed-in-frontswap. The tmem_obj is not huge (roughly 100 bytes), but it is large enough to add a not-insignificant memory overhead to zcache. The SWIZ_BITS=8 will get roughly the same lock contention without the space wastage. The effect of SWIZ_BITS can be thought of as "2^SWIZ_BITS is the number of unique oids that be generated" (This concept is limited to frontswap's use of tmem). Acked-by: Seth Jennings Signed-off-by: Konrad Rzeszutek Wilk Cc: stable Signed-off-by: Greg Kroah-Hartman staging: zcache: fix serialization bug in zv stats In a multithreaded workload, the zv_curr_dist_counts and zv_cumul_dist_counts statistics are being corrupted because the increments and decrements in zv_create and zv_free are not atomic. This patch converts these statistics and their corresponding increments/decrements/reads to atomic operations. Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: crypto API support This patch allow zcache to use the crypto API for page compression. It replaces the direct LZO compress/decompress calls with calls into the crypto compression API. The compressor to be used is specified in the kernel boot line with the zcache parameter like: zcache=lzo or zcache=deflate. If the specified compressor can't be loaded, zcache uses lzo as the default compressor. Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman staging: zcache: avoid AB-BA deadlock condition Commit 9256a47 fixed a deadlock condition, being sure that the buddy list spinlock is always taken before the page spinlock. However in zbud_free_and_delist() locking order is the opposite (page lock -> list lock). Possible unsafe locking scenario (reported by lockdep): CPU0 CPU1 ---- ---- lock(&(&zbpg->lock)->rlock); lock(zbud_budlists_spinlock); lock(&(&zbpg->lock)->rlock); lock(zbud_budlists_spinlock); Fix by grabbing the locks in opposite order in zbud_free_and_delist(). Signed-off-by: Andrea Righi Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zcache/Kconfig | 7 +- drivers/staging/zcache/Makefile | 2 +- drivers/staging/zcache/tmem.c | 102 +- drivers/staging/zcache/tmem.h | 23 +- drivers/staging/zcache/zcache-main.c | 2083 ++++++++++++++++++++++++++++++++++ drivers/staging/zcache/zcache.c | 1661 --------------------------- 6 files changed, 2185 insertions(+), 1693 deletions(-) create mode 100644 drivers/staging/zcache/zcache-main.c delete mode 100644 drivers/staging/zcache/zcache.c diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 7fabcb2..1b7bba7 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -1,13 +1,12 @@ config ZCACHE tristate "Dynamic compression of swap pages and clean pagecache pages" - depends on CLEANCACHE || FRONTSWAP + depends on (CLEANCACHE || FRONTSWAP) && CRYPTO select XVMALLOC - select LZO_COMPRESS - select LZO_DECOMPRESS + select CRYPTO_LZO default n help Zcache doubles RAM efficiency while providing a significant - performance boosts on many workloads. Zcache uses lzo1x + performance boosts on many workloads. Zcache uses compression and an in-kernel implementation of transcendent memory to store clean page cache pages and swap in RAM, providing a noticeable reduction in disk I/O. diff --git a/drivers/staging/zcache/Makefile b/drivers/staging/zcache/Makefile index f5ec64f..60daa27 100644 --- a/drivers/staging/zcache/Makefile +++ b/drivers/staging/zcache/Makefile @@ -1,3 +1,3 @@ -zcache-y := tmem.o +zcache-y := zcache-main.o tmem.o obj-$(CONFIG_ZCACHE) += zcache.o diff --git a/drivers/staging/zcache/tmem.c b/drivers/staging/zcache/tmem.c index e954d40..1ca66ea 100644 --- a/drivers/staging/zcache/tmem.c +++ b/drivers/staging/zcache/tmem.c @@ -142,6 +142,7 @@ static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb, obj->oid = *oidp; obj->objnode_count = 0; obj->pampd_count = 0; + (*tmem_pamops.new_obj)(obj); SET_SENTINEL(obj, OBJ); while (*new) { BUG_ON(RB_EMPTY_NODE(*new)); @@ -274,7 +275,7 @@ static void tmem_objnode_free(struct tmem_objnode *objnode) /* * lookup index in object and return associated pampd (or NULL if not found) */ -static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index) +static void **__tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index) { unsigned int height, shift; struct tmem_objnode **slot = NULL; @@ -303,9 +304,33 @@ static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index) height--; } out: + return slot != NULL ? (void **)slot : NULL; +} + +static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index) +{ + struct tmem_objnode **slot; + + slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index); return slot != NULL ? *slot : NULL; } +static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index, + void *new_pampd) +{ + struct tmem_objnode **slot; + void *ret = NULL; + + slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index); + if ((slot != NULL) && (*slot != NULL)) { + void *old_pampd = *(void **)slot; + *(void **)slot = new_pampd; + (*tmem_pamops.free)(old_pampd, obj->pool, NULL, 0); + ret = new_pampd; + } + return ret; +} + static int tmem_pampd_add_to_obj(struct tmem_obj *obj, uint32_t index, void *pampd) { @@ -456,7 +481,7 @@ static void tmem_objnode_node_destroy(struct tmem_obj *obj, if (ht == 1) { obj->pampd_count--; (*tmem_pamops.free)(objnode->slots[i], - obj->pool); + obj->pool, NULL, 0); objnode->slots[i] = NULL; continue; } @@ -473,7 +498,7 @@ static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj) return; if (obj->objnode_tree_height == 0) { obj->pampd_count--; - (*tmem_pamops.free)(obj->objnode_tree_root, obj->pool); + (*tmem_pamops.free)(obj->objnode_tree_root, obj->pool, NULL, 0); } else { tmem_objnode_node_destroy(obj, obj->objnode_tree_root, obj->objnode_tree_height); @@ -481,6 +506,7 @@ static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj) obj->objnode_tree_height = 0; } obj->objnode_tree_root = NULL; + (*tmem_pamops.free_obj)(obj->pool, obj); } /* @@ -503,15 +529,13 @@ static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj) * always flushes for simplicity. */ int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index, - struct page *page) + char *data, size_t size, bool raw, bool ephemeral) { struct tmem_obj *obj = NULL, *objfound = NULL, *objnew = NULL; void *pampd = NULL, *pampd_del = NULL; int ret = -ENOMEM; - bool ephemeral; struct tmem_hashbucket *hb; - ephemeral = is_ephemeral(pool); hb = &pool->hashbucket[tmem_oid_hash(oidp)]; spin_lock(&hb->lock); obj = objfound = tmem_obj_find(hb, oidp); @@ -521,7 +545,7 @@ int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index, /* if found, is a dup put, flush the old one */ pampd_del = tmem_pampd_delete_from_obj(obj, index); BUG_ON(pampd_del != pampd); - (*tmem_pamops.free)(pampd, pool); + (*tmem_pamops.free)(pampd, pool, oidp, index); if (obj->pampd_count == 0) { objnew = obj; objfound = NULL; @@ -538,7 +562,8 @@ int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index, } BUG_ON(obj == NULL); BUG_ON(((objnew != obj) && (objfound != obj)) || (objnew == objfound)); - pampd = (*tmem_pamops.create)(obj->pool, &obj->oid, index, page); + pampd = (*tmem_pamops.create)(data, size, raw, ephemeral, + obj->pool, &obj->oid, index); if (unlikely(pampd == NULL)) goto free; ret = tmem_pampd_add_to_obj(obj, index, pampd); @@ -551,7 +576,7 @@ delete_and_free: (void)tmem_pampd_delete_from_obj(obj, index); free: if (pampd) - (*tmem_pamops.free)(pampd, pool); + (*tmem_pamops.free)(pampd, pool, NULL, 0); if (objnew) { tmem_obj_free(objnew, hb); (*tmem_hostops.obj_free)(objnew, pool); @@ -573,41 +598,52 @@ out: * "put" done with the same handle). */ -int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, - uint32_t index, struct page *page) +int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index, + char *data, size_t *size, bool raw, int get_and_free) { struct tmem_obj *obj; void *pampd; bool ephemeral = is_ephemeral(pool); - uint32_t ret = -1; + int ret = -1; struct tmem_hashbucket *hb; + bool free = (get_and_free == 1) || ((get_and_free == 0) && ephemeral); + bool lock_held = false; hb = &pool->hashbucket[tmem_oid_hash(oidp)]; spin_lock(&hb->lock); + lock_held = true; obj = tmem_obj_find(hb, oidp); if (obj == NULL) goto out; - ephemeral = is_ephemeral(pool); - if (ephemeral) + if (free) pampd = tmem_pampd_delete_from_obj(obj, index); else pampd = tmem_pampd_lookup_in_obj(obj, index); if (pampd == NULL) goto out; - ret = (*tmem_pamops.get_data)(page, pampd, pool); - if (ret < 0) - goto out; - if (ephemeral) { - (*tmem_pamops.free)(pampd, pool); + if (free) { if (obj->pampd_count == 0) { tmem_obj_free(obj, hb); (*tmem_hostops.obj_free)(obj, pool); obj = NULL; } } + if (tmem_pamops.is_remote(pampd)) { + lock_held = false; + spin_unlock(&hb->lock); + } + if (free) + ret = (*tmem_pamops.get_data_and_free)( + data, size, raw, pampd, pool, oidp, index); + else + ret = (*tmem_pamops.get_data)( + data, size, raw, pampd, pool, oidp, index); + if (ret < 0) + goto out; ret = 0; out: - spin_unlock(&hb->lock); + if (lock_held) + spin_unlock(&hb->lock); return ret; } @@ -632,7 +668,7 @@ int tmem_flush_page(struct tmem_pool *pool, pampd = tmem_pampd_delete_from_obj(obj, index); if (pampd == NULL) goto out; - (*tmem_pamops.free)(pampd, pool); + (*tmem_pamops.free)(pampd, pool, oidp, index); if (obj->pampd_count == 0) { tmem_obj_free(obj, hb); (*tmem_hostops.obj_free)(obj, pool); @@ -645,6 +681,30 @@ out: } /* + * If a page in tmem matches the handle, replace the page so that any + * subsequent "get" gets the new page. Returns 0 if + * there was a page to replace, else returns -1. + */ +int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp, + uint32_t index, void *new_pampd) +{ + struct tmem_obj *obj; + int ret = -1; + struct tmem_hashbucket *hb; + + hb = &pool->hashbucket[tmem_oid_hash(oidp)]; + spin_lock(&hb->lock); + obj = tmem_obj_find(hb, oidp); + if (obj == NULL) + goto out; + new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd); + ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj); +out: + spin_unlock(&hb->lock); + return ret; +} + +/* * "Flush" all pages in tmem matching this oid. */ int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp) diff --git a/drivers/staging/zcache/tmem.h b/drivers/staging/zcache/tmem.h index 2e07e21..ed147c4 100644 --- a/drivers/staging/zcache/tmem.h +++ b/drivers/staging/zcache/tmem.h @@ -147,6 +147,7 @@ struct tmem_obj { unsigned int objnode_tree_height; unsigned long objnode_count; long pampd_count; + void *extra; /* for private use by pampd implementation */ DECL_SENTINEL }; @@ -166,10 +167,18 @@ struct tmem_objnode { /* pampd abstract datatype methods provided by the PAM implementation */ struct tmem_pamops { - void *(*create)(struct tmem_pool *, struct tmem_oid *, uint32_t, - struct page *); - int (*get_data)(struct page *, void *, struct tmem_pool *); - void (*free)(void *, struct tmem_pool *); + void *(*create)(char *, size_t, bool, int, + struct tmem_pool *, struct tmem_oid *, uint32_t); + int (*get_data)(char *, size_t *, bool, void *, struct tmem_pool *, + struct tmem_oid *, uint32_t); + int (*get_data_and_free)(char *, size_t *, bool, void *, + struct tmem_pool *, struct tmem_oid *, + uint32_t); + void (*free)(void *, struct tmem_pool *, struct tmem_oid *, uint32_t); + void (*free_obj)(struct tmem_pool *, struct tmem_obj *); + bool (*is_remote)(void *); + void (*new_obj)(struct tmem_obj *); + int (*replace_in_obj)(void *, struct tmem_obj *); }; extern void tmem_register_pamops(struct tmem_pamops *m); @@ -184,9 +193,11 @@ extern void tmem_register_hostops(struct tmem_hostops *m); /* core tmem accessor functions */ extern int tmem_put(struct tmem_pool *, struct tmem_oid *, uint32_t index, - struct page *page); + char *, size_t, bool, bool); extern int tmem_get(struct tmem_pool *, struct tmem_oid *, uint32_t index, - struct page *page); + char *, size_t *, bool, int); +extern int tmem_replace(struct tmem_pool *, struct tmem_oid *, uint32_t index, + void *); extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *, uint32_t index); extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *); diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c new file mode 100644 index 0000000..276940d --- /dev/null +++ b/drivers/staging/zcache/zcache-main.c @@ -0,0 +1,2083 @@ +/* + * zcache.c + * + * Copyright (c) 2010,2011, Dan Magenheimer, Oracle Corp. + * Copyright (c) 2010,2011, Nitin Gupta + * + * Zcache provides an in-kernel "host implementation" for transcendent memory + * and, thus indirectly, for cleancache and frontswap. Zcache includes two + * page-accessible memory [1] interfaces, both utilizing the crypto compression + * API: + * 1) "compression buddies" ("zbud") is used for ephemeral pages + * 2) xvmalloc is used for persistent pages. + * Xvmalloc (based on the TLSF allocator) has very low fragmentation + * so maximizes space efficiency, while zbud allows pairs (and potentially, + * in the future, more than a pair of) compressed pages to be closely linked + * so that reclaiming can be done via the kernel's physical-page-oriented + * "shrinker" interface. + * + * [1] For a definition of page-accessible memory (aka PAM), see: + * http://marc.info/?l=linux-mm&m=127811271605009 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tmem.h" + +#include "../zram/xvmalloc.h" /* if built in drivers/staging */ + +#if (!defined(CONFIG_CLEANCACHE) && !defined(CONFIG_FRONTSWAP)) +#error "zcache is useless without CONFIG_CLEANCACHE or CONFIG_FRONTSWAP" +#endif +#ifdef CONFIG_CLEANCACHE +#include +#endif +#ifdef CONFIG_FRONTSWAP +#include +#endif + +#if 0 +/* this is more aggressive but may cause other problems? */ +#define ZCACHE_GFP_MASK (GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN) +#else +#define ZCACHE_GFP_MASK \ + (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC) +#endif + +#define MAX_POOLS_PER_CLIENT 16 + +#define MAX_CLIENTS 16 +#define LOCAL_CLIENT ((uint16_t)-1) + +MODULE_LICENSE("GPL"); + +struct zcache_client { + struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT]; + struct xv_pool *xvpool; + bool allocated; + atomic_t refcount; +}; + +static struct zcache_client zcache_host; +static struct zcache_client zcache_clients[MAX_CLIENTS]; + +static inline uint16_t get_client_id_from_client(struct zcache_client *cli) +{ + BUG_ON(cli == NULL); + if (cli == &zcache_host) + return LOCAL_CLIENT; + return cli - &zcache_clients[0]; +} + +static inline bool is_local_client(struct zcache_client *cli) +{ + return cli == &zcache_host; +} + +/* crypto API for zcache */ +#define ZCACHE_COMP_NAME_SZ CRYPTO_MAX_ALG_NAME +static char zcache_comp_name[ZCACHE_COMP_NAME_SZ]; +static struct crypto_comp * __percpu *zcache_comp_pcpu_tfms; + +enum comp_op { + ZCACHE_COMPOP_COMPRESS, + ZCACHE_COMPOP_DECOMPRESS +}; + +static inline int zcache_comp_op(enum comp_op op, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen) +{ + struct crypto_comp *tfm; + int ret; + + BUG_ON(!zcache_comp_pcpu_tfms); + tfm = *per_cpu_ptr(zcache_comp_pcpu_tfms, get_cpu()); + BUG_ON(!tfm); + switch (op) { + case ZCACHE_COMPOP_COMPRESS: + ret = crypto_comp_compress(tfm, src, slen, dst, dlen); + break; + case ZCACHE_COMPOP_DECOMPRESS: + ret = crypto_comp_decompress(tfm, src, slen, dst, dlen); + break; + } + put_cpu(); + return ret; +} + +/********** + * Compression buddies ("zbud") provides for packing two (or, possibly + * in the future, more) compressed ephemeral pages into a single "raw" + * (physical) page and tracking them with data structures so that + * the raw pages can be easily reclaimed. + * + * A zbud page ("zbpg") is an aligned page containing a list_head, + * a lock, and two "zbud headers". The remainder of the physical + * page is divided up into aligned 64-byte "chunks" which contain + * the compressed data for zero, one, or two zbuds. Each zbpg + * resides on: (1) an "unused list" if it has no zbuds; (2) a + * "buddied" list if it is fully populated with two zbuds; or + * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks + * the one unbuddied zbud uses. The data inside a zbpg cannot be + * read or written unless the zbpg's lock is held. + */ + +#define ZBH_SENTINEL 0x43214321 +#define ZBPG_SENTINEL 0xdeadbeef + +#define ZBUD_MAX_BUDS 2 + +struct zbud_hdr { + uint16_t client_id; + uint16_t pool_id; + struct tmem_oid oid; + uint32_t index; + uint16_t size; /* compressed size in bytes, zero means unused */ + DECL_SENTINEL +}; + +struct zbud_page { + struct list_head bud_list; + spinlock_t lock; + struct zbud_hdr buddy[ZBUD_MAX_BUDS]; + DECL_SENTINEL + /* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */ +}; + +#define CHUNK_SHIFT 6 +#define CHUNK_SIZE (1 << CHUNK_SHIFT) +#define CHUNK_MASK (~(CHUNK_SIZE-1)) +#define NCHUNKS (((PAGE_SIZE - sizeof(struct zbud_page)) & \ + CHUNK_MASK) >> CHUNK_SHIFT) +#define MAX_CHUNK (NCHUNKS-1) + +static struct { + struct list_head list; + unsigned count; +} zbud_unbuddied[NCHUNKS]; +/* list N contains pages with N chunks USED and NCHUNKS-N unused */ +/* element 0 is never used but optimizing that isn't worth it */ +static unsigned long zbud_cumul_chunk_counts[NCHUNKS]; + +struct list_head zbud_buddied_list; +static unsigned long zcache_zbud_buddied_count; + +/* protects the buddied list and all unbuddied lists */ +static DEFINE_SPINLOCK(zbud_budlists_spinlock); + +static LIST_HEAD(zbpg_unused_list); +static unsigned long zcache_zbpg_unused_list_count; + +/* protects the unused page list */ +static DEFINE_SPINLOCK(zbpg_unused_list_spinlock); + +static atomic_t zcache_zbud_curr_raw_pages; +static atomic_t zcache_zbud_curr_zpages; +static unsigned long zcache_zbud_curr_zbytes; +static unsigned long zcache_zbud_cumul_zpages; +static unsigned long zcache_zbud_cumul_zbytes; +static unsigned long zcache_compress_poor; +static unsigned long zcache_mean_compress_poor; + +/* forward references */ +static void *zcache_get_free_page(void); +static void zcache_free_page(void *p); + +/* + * zbud helper functions + */ + +static inline unsigned zbud_max_buddy_size(void) +{ + return MAX_CHUNK << CHUNK_SHIFT; +} + +static inline unsigned zbud_size_to_chunks(unsigned size) +{ + BUG_ON(size == 0 || size > zbud_max_buddy_size()); + return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT; +} + +static inline int zbud_budnum(struct zbud_hdr *zh) +{ + unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1); + struct zbud_page *zbpg = NULL; + unsigned budnum = -1U; + int i; + + for (i = 0; i < ZBUD_MAX_BUDS; i++) + if (offset == offsetof(typeof(*zbpg), buddy[i])) { + budnum = i; + break; + } + BUG_ON(budnum == -1U); + return budnum; +} + +static char *zbud_data(struct zbud_hdr *zh, unsigned size) +{ + struct zbud_page *zbpg; + char *p; + unsigned budnum; + + ASSERT_SENTINEL(zh, ZBH); + budnum = zbud_budnum(zh); + BUG_ON(size == 0 || size > zbud_max_buddy_size()); + zbpg = container_of(zh, struct zbud_page, buddy[budnum]); + ASSERT_SPINLOCK(&zbpg->lock); + p = (char *)zbpg; + if (budnum == 0) + p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) & + CHUNK_MASK); + else if (budnum == 1) + p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK); + return p; +} + +/* + * zbud raw page management + */ + +static struct zbud_page *zbud_alloc_raw_page(void) +{ + struct zbud_page *zbpg = NULL; + struct zbud_hdr *zh0, *zh1; + bool recycled = 0; + + /* if any pages on the zbpg list, use one */ + spin_lock(&zbpg_unused_list_spinlock); + if (!list_empty(&zbpg_unused_list)) { + zbpg = list_first_entry(&zbpg_unused_list, + struct zbud_page, bud_list); + list_del_init(&zbpg->bud_list); + zcache_zbpg_unused_list_count--; + recycled = 1; + } + spin_unlock(&zbpg_unused_list_spinlock); + if (zbpg == NULL) + /* none on zbpg list, try to get a kernel page */ + zbpg = zcache_get_free_page(); + if (likely(zbpg != NULL)) { + INIT_LIST_HEAD(&zbpg->bud_list); + zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1]; + spin_lock_init(&zbpg->lock); + if (recycled) { + ASSERT_INVERTED_SENTINEL(zbpg, ZBPG); + SET_SENTINEL(zbpg, ZBPG); + BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid)); + BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid)); + } else { + atomic_inc(&zcache_zbud_curr_raw_pages); + INIT_LIST_HEAD(&zbpg->bud_list); + SET_SENTINEL(zbpg, ZBPG); + zh0->size = 0; zh1->size = 0; + tmem_oid_set_invalid(&zh0->oid); + tmem_oid_set_invalid(&zh1->oid); + } + } + return zbpg; +} + +static void zbud_free_raw_page(struct zbud_page *zbpg) +{ + struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1]; + + ASSERT_SENTINEL(zbpg, ZBPG); + BUG_ON(!list_empty(&zbpg->bud_list)); + ASSERT_SPINLOCK(&zbpg->lock); + BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid)); + BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid)); + INVERT_SENTINEL(zbpg, ZBPG); + spin_unlock(&zbpg->lock); + spin_lock(&zbpg_unused_list_spinlock); + list_add(&zbpg->bud_list, &zbpg_unused_list); + zcache_zbpg_unused_list_count++; + spin_unlock(&zbpg_unused_list_spinlock); +} + +/* + * core zbud handling routines + */ + +static unsigned zbud_free(struct zbud_hdr *zh) +{ + unsigned size; + + ASSERT_SENTINEL(zh, ZBH); + BUG_ON(!tmem_oid_valid(&zh->oid)); + size = zh->size; + BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size()); + zh->size = 0; + tmem_oid_set_invalid(&zh->oid); + INVERT_SENTINEL(zh, ZBH); + zcache_zbud_curr_zbytes -= size; + atomic_dec(&zcache_zbud_curr_zpages); + return size; +} + +static void zbud_free_and_delist(struct zbud_hdr *zh) +{ + unsigned chunks; + struct zbud_hdr *zh_other; + unsigned budnum = zbud_budnum(zh), size; + struct zbud_page *zbpg = + container_of(zh, struct zbud_page, buddy[budnum]); + + spin_lock(&zbud_budlists_spinlock); + spin_lock(&zbpg->lock); + if (list_empty(&zbpg->bud_list)) { + /* ignore zombie page... see zbud_evict_pages() */ + spin_unlock(&zbpg->lock); + spin_unlock(&zbud_budlists_spinlock); + return; + } + size = zbud_free(zh); + ASSERT_SPINLOCK(&zbpg->lock); + zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0]; + if (zh_other->size == 0) { /* was unbuddied: unlist and free */ + chunks = zbud_size_to_chunks(size) ; + BUG_ON(list_empty(&zbud_unbuddied[chunks].list)); + list_del_init(&zbpg->bud_list); + zbud_unbuddied[chunks].count--; + spin_unlock(&zbud_budlists_spinlock); + zbud_free_raw_page(zbpg); + } else { /* was buddied: move remaining buddy to unbuddied list */ + chunks = zbud_size_to_chunks(zh_other->size) ; + list_del_init(&zbpg->bud_list); + zcache_zbud_buddied_count--; + list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list); + zbud_unbuddied[chunks].count++; + spin_unlock(&zbud_budlists_spinlock); + spin_unlock(&zbpg->lock); + } +} + +static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id, + struct tmem_oid *oid, + uint32_t index, struct page *page, + void *cdata, unsigned size) +{ + struct zbud_hdr *zh0, *zh1, *zh = NULL; + struct zbud_page *zbpg = NULL, *ztmp; + unsigned nchunks; + char *to; + int i, found_good_buddy = 0; + + nchunks = zbud_size_to_chunks(size) ; + for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) { + spin_lock(&zbud_budlists_spinlock); + if (!list_empty(&zbud_unbuddied[i].list)) { + list_for_each_entry_safe(zbpg, ztmp, + &zbud_unbuddied[i].list, bud_list) { + if (spin_trylock(&zbpg->lock)) { + found_good_buddy = i; + goto found_unbuddied; + } + } + } + spin_unlock(&zbud_budlists_spinlock); + } + /* didn't find a good buddy, try allocating a new page */ + zbpg = zbud_alloc_raw_page(); + if (unlikely(zbpg == NULL)) + goto out; + /* ok, have a page, now compress the data before taking locks */ + spin_lock(&zbud_budlists_spinlock); + spin_lock(&zbpg->lock); + list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list); + zbud_unbuddied[nchunks].count++; + zh = &zbpg->buddy[0]; + goto init_zh; + +found_unbuddied: + ASSERT_SPINLOCK(&zbpg->lock); + zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1]; + BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0))); + if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */ + ASSERT_SENTINEL(zh0, ZBH); + zh = zh1; + } else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */ + ASSERT_SENTINEL(zh1, ZBH); + zh = zh0; + } else + BUG(); + list_del_init(&zbpg->bud_list); + zbud_unbuddied[found_good_buddy].count--; + list_add_tail(&zbpg->bud_list, &zbud_buddied_list); + zcache_zbud_buddied_count++; + +init_zh: + SET_SENTINEL(zh, ZBH); + zh->size = size; + zh->index = index; + zh->oid = *oid; + zh->pool_id = pool_id; + zh->client_id = client_id; + to = zbud_data(zh, size); + memcpy(to, cdata, size); + spin_unlock(&zbpg->lock); + spin_unlock(&zbud_budlists_spinlock); + + zbud_cumul_chunk_counts[nchunks]++; + atomic_inc(&zcache_zbud_curr_zpages); + zcache_zbud_cumul_zpages++; + zcache_zbud_curr_zbytes += size; + zcache_zbud_cumul_zbytes += size; +out: + return zh; +} + +static int zbud_decompress(struct page *page, struct zbud_hdr *zh) +{ + struct zbud_page *zbpg; + unsigned budnum = zbud_budnum(zh); + unsigned int out_len = PAGE_SIZE; + char *to_va, *from_va; + unsigned size; + int ret = 0; + + zbpg = container_of(zh, struct zbud_page, buddy[budnum]); + spin_lock(&zbpg->lock); + if (list_empty(&zbpg->bud_list)) { + /* ignore zombie page... see zbud_evict_pages() */ + ret = -EINVAL; + goto out; + } + ASSERT_SENTINEL(zh, ZBH); + BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size()); + to_va = kmap_atomic(page, KM_USER0); + size = zh->size; + from_va = zbud_data(zh, size); + ret = zcache_comp_op(ZCACHE_COMPOP_DECOMPRESS, from_va, size, + to_va, &out_len); + BUG_ON(ret); + BUG_ON(out_len != PAGE_SIZE); + kunmap_atomic(to_va, KM_USER0); +out: + spin_unlock(&zbpg->lock); + return ret; +} + +/* + * The following routines handle shrinking of ephemeral pages by evicting + * pages "least valuable" first. + */ + +static unsigned long zcache_evicted_raw_pages; +static unsigned long zcache_evicted_buddied_pages; +static unsigned long zcache_evicted_unbuddied_pages; + +static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, + uint16_t poolid); +static void zcache_put_pool(struct tmem_pool *pool); + +/* + * Flush and free all zbuds in a zbpg, then free the pageframe + */ +static void zbud_evict_zbpg(struct zbud_page *zbpg) +{ + struct zbud_hdr *zh; + int i, j; + uint32_t pool_id[ZBUD_MAX_BUDS], client_id[ZBUD_MAX_BUDS]; + uint32_t index[ZBUD_MAX_BUDS]; + struct tmem_oid oid[ZBUD_MAX_BUDS]; + struct tmem_pool *pool; + + ASSERT_SPINLOCK(&zbpg->lock); + BUG_ON(!list_empty(&zbpg->bud_list)); + for (i = 0, j = 0; i < ZBUD_MAX_BUDS; i++) { + zh = &zbpg->buddy[i]; + if (zh->size) { + client_id[j] = zh->client_id; + pool_id[j] = zh->pool_id; + oid[j] = zh->oid; + index[j] = zh->index; + j++; + zbud_free(zh); + } + } + spin_unlock(&zbpg->lock); + for (i = 0; i < j; i++) { + pool = zcache_get_pool_by_id(client_id[i], pool_id[i]); + if (pool != NULL) { + tmem_flush_page(pool, &oid[i], index[i]); + zcache_put_pool(pool); + } + } + ASSERT_SENTINEL(zbpg, ZBPG); + spin_lock(&zbpg->lock); + zbud_free_raw_page(zbpg); +} + +/* + * Free nr pages. This code is funky because we want to hold the locks + * protecting various lists for as short a time as possible, and in some + * circumstances the list may change asynchronously when the list lock is + * not held. In some cases we also trylock not only to avoid waiting on a + * page in use by another cpu, but also to avoid potential deadlock due to + * lock inversion. + */ +static void zbud_evict_pages(int nr) +{ + struct zbud_page *zbpg; + int i; + + /* first try freeing any pages on unused list */ +retry_unused_list: + spin_lock_bh(&zbpg_unused_list_spinlock); + if (!list_empty(&zbpg_unused_list)) { + /* can't walk list here, since it may change when unlocked */ + zbpg = list_first_entry(&zbpg_unused_list, + struct zbud_page, bud_list); + list_del_init(&zbpg->bud_list); + zcache_zbpg_unused_list_count--; + atomic_dec(&zcache_zbud_curr_raw_pages); + spin_unlock_bh(&zbpg_unused_list_spinlock); + zcache_free_page(zbpg); + zcache_evicted_raw_pages++; + if (--nr <= 0) + goto out; + goto retry_unused_list; + } + spin_unlock_bh(&zbpg_unused_list_spinlock); + + /* now try freeing unbuddied pages, starting with least space avail */ + for (i = 0; i < MAX_CHUNK; i++) { +retry_unbud_list_i: + spin_lock_bh(&zbud_budlists_spinlock); + if (list_empty(&zbud_unbuddied[i].list)) { + spin_unlock_bh(&zbud_budlists_spinlock); + continue; + } + list_for_each_entry(zbpg, &zbud_unbuddied[i].list, bud_list) { + if (unlikely(!spin_trylock(&zbpg->lock))) + continue; + list_del_init(&zbpg->bud_list); + zbud_unbuddied[i].count--; + spin_unlock(&zbud_budlists_spinlock); + zcache_evicted_unbuddied_pages++; + /* want budlists unlocked when doing zbpg eviction */ + zbud_evict_zbpg(zbpg); + local_bh_enable(); + if (--nr <= 0) + goto out; + goto retry_unbud_list_i; + } + spin_unlock_bh(&zbud_budlists_spinlock); + } + + /* as a last resort, free buddied pages */ +retry_bud_list: + spin_lock_bh(&zbud_budlists_spinlock); + if (list_empty(&zbud_buddied_list)) { + spin_unlock_bh(&zbud_budlists_spinlock); + goto out; + } + list_for_each_entry(zbpg, &zbud_buddied_list, bud_list) { + if (unlikely(!spin_trylock(&zbpg->lock))) + continue; + list_del_init(&zbpg->bud_list); + zcache_zbud_buddied_count--; + spin_unlock(&zbud_budlists_spinlock); + zcache_evicted_buddied_pages++; + /* want budlists unlocked when doing zbpg eviction */ + zbud_evict_zbpg(zbpg); + local_bh_enable(); + if (--nr <= 0) + goto out; + goto retry_bud_list; + } + spin_unlock_bh(&zbud_budlists_spinlock); +out: + return; +} + +static void zbud_init(void) +{ + int i; + + INIT_LIST_HEAD(&zbud_buddied_list); + zcache_zbud_buddied_count = 0; + for (i = 0; i < NCHUNKS; i++) { + INIT_LIST_HEAD(&zbud_unbuddied[i].list); + zbud_unbuddied[i].count = 0; + } +} + +#ifdef CONFIG_SYSFS +/* + * These sysfs routines show a nice distribution of how many zbpg's are + * currently (and have ever been placed) in each unbuddied list. It's fun + * to watch but can probably go away before final merge. + */ +static int zbud_show_unbuddied_list_counts(char *buf) +{ + int i; + char *p = buf; + + for (i = 0; i < NCHUNKS; i++) + p += sprintf(p, "%u ", zbud_unbuddied[i].count); + return p - buf; +} + +static int zbud_show_cumul_chunk_counts(char *buf) +{ + unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0; + unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0; + unsigned long total_chunks_lte_42 = 0; + char *p = buf; + + for (i = 0; i < NCHUNKS; i++) { + p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]); + chunks += zbud_cumul_chunk_counts[i]; + total_chunks += zbud_cumul_chunk_counts[i]; + sum_total_chunks += i * zbud_cumul_chunk_counts[i]; + if (i == 21) + total_chunks_lte_21 = total_chunks; + if (i == 32) + total_chunks_lte_32 = total_chunks; + if (i == 42) + total_chunks_lte_42 = total_chunks; + } + p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n", + total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42, + chunks == 0 ? 0 : sum_total_chunks / chunks); + return p - buf; +} +#endif + +/********** + * This "zv" PAM implementation combines the TLSF-based xvMalloc + * with the crypto compression API to maximize the amount of data that can + * be packed into a physical page. + * + * Zv represents a PAM page with the index and object (plus a "size" value + * necessary for decompression) immediately preceding the compressed data. + */ + +#define ZVH_SENTINEL 0x43214321 + +struct zv_hdr { + uint32_t pool_id; + struct tmem_oid oid; + uint32_t index; + DECL_SENTINEL +}; + +/* rudimentary policy limits */ +/* total number of persistent pages may not exceed this percentage */ +static unsigned int zv_page_count_policy_percent = 75; +/* + * byte count defining poor compression; pages with greater zsize will be + * rejected + */ +static unsigned int zv_max_zsize = (PAGE_SIZE / 8) * 7; +/* + * byte count defining poor *mean* compression; pages with greater zsize + * will be rejected until sufficient better-compressed pages are accepted + * driving the mean below this threshold + */ +static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5; + +static atomic_t zv_curr_dist_counts[NCHUNKS]; +static atomic_t zv_cumul_dist_counts[NCHUNKS]; + +static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, + struct tmem_oid *oid, uint32_t index, + void *cdata, unsigned clen) +{ + struct page *page; + struct zv_hdr *zv = NULL; + uint32_t offset; + int alloc_size = clen + sizeof(struct zv_hdr); + int chunks = (alloc_size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; + int ret; + + BUG_ON(!irqs_disabled()); + BUG_ON(chunks >= NCHUNKS); + ret = xv_malloc(xvpool, alloc_size, + &page, &offset, ZCACHE_GFP_MASK); + if (unlikely(ret)) + goto out; + atomic_inc(&zv_curr_dist_counts[chunks]); + atomic_inc(&zv_cumul_dist_counts[chunks]); + zv = kmap_atomic(page, KM_USER0) + offset; + zv->index = index; + zv->oid = *oid; + zv->pool_id = pool_id; + SET_SENTINEL(zv, ZVH); + memcpy((char *)zv + sizeof(struct zv_hdr), cdata, clen); + kunmap_atomic(zv, KM_USER0); +out: + return zv; +} + +static void zv_free(struct xv_pool *xvpool, struct zv_hdr *zv) +{ + unsigned long flags; + struct page *page; + uint32_t offset; + uint16_t size = xv_get_object_size(zv); + int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; + + ASSERT_SENTINEL(zv, ZVH); + BUG_ON(chunks >= NCHUNKS); + atomic_dec(&zv_curr_dist_counts[chunks]); + size -= sizeof(*zv); + BUG_ON(size == 0); + INVERT_SENTINEL(zv, ZVH); + page = virt_to_page(zv); + offset = (unsigned long)zv & ~PAGE_MASK; + local_irq_save(flags); + xv_free(xvpool, page, offset); + local_irq_restore(flags); +} + +static void zv_decompress(struct page *page, struct zv_hdr *zv) +{ + unsigned int clen = PAGE_SIZE; + char *to_va; + unsigned size; + int ret; + + ASSERT_SENTINEL(zv, ZVH); + size = xv_get_object_size(zv) - sizeof(*zv); + BUG_ON(size == 0); + to_va = kmap_atomic(page, KM_USER0); + ret = zcache_comp_op(ZCACHE_COMPOP_DECOMPRESS, (char *)zv + sizeof(*zv), + size, to_va, &clen); + kunmap_atomic(to_va, KM_USER0); + BUG_ON(ret); + BUG_ON(clen != PAGE_SIZE); +} + +#ifdef CONFIG_SYSFS +/* + * show a distribution of compression stats for zv pages. + */ + +static int zv_curr_dist_counts_show(char *buf) +{ + unsigned long i, n, chunks = 0, sum_total_chunks = 0; + char *p = buf; + + for (i = 0; i < NCHUNKS; i++) { + n = atomic_read(&zv_curr_dist_counts[i]); + p += sprintf(p, "%lu ", n); + chunks += n; + sum_total_chunks += i * n; + } + p += sprintf(p, "mean:%lu\n", + chunks == 0 ? 0 : sum_total_chunks / chunks); + return p - buf; +} + +static int zv_cumul_dist_counts_show(char *buf) +{ + unsigned long i, n, chunks = 0, sum_total_chunks = 0; + char *p = buf; + + for (i = 0; i < NCHUNKS; i++) { + n = atomic_read(&zv_cumul_dist_counts[i]); + p += sprintf(p, "%lu ", n); + chunks += n; + sum_total_chunks += i * n; + } + p += sprintf(p, "mean:%lu\n", + chunks == 0 ? 0 : sum_total_chunks / chunks); + return p - buf; +} + +/* + * setting zv_max_zsize via sysfs causes all persistent (e.g. swap) + * pages that don't compress to less than this value (including metadata + * overhead) to be rejected. We don't allow the value to get too close + * to PAGE_SIZE. + */ +static ssize_t zv_max_zsize_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", zv_max_zsize); +} + +static ssize_t zv_max_zsize_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + err = kstrtoul(buf, 10, &val); + if (err || (val == 0) || (val > (PAGE_SIZE / 8) * 7)) + return -EINVAL; + zv_max_zsize = val; + return count; +} + +/* + * setting zv_max_mean_zsize via sysfs causes all persistent (e.g. swap) + * pages that don't compress to less than this value (including metadata + * overhead) to be rejected UNLESS the mean compression is also smaller + * than this value. In other words, we are load-balancing-by-zsize the + * accepted pages. Again, we don't allow the value to get too close + * to PAGE_SIZE. + */ +static ssize_t zv_max_mean_zsize_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", zv_max_mean_zsize); +} + +static ssize_t zv_max_mean_zsize_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + err = kstrtoul(buf, 10, &val); + if (err || (val == 0) || (val > (PAGE_SIZE / 8) * 7)) + return -EINVAL; + zv_max_mean_zsize = val; + return count; +} + +/* + * setting zv_page_count_policy_percent via sysfs sets an upper bound of + * persistent (e.g. swap) pages that will be retained according to: + * (zv_page_count_policy_percent * totalram_pages) / 100) + * when that limit is reached, further puts will be rejected (until + * some pages have been flushed). Note that, due to compression, + * this number may exceed 100; it defaults to 75 and we set an + * arbitary limit of 150. A poor choice will almost certainly result + * in OOM's, so this value should only be changed prudently. + */ +static ssize_t zv_page_count_policy_percent_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", zv_page_count_policy_percent); +} + +static ssize_t zv_page_count_policy_percent_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + err = kstrtoul(buf, 10, &val); + if (err || (val == 0) || (val > 150)) + return -EINVAL; + zv_page_count_policy_percent = val; + return count; +} + +static struct kobj_attribute zcache_zv_max_zsize_attr = { + .attr = { .name = "zv_max_zsize", .mode = 0644 }, + .show = zv_max_zsize_show, + .store = zv_max_zsize_store, +}; + +static struct kobj_attribute zcache_zv_max_mean_zsize_attr = { + .attr = { .name = "zv_max_mean_zsize", .mode = 0644 }, + .show = zv_max_mean_zsize_show, + .store = zv_max_mean_zsize_store, +}; + +static struct kobj_attribute zcache_zv_page_count_policy_percent_attr = { + .attr = { .name = "zv_page_count_policy_percent", + .mode = 0644 }, + .show = zv_page_count_policy_percent_show, + .store = zv_page_count_policy_percent_store, +}; +#endif + +/* + * zcache core code starts here + */ + +/* useful stats not collected by cleancache or frontswap */ +static unsigned long zcache_flush_total; +static unsigned long zcache_flush_found; +static unsigned long zcache_flobj_total; +static unsigned long zcache_flobj_found; +static unsigned long zcache_failed_eph_puts; +static unsigned long zcache_failed_pers_puts; + +/* + * Tmem operations assume the poolid implies the invoking client. + * Zcache only has one client (the kernel itself): LOCAL_CLIENT. + * RAMster has each client numbered by cluster node, and a KVM version + * of zcache would have one client per guest and each client might + * have a poolid==N. + */ +static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid) +{ + struct tmem_pool *pool = NULL; + struct zcache_client *cli = NULL; + + if (cli_id == LOCAL_CLIENT) + cli = &zcache_host; + else { + if (cli_id >= MAX_CLIENTS) + goto out; + cli = &zcache_clients[cli_id]; + if (cli == NULL) + goto out; + atomic_inc(&cli->refcount); + } + if (poolid < MAX_POOLS_PER_CLIENT) { + pool = cli->tmem_pools[poolid]; + if (pool != NULL) + atomic_inc(&pool->refcount); + } +out: + return pool; +} + +static void zcache_put_pool(struct tmem_pool *pool) +{ + struct zcache_client *cli = NULL; + + if (pool == NULL) + BUG(); + cli = pool->client; + atomic_dec(&pool->refcount); + atomic_dec(&cli->refcount); +} + +int zcache_new_client(uint16_t cli_id) +{ + struct zcache_client *cli = NULL; + int ret = -1; + + if (cli_id == LOCAL_CLIENT) + cli = &zcache_host; + else if ((unsigned int)cli_id < MAX_CLIENTS) + cli = &zcache_clients[cli_id]; + if (cli == NULL) + goto out; + if (cli->allocated) + goto out; + cli->allocated = 1; +#ifdef CONFIG_FRONTSWAP + cli->xvpool = xv_create_pool(); + if (cli->xvpool == NULL) + goto out; +#endif + ret = 0; +out: + return ret; +} + +/* counters for debugging */ +static unsigned long zcache_failed_get_free_pages; +static unsigned long zcache_failed_alloc; +static unsigned long zcache_put_to_flush; + +/* + * for now, used named slabs so can easily track usage; later can + * either just use kmalloc, or perhaps add a slab-like allocator + * to more carefully manage total memory utilization + */ +static struct kmem_cache *zcache_objnode_cache; +static struct kmem_cache *zcache_obj_cache; +static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0); +static unsigned long zcache_curr_obj_count_max; +static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0); +static unsigned long zcache_curr_objnode_count_max; + +/* + * to avoid memory allocation recursion (e.g. due to direct reclaim), we + * preload all necessary data structures so the hostops callbacks never + * actually do a malloc + */ +struct zcache_preload { + void *page; + struct tmem_obj *obj; + int nr; + struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH]; +}; +static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, }; + +static int zcache_do_preload(struct tmem_pool *pool) +{ + struct zcache_preload *kp; + struct tmem_objnode *objnode; + struct tmem_obj *obj; + void *page; + int ret = -ENOMEM; + + if (unlikely(zcache_objnode_cache == NULL)) + goto out; + if (unlikely(zcache_obj_cache == NULL)) + goto out; + preempt_disable(); + kp = &__get_cpu_var(zcache_preloads); + while (kp->nr < ARRAY_SIZE(kp->objnodes)) { + preempt_enable_no_resched(); + objnode = kmem_cache_alloc(zcache_objnode_cache, + ZCACHE_GFP_MASK); + if (unlikely(objnode == NULL)) { + zcache_failed_alloc++; + goto out; + } + preempt_disable(); + kp = &__get_cpu_var(zcache_preloads); + if (kp->nr < ARRAY_SIZE(kp->objnodes)) + kp->objnodes[kp->nr++] = objnode; + else + kmem_cache_free(zcache_objnode_cache, objnode); + } + preempt_enable_no_resched(); + obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK); + if (unlikely(obj == NULL)) { + zcache_failed_alloc++; + goto out; + } + page = (void *)__get_free_page(ZCACHE_GFP_MASK); + if (unlikely(page == NULL)) { + zcache_failed_get_free_pages++; + kmem_cache_free(zcache_obj_cache, obj); + goto out; + } + preempt_disable(); + kp = &__get_cpu_var(zcache_preloads); + if (kp->obj == NULL) + kp->obj = obj; + else + kmem_cache_free(zcache_obj_cache, obj); + if (kp->page == NULL) + kp->page = page; + else + free_page((unsigned long)page); + ret = 0; +out: + return ret; +} + +static void *zcache_get_free_page(void) +{ + struct zcache_preload *kp; + void *page; + + kp = &__get_cpu_var(zcache_preloads); + page = kp->page; + BUG_ON(page == NULL); + kp->page = NULL; + return page; +} + +static void zcache_free_page(void *p) +{ + free_page((unsigned long)p); +} + +/* + * zcache implementation for tmem host ops + */ + +static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool) +{ + struct tmem_objnode *objnode = NULL; + unsigned long count; + struct zcache_preload *kp; + + kp = &__get_cpu_var(zcache_preloads); + if (kp->nr <= 0) + goto out; + objnode = kp->objnodes[kp->nr - 1]; + BUG_ON(objnode == NULL); + kp->objnodes[kp->nr - 1] = NULL; + kp->nr--; + count = atomic_inc_return(&zcache_curr_objnode_count); + if (count > zcache_curr_objnode_count_max) + zcache_curr_objnode_count_max = count; +out: + return objnode; +} + +static void zcache_objnode_free(struct tmem_objnode *objnode, + struct tmem_pool *pool) +{ + atomic_dec(&zcache_curr_objnode_count); + BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0); + kmem_cache_free(zcache_objnode_cache, objnode); +} + +static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool) +{ + struct tmem_obj *obj = NULL; + unsigned long count; + struct zcache_preload *kp; + + kp = &__get_cpu_var(zcache_preloads); + obj = kp->obj; + BUG_ON(obj == NULL); + kp->obj = NULL; + count = atomic_inc_return(&zcache_curr_obj_count); + if (count > zcache_curr_obj_count_max) + zcache_curr_obj_count_max = count; + return obj; +} + +static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool) +{ + atomic_dec(&zcache_curr_obj_count); + BUG_ON(atomic_read(&zcache_curr_obj_count) < 0); + kmem_cache_free(zcache_obj_cache, obj); +} + +static struct tmem_hostops zcache_hostops = { + .obj_alloc = zcache_obj_alloc, + .obj_free = zcache_obj_free, + .objnode_alloc = zcache_objnode_alloc, + .objnode_free = zcache_objnode_free, +}; + +/* + * zcache implementations for PAM page descriptor ops + */ + +static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0); +static unsigned long zcache_curr_eph_pampd_count_max; +static atomic_t zcache_curr_pers_pampd_count = ATOMIC_INIT(0); +static unsigned long zcache_curr_pers_pampd_count_max; + +/* forward reference */ +static int zcache_compress(struct page *from, void **out_va, size_t *out_len); + +static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph, + struct tmem_pool *pool, struct tmem_oid *oid, + uint32_t index) +{ + void *pampd = NULL, *cdata; + size_t clen; + int ret; + unsigned long count; + struct page *page = (struct page *)(data); + struct zcache_client *cli = pool->client; + uint16_t client_id = get_client_id_from_client(cli); + unsigned long zv_mean_zsize; + unsigned long curr_pers_pampd_count; + u64 total_zsize; + + if (eph) { + ret = zcache_compress(page, &cdata, &clen); + if (ret == 0) + goto out; + if (clen == 0 || clen > zbud_max_buddy_size()) { + zcache_compress_poor++; + goto out; + } + pampd = (void *)zbud_create(client_id, pool->pool_id, oid, + index, page, cdata, clen); + if (pampd != NULL) { + count = atomic_inc_return(&zcache_curr_eph_pampd_count); + if (count > zcache_curr_eph_pampd_count_max) + zcache_curr_eph_pampd_count_max = count; + } + } else { + curr_pers_pampd_count = + atomic_read(&zcache_curr_pers_pampd_count); + if (curr_pers_pampd_count > + (zv_page_count_policy_percent * totalram_pages) / 100) + goto out; + ret = zcache_compress(page, &cdata, &clen); + if (ret == 0) + goto out; + /* reject if compression is too poor */ + if (clen > zv_max_zsize) { + zcache_compress_poor++; + goto out; + } + /* reject if mean compression is too poor */ + if ((clen > zv_max_mean_zsize) && (curr_pers_pampd_count > 0)) { + total_zsize = xv_get_total_size_bytes(cli->xvpool); + zv_mean_zsize = div_u64(total_zsize, + curr_pers_pampd_count); + if (zv_mean_zsize > zv_max_mean_zsize) { + zcache_mean_compress_poor++; + goto out; + } + } + pampd = (void *)zv_create(cli->xvpool, pool->pool_id, + oid, index, cdata, clen); + if (pampd == NULL) + goto out; + count = atomic_inc_return(&zcache_curr_pers_pampd_count); + if (count > zcache_curr_pers_pampd_count_max) + zcache_curr_pers_pampd_count_max = count; + } +out: + return pampd; +} + +/* + * fill the pageframe corresponding to the struct page with the data + * from the passed pampd + */ +static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw, + void *pampd, struct tmem_pool *pool, + struct tmem_oid *oid, uint32_t index) +{ + int ret = 0; + + BUG_ON(is_ephemeral(pool)); + zv_decompress((struct page *)(data), pampd); + return ret; +} + +/* + * fill the pageframe corresponding to the struct page with the data + * from the passed pampd + */ +static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw, + void *pampd, struct tmem_pool *pool, + struct tmem_oid *oid, uint32_t index) +{ + int ret = 0; + + BUG_ON(!is_ephemeral(pool)); + zbud_decompress((struct page *)(data), pampd); + zbud_free_and_delist((struct zbud_hdr *)pampd); + atomic_dec(&zcache_curr_eph_pampd_count); + return ret; +} + +/* + * free the pampd and remove it from any zcache lists + * pampd must no longer be pointed to from any tmem data structures! + */ +static void zcache_pampd_free(void *pampd, struct tmem_pool *pool, + struct tmem_oid *oid, uint32_t index) +{ + struct zcache_client *cli = pool->client; + + if (is_ephemeral(pool)) { + zbud_free_and_delist((struct zbud_hdr *)pampd); + atomic_dec(&zcache_curr_eph_pampd_count); + BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0); + } else { + zv_free(cli->xvpool, (struct zv_hdr *)pampd); + atomic_dec(&zcache_curr_pers_pampd_count); + BUG_ON(atomic_read(&zcache_curr_pers_pampd_count) < 0); + } +} + +static void zcache_pampd_free_obj(struct tmem_pool *pool, struct tmem_obj *obj) +{ +} + +static void zcache_pampd_new_obj(struct tmem_obj *obj) +{ +} + +static int zcache_pampd_replace_in_obj(void *pampd, struct tmem_obj *obj) +{ + return -1; +} + +static bool zcache_pampd_is_remote(void *pampd) +{ + return 0; +} + +static struct tmem_pamops zcache_pamops = { + .create = zcache_pampd_create, + .get_data = zcache_pampd_get_data, + .get_data_and_free = zcache_pampd_get_data_and_free, + .free = zcache_pampd_free, + .free_obj = zcache_pampd_free_obj, + .new_obj = zcache_pampd_new_obj, + .replace_in_obj = zcache_pampd_replace_in_obj, + .is_remote = zcache_pampd_is_remote, +}; + +/* + * zcache compression/decompression and related per-cpu stuff + */ + +static DEFINE_PER_CPU(unsigned char *, zcache_dstmem); +#define ZCACHE_DSTMEM_ORDER 1 + +static int zcache_compress(struct page *from, void **out_va, size_t *out_len) +{ + int ret = 0; + unsigned char *dmem = __get_cpu_var(zcache_dstmem); + char *from_va; + + BUG_ON(!irqs_disabled()); + if (unlikely(dmem == NULL)) + goto out; /* no buffer or no compressor so can't compress */ + *out_len = PAGE_SIZE << ZCACHE_DSTMEM_ORDER; + from_va = kmap_atomic(from, KM_USER0); + mb(); + ret = zcache_comp_op(ZCACHE_COMPOP_COMPRESS, from_va, PAGE_SIZE, dmem, + (unsigned int *)out_len); + BUG_ON(ret); + *out_va = dmem; + kunmap_atomic(from_va, KM_USER0); + ret = 1; +out: + return ret; +} + +static int zcache_comp_cpu_up(int cpu) +{ + struct crypto_comp *tfm; + + tfm = crypto_alloc_comp(zcache_comp_name, 0, 0); + if (IS_ERR(tfm)) + return NOTIFY_BAD; + *per_cpu_ptr(zcache_comp_pcpu_tfms, cpu) = tfm; + return NOTIFY_OK; +} + +static void zcache_comp_cpu_down(int cpu) +{ + struct crypto_comp *tfm; + + tfm = *per_cpu_ptr(zcache_comp_pcpu_tfms, cpu); + crypto_free_comp(tfm); + *per_cpu_ptr(zcache_comp_pcpu_tfms, cpu) = NULL; +} + +static int zcache_cpu_notifier(struct notifier_block *nb, + unsigned long action, void *pcpu) +{ + int ret, cpu = (long)pcpu; + struct zcache_preload *kp; + + switch (action) { + case CPU_UP_PREPARE: + ret = zcache_comp_cpu_up(cpu); + if (ret != NOTIFY_OK) { + pr_err("zcache: can't allocate compressor transform\n"); + return ret; + } + per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages( + GFP_KERNEL | __GFP_REPEAT, ZCACHE_DSTMEM_ORDER); + break; + case CPU_DEAD: + case CPU_UP_CANCELED: + zcache_comp_cpu_down(cpu); + free_pages((unsigned long)per_cpu(zcache_dstmem, cpu), + ZCACHE_DSTMEM_ORDER); + per_cpu(zcache_dstmem, cpu) = NULL; + kp = &per_cpu(zcache_preloads, cpu); + while (kp->nr) { + kmem_cache_free(zcache_objnode_cache, + kp->objnodes[kp->nr - 1]); + kp->objnodes[kp->nr - 1] = NULL; + kp->nr--; + } + if (kp->obj) { + kmem_cache_free(zcache_obj_cache, kp->obj); + kp->obj = NULL; + } + if (kp->page) { + free_page((unsigned long)kp->page); + kp->page = NULL; + } + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block zcache_cpu_notifier_block = { + .notifier_call = zcache_cpu_notifier +}; + +#ifdef CONFIG_SYSFS +#define ZCACHE_SYSFS_RO(_name) \ + static ssize_t zcache_##_name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ + { \ + return sprintf(buf, "%lu\n", zcache_##_name); \ + } \ + static struct kobj_attribute zcache_##_name##_attr = { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = zcache_##_name##_show, \ + } + +#define ZCACHE_SYSFS_RO_ATOMIC(_name) \ + static ssize_t zcache_##_name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ + { \ + return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \ + } \ + static struct kobj_attribute zcache_##_name##_attr = { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = zcache_##_name##_show, \ + } + +#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \ + static ssize_t zcache_##_name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ + { \ + return _func(buf); \ + } \ + static struct kobj_attribute zcache_##_name##_attr = { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = zcache_##_name##_show, \ + } + +ZCACHE_SYSFS_RO(curr_obj_count_max); +ZCACHE_SYSFS_RO(curr_objnode_count_max); +ZCACHE_SYSFS_RO(flush_total); +ZCACHE_SYSFS_RO(flush_found); +ZCACHE_SYSFS_RO(flobj_total); +ZCACHE_SYSFS_RO(flobj_found); +ZCACHE_SYSFS_RO(failed_eph_puts); +ZCACHE_SYSFS_RO(failed_pers_puts); +ZCACHE_SYSFS_RO(zbud_curr_zbytes); +ZCACHE_SYSFS_RO(zbud_cumul_zpages); +ZCACHE_SYSFS_RO(zbud_cumul_zbytes); +ZCACHE_SYSFS_RO(zbud_buddied_count); +ZCACHE_SYSFS_RO(zbpg_unused_list_count); +ZCACHE_SYSFS_RO(evicted_raw_pages); +ZCACHE_SYSFS_RO(evicted_unbuddied_pages); +ZCACHE_SYSFS_RO(evicted_buddied_pages); +ZCACHE_SYSFS_RO(failed_get_free_pages); +ZCACHE_SYSFS_RO(failed_alloc); +ZCACHE_SYSFS_RO(put_to_flush); +ZCACHE_SYSFS_RO(compress_poor); +ZCACHE_SYSFS_RO(mean_compress_poor); +ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages); +ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages); +ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count); +ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count); +ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts, + zbud_show_unbuddied_list_counts); +ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts, + zbud_show_cumul_chunk_counts); +ZCACHE_SYSFS_RO_CUSTOM(zv_curr_dist_counts, + zv_curr_dist_counts_show); +ZCACHE_SYSFS_RO_CUSTOM(zv_cumul_dist_counts, + zv_cumul_dist_counts_show); + +static struct attribute *zcache_attrs[] = { + &zcache_curr_obj_count_attr.attr, + &zcache_curr_obj_count_max_attr.attr, + &zcache_curr_objnode_count_attr.attr, + &zcache_curr_objnode_count_max_attr.attr, + &zcache_flush_total_attr.attr, + &zcache_flobj_total_attr.attr, + &zcache_flush_found_attr.attr, + &zcache_flobj_found_attr.attr, + &zcache_failed_eph_puts_attr.attr, + &zcache_failed_pers_puts_attr.attr, + &zcache_compress_poor_attr.attr, + &zcache_mean_compress_poor_attr.attr, + &zcache_zbud_curr_raw_pages_attr.attr, + &zcache_zbud_curr_zpages_attr.attr, + &zcache_zbud_curr_zbytes_attr.attr, + &zcache_zbud_cumul_zpages_attr.attr, + &zcache_zbud_cumul_zbytes_attr.attr, + &zcache_zbud_buddied_count_attr.attr, + &zcache_zbpg_unused_list_count_attr.attr, + &zcache_evicted_raw_pages_attr.attr, + &zcache_evicted_unbuddied_pages_attr.attr, + &zcache_evicted_buddied_pages_attr.attr, + &zcache_failed_get_free_pages_attr.attr, + &zcache_failed_alloc_attr.attr, + &zcache_put_to_flush_attr.attr, + &zcache_zbud_unbuddied_list_counts_attr.attr, + &zcache_zbud_cumul_chunk_counts_attr.attr, + &zcache_zv_curr_dist_counts_attr.attr, + &zcache_zv_cumul_dist_counts_attr.attr, + &zcache_zv_max_zsize_attr.attr, + &zcache_zv_max_mean_zsize_attr.attr, + &zcache_zv_page_count_policy_percent_attr.attr, + NULL, +}; + +static struct attribute_group zcache_attr_group = { + .attrs = zcache_attrs, + .name = "zcache", +}; + +#endif /* CONFIG_SYSFS */ +/* + * When zcache is disabled ("frozen"), pools can be created and destroyed, + * but all puts (and thus all other operations that require memory allocation) + * must fail. If zcache is unfrozen, accepts puts, then frozen again, + * data consistency requires all puts while frozen to be converted into + * flushes. + */ +static bool zcache_freeze; + +/* + * zcache shrinker interface (only useful for ephemeral pages, so zbud only) + */ +static int shrink_zcache_memory(struct shrinker *shrink, + struct shrink_control *sc) +{ + int ret = -1; + int nr = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; + + if (nr >= 0) { + if (!(gfp_mask & __GFP_FS)) + /* does this case really need to be skipped? */ + goto out; + zbud_evict_pages(nr); + } + ret = (int)atomic_read(&zcache_zbud_curr_raw_pages); +out: + return ret; +} + +static struct shrinker zcache_shrinker = { + .shrink = shrink_zcache_memory, + .seeks = DEFAULT_SEEKS, +}; + +/* + * zcache shims between cleancache/frontswap ops and tmem + */ + +static int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp, + uint32_t index, struct page *page) +{ + struct tmem_pool *pool; + int ret = -1; + + BUG_ON(!irqs_disabled()); + pool = zcache_get_pool_by_id(cli_id, pool_id); + if (unlikely(pool == NULL)) + goto out; + if (!zcache_freeze && zcache_do_preload(pool) == 0) { + /* preload does preempt_disable on success */ + ret = tmem_put(pool, oidp, index, (char *)(page), + PAGE_SIZE, 0, is_ephemeral(pool)); + if (ret < 0) { + if (is_ephemeral(pool)) + zcache_failed_eph_puts++; + else + zcache_failed_pers_puts++; + } + zcache_put_pool(pool); + preempt_enable_no_resched(); + } else { + zcache_put_to_flush++; + if (atomic_read(&pool->obj_count) > 0) + /* the put fails whether the flush succeeds or not */ + (void)tmem_flush_page(pool, oidp, index); + zcache_put_pool(pool); + } +out: + return ret; +} + +static int zcache_get_page(int cli_id, int pool_id, struct tmem_oid *oidp, + uint32_t index, struct page *page) +{ + struct tmem_pool *pool; + int ret = -1; + unsigned long flags; + size_t size = PAGE_SIZE; + + local_irq_save(flags); + pool = zcache_get_pool_by_id(cli_id, pool_id); + if (likely(pool != NULL)) { + if (atomic_read(&pool->obj_count) > 0) + ret = tmem_get(pool, oidp, index, (char *)(page), + &size, 0, is_ephemeral(pool)); + zcache_put_pool(pool); + } + local_irq_restore(flags); + return ret; +} + +static int zcache_flush_page(int cli_id, int pool_id, + struct tmem_oid *oidp, uint32_t index) +{ + struct tmem_pool *pool; + int ret = -1; + unsigned long flags; + + local_irq_save(flags); + zcache_flush_total++; + pool = zcache_get_pool_by_id(cli_id, pool_id); + if (likely(pool != NULL)) { + if (atomic_read(&pool->obj_count) > 0) + ret = tmem_flush_page(pool, oidp, index); + zcache_put_pool(pool); + } + if (ret >= 0) + zcache_flush_found++; + local_irq_restore(flags); + return ret; +} + +static int zcache_flush_object(int cli_id, int pool_id, + struct tmem_oid *oidp) +{ + struct tmem_pool *pool; + int ret = -1; + unsigned long flags; + + local_irq_save(flags); + zcache_flobj_total++; + pool = zcache_get_pool_by_id(cli_id, pool_id); + if (likely(pool != NULL)) { + if (atomic_read(&pool->obj_count) > 0) + ret = tmem_flush_object(pool, oidp); + zcache_put_pool(pool); + } + if (ret >= 0) + zcache_flobj_found++; + local_irq_restore(flags); + return ret; +} + +static int zcache_destroy_pool(int cli_id, int pool_id) +{ + struct tmem_pool *pool = NULL; + struct zcache_client *cli = NULL; + int ret = -1; + + if (pool_id < 0) + goto out; + if (cli_id == LOCAL_CLIENT) + cli = &zcache_host; + else if ((unsigned int)cli_id < MAX_CLIENTS) + cli = &zcache_clients[cli_id]; + if (cli == NULL) + goto out; + atomic_inc(&cli->refcount); + pool = cli->tmem_pools[pool_id]; + if (pool == NULL) + goto out; + cli->tmem_pools[pool_id] = NULL; + /* wait for pool activity on other cpus to quiesce */ + while (atomic_read(&pool->refcount) != 0) + ; + atomic_dec(&cli->refcount); + local_bh_disable(); + ret = tmem_destroy_pool(pool); + local_bh_enable(); + kfree(pool); + pr_info("zcache: destroyed pool id=%d, cli_id=%d\n", + pool_id, cli_id); +out: + return ret; +} + +static int zcache_new_pool(uint16_t cli_id, uint32_t flags) +{ + int poolid = -1; + struct tmem_pool *pool; + struct zcache_client *cli = NULL; + + if (cli_id == LOCAL_CLIENT) + cli = &zcache_host; + else if ((unsigned int)cli_id < MAX_CLIENTS) + cli = &zcache_clients[cli_id]; + if (cli == NULL) + goto out; + atomic_inc(&cli->refcount); + pool = kmalloc(sizeof(struct tmem_pool), GFP_ATOMIC); + if (pool == NULL) { + pr_info("zcache: pool creation failed: out of memory\n"); + goto out; + } + + for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++) + if (cli->tmem_pools[poolid] == NULL) + break; + if (poolid >= MAX_POOLS_PER_CLIENT) { + pr_info("zcache: pool creation failed: max exceeded\n"); + kfree(pool); + poolid = -1; + goto out; + } + atomic_set(&pool->refcount, 0); + pool->client = cli; + pool->pool_id = poolid; + tmem_new_pool(pool, flags); + cli->tmem_pools[poolid] = pool; + pr_info("zcache: created %s tmem pool, id=%d, client=%d\n", + flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral", + poolid, cli_id); +out: + if (cli != NULL) + atomic_dec(&cli->refcount); + return poolid; +} + +/********** + * Two kernel functionalities currently can be layered on top of tmem. + * These are "cleancache" which is used as a second-chance cache for clean + * page cache pages; and "frontswap" which is used for swap pages + * to avoid writes to disk. A generic "shim" is provided here for each + * to translate in-kernel semantics to zcache semantics. + */ + +#ifdef CONFIG_CLEANCACHE +static void zcache_cleancache_put_page(int pool_id, + struct cleancache_filekey key, + pgoff_t index, struct page *page) +{ + u32 ind = (u32) index; + struct tmem_oid oid = *(struct tmem_oid *)&key; + + if (likely(ind == index)) + (void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index, page); +} + +static int zcache_cleancache_get_page(int pool_id, + struct cleancache_filekey key, + pgoff_t index, struct page *page) +{ + u32 ind = (u32) index; + struct tmem_oid oid = *(struct tmem_oid *)&key; + int ret = -1; + + if (likely(ind == index)) + ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index, page); + return ret; +} + +static void zcache_cleancache_flush_page(int pool_id, + struct cleancache_filekey key, + pgoff_t index) +{ + u32 ind = (u32) index; + struct tmem_oid oid = *(struct tmem_oid *)&key; + + if (likely(ind == index)) + (void)zcache_flush_page(LOCAL_CLIENT, pool_id, &oid, ind); +} + +static void zcache_cleancache_flush_inode(int pool_id, + struct cleancache_filekey key) +{ + struct tmem_oid oid = *(struct tmem_oid *)&key; + + (void)zcache_flush_object(LOCAL_CLIENT, pool_id, &oid); +} + +static void zcache_cleancache_flush_fs(int pool_id) +{ + if (pool_id >= 0) + (void)zcache_destroy_pool(LOCAL_CLIENT, pool_id); +} + +static int zcache_cleancache_init_fs(size_t pagesize) +{ + BUG_ON(sizeof(struct cleancache_filekey) != + sizeof(struct tmem_oid)); + BUG_ON(pagesize != PAGE_SIZE); + return zcache_new_pool(LOCAL_CLIENT, 0); +} + +static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize) +{ + /* shared pools are unsupported and map to private */ + BUG_ON(sizeof(struct cleancache_filekey) != + sizeof(struct tmem_oid)); + BUG_ON(pagesize != PAGE_SIZE); + return zcache_new_pool(LOCAL_CLIENT, 0); +} + +static struct cleancache_ops zcache_cleancache_ops = { + .put_page = zcache_cleancache_put_page, + .get_page = zcache_cleancache_get_page, + .flush_page = zcache_cleancache_flush_page, + .flush_inode = zcache_cleancache_flush_inode, + .flush_fs = zcache_cleancache_flush_fs, + .init_shared_fs = zcache_cleancache_init_shared_fs, + .init_fs = zcache_cleancache_init_fs +}; + +struct cleancache_ops zcache_cleancache_register_ops(void) +{ + struct cleancache_ops old_ops = + cleancache_register_ops(&zcache_cleancache_ops); + + return old_ops; +} +#endif + +#ifdef CONFIG_FRONTSWAP +/* a single tmem poolid is used for all frontswap "types" (swapfiles) */ +static int zcache_frontswap_poolid = -1; + +/* + * Swizzling increases objects per swaptype, increasing tmem concurrency + * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS + * Setting SWIZ_BITS to 27 basically reconstructs the swap entry from + * frontswap_get_page(), but has side-effects. Hence using 8. + */ +#define SWIZ_BITS 8 +#define SWIZ_MASK ((1 << SWIZ_BITS) - 1) +#define _oswiz(_type, _ind) ((_type << SWIZ_BITS) | (_ind & SWIZ_MASK)) +#define iswiz(_ind) (_ind >> SWIZ_BITS) + +static inline struct tmem_oid oswiz(unsigned type, u32 ind) +{ + struct tmem_oid oid = { .oid = { 0 } }; + oid.oid[0] = _oswiz(type, ind); + return oid; +} + +static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, + struct page *page) +{ + u64 ind64 = (u64)offset; + u32 ind = (u32)offset; + struct tmem_oid oid = oswiz(type, ind); + int ret = -1; + unsigned long flags; + + BUG_ON(!PageLocked(page)); + if (likely(ind64 == ind)) { + local_irq_save(flags); + ret = zcache_put_page(LOCAL_CLIENT, zcache_frontswap_poolid, + &oid, iswiz(ind), page); + local_irq_restore(flags); + } + return ret; +} + +/* returns 0 if the page was successfully gotten from frontswap, -1 if + * was not present (should never happen!) */ +static int zcache_frontswap_get_page(unsigned type, pgoff_t offset, + struct page *page) +{ + u64 ind64 = (u64)offset; + u32 ind = (u32)offset; + struct tmem_oid oid = oswiz(type, ind); + int ret = -1; + + BUG_ON(!PageLocked(page)); + if (likely(ind64 == ind)) + ret = zcache_get_page(LOCAL_CLIENT, zcache_frontswap_poolid, + &oid, iswiz(ind), page); + return ret; +} + +/* flush a single page from frontswap */ +static void zcache_frontswap_flush_page(unsigned type, pgoff_t offset) +{ + u64 ind64 = (u64)offset; + u32 ind = (u32)offset; + struct tmem_oid oid = oswiz(type, ind); + + if (likely(ind64 == ind)) + (void)zcache_flush_page(LOCAL_CLIENT, zcache_frontswap_poolid, + &oid, iswiz(ind)); +} + +/* flush all pages from the passed swaptype */ +static void zcache_frontswap_flush_area(unsigned type) +{ + struct tmem_oid oid; + int ind; + + for (ind = SWIZ_MASK; ind >= 0; ind--) { + oid = oswiz(type, ind); + (void)zcache_flush_object(LOCAL_CLIENT, + zcache_frontswap_poolid, &oid); + } +} + +static void zcache_frontswap_init(unsigned ignored) +{ + /* a single tmem poolid is used for all frontswap "types" (swapfiles) */ + if (zcache_frontswap_poolid < 0) + zcache_frontswap_poolid = + zcache_new_pool(LOCAL_CLIENT, TMEM_POOL_PERSIST); +} + +static struct frontswap_ops zcache_frontswap_ops = { + .put_page = zcache_frontswap_put_page, + .get_page = zcache_frontswap_get_page, + .flush_page = zcache_frontswap_flush_page, + .flush_area = zcache_frontswap_flush_area, + .init = zcache_frontswap_init +}; + +struct frontswap_ops zcache_frontswap_register_ops(void) +{ + struct frontswap_ops old_ops = + frontswap_register_ops(&zcache_frontswap_ops); + + return old_ops; +} +#endif + +/* + * zcache initialization + * NOTE FOR NOW zcache MUST BE PROVIDED AS A KERNEL BOOT PARAMETER OR + * NOTHING HAPPENS! + */ + +static int zcache_enabled; + +static int __init enable_zcache(char *s) +{ + zcache_enabled = 1; + return 1; +} +__setup("zcache", enable_zcache); + +/* allow independent dynamic disabling of cleancache and frontswap */ + +static int use_cleancache = 1; + +static int __init no_cleancache(char *s) +{ + use_cleancache = 0; + return 1; +} + +__setup("nocleancache", no_cleancache); + +static int use_frontswap = 1; + +static int __init no_frontswap(char *s) +{ + use_frontswap = 0; + return 1; +} + +__setup("nofrontswap", no_frontswap); + +static int __init enable_zcache_compressor(char *s) +{ + strncpy(zcache_comp_name, s, ZCACHE_COMP_NAME_SZ); + zcache_enabled = 1; + return 1; +} +__setup("zcache=", enable_zcache_compressor); + + +static int zcache_comp_init(void) +{ + int ret = 0; + + /* check crypto algorithm */ + if (*zcache_comp_name != '\0') { + ret = crypto_has_comp(zcache_comp_name, 0, 0); + if (!ret) + pr_info("zcache: %s not supported\n", + zcache_comp_name); + } + if (!ret) + strcpy(zcache_comp_name, "lzo"); + ret = crypto_has_comp(zcache_comp_name, 0, 0); + if (!ret) { + ret = 1; + goto out; + } + pr_info("zcache: using %s compressor\n", zcache_comp_name); + + /* alloc percpu transforms */ + ret = 0; + zcache_comp_pcpu_tfms = alloc_percpu(struct crypto_comp *); + if (!zcache_comp_pcpu_tfms) + ret = 1; +out: + return ret; +} + +static int __init zcache_init(void) +{ + int ret = 0; + +#ifdef CONFIG_SYSFS + ret = sysfs_create_group(mm_kobj, &zcache_attr_group); + if (ret) { + pr_err("zcache: can't create sysfs\n"); + goto out; + } +#endif /* CONFIG_SYSFS */ +#if defined(CONFIG_CLEANCACHE) || defined(CONFIG_FRONTSWAP) + if (zcache_enabled) { + unsigned int cpu; + + tmem_register_hostops(&zcache_hostops); + tmem_register_pamops(&zcache_pamops); + ret = register_cpu_notifier(&zcache_cpu_notifier_block); + if (ret) { + pr_err("zcache: can't register cpu notifier\n"); + goto out; + } + ret = zcache_comp_init(); + if (ret) { + pr_err("zcache: compressor initialization failed\n"); + goto out; + } + for_each_online_cpu(cpu) { + void *pcpu = (void *)(long)cpu; + zcache_cpu_notifier(&zcache_cpu_notifier_block, + CPU_UP_PREPARE, pcpu); + } + } + zcache_objnode_cache = kmem_cache_create("zcache_objnode", + sizeof(struct tmem_objnode), 0, 0, NULL); + zcache_obj_cache = kmem_cache_create("zcache_obj", + sizeof(struct tmem_obj), 0, 0, NULL); + ret = zcache_new_client(LOCAL_CLIENT); + if (ret) { + pr_err("zcache: can't create client\n"); + goto out; + } +#endif +#ifdef CONFIG_CLEANCACHE + if (zcache_enabled && use_cleancache) { + struct cleancache_ops old_ops; + + zbud_init(); + register_shrinker(&zcache_shrinker); + old_ops = zcache_cleancache_register_ops(); + pr_info("zcache: cleancache enabled using kernel " + "transcendent memory and compression buddies\n"); + if (old_ops.init_fs != NULL) + pr_warning("zcache: cleancache_ops overridden"); + } +#endif +#ifdef CONFIG_FRONTSWAP + if (zcache_enabled && use_frontswap) { + struct frontswap_ops old_ops; + + old_ops = zcache_frontswap_register_ops(); + pr_info("zcache: frontswap enabled using kernel " + "transcendent memory and xvmalloc\n"); + if (old_ops.init != NULL) + pr_warning("zcache: frontswap_ops overridden"); + } +#endif +out: + return ret; +} + +module_init(zcache_init) diff --git a/drivers/staging/zcache/zcache.c b/drivers/staging/zcache/zcache.c deleted file mode 100644 index 77ac2d4..0000000 --- a/drivers/staging/zcache/zcache.c +++ /dev/null @@ -1,1661 +0,0 @@ -/* - * zcache.c - * - * Copyright (c) 2010,2011, Dan Magenheimer, Oracle Corp. - * Copyright (c) 2010,2011, Nitin Gupta - * - * Zcache provides an in-kernel "host implementation" for transcendent memory - * and, thus indirectly, for cleancache and frontswap. Zcache includes two - * page-accessible memory [1] interfaces, both utilizing lzo1x compression: - * 1) "compression buddies" ("zbud") is used for ephemeral pages - * 2) xvmalloc is used for persistent pages. - * Xvmalloc (based on the TLSF allocator) has very low fragmentation - * so maximizes space efficiency, while zbud allows pairs (and potentially, - * in the future, more than a pair of) compressed pages to be closely linked - * so that reclaiming can be done via the kernel's physical-page-oriented - * "shrinker" interface. - * - * [1] For a definition of page-accessible memory (aka PAM), see: - * http://marc.info/?l=linux-mm&m=127811271605009 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "tmem.h" - -#include "../zram/xvmalloc.h" /* if built in drivers/staging */ - -#if (!defined(CONFIG_CLEANCACHE) && !defined(CONFIG_FRONTSWAP)) -#error "zcache is useless without CONFIG_CLEANCACHE or CONFIG_FRONTSWAP" -#endif -#ifdef CONFIG_CLEANCACHE -#include -#endif -#ifdef CONFIG_FRONTSWAP -#include -#endif - -#if 0 -/* this is more aggressive but may cause other problems? */ -#define ZCACHE_GFP_MASK (GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN) -#else -#define ZCACHE_GFP_MASK \ - (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC) -#endif - -/********** - * Compression buddies ("zbud") provides for packing two (or, possibly - * in the future, more) compressed ephemeral pages into a single "raw" - * (physical) page and tracking them with data structures so that - * the raw pages can be easily reclaimed. - * - * A zbud page ("zbpg") is an aligned page containing a list_head, - * a lock, and two "zbud headers". The remainder of the physical - * page is divided up into aligned 64-byte "chunks" which contain - * the compressed data for zero, one, or two zbuds. Each zbpg - * resides on: (1) an "unused list" if it has no zbuds; (2) a - * "buddied" list if it is fully populated with two zbuds; or - * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks - * the one unbuddied zbud uses. The data inside a zbpg cannot be - * read or written unless the zbpg's lock is held. - */ - -#define ZBH_SENTINEL 0x43214321 -#define ZBPG_SENTINEL 0xdeadbeef - -#define ZBUD_MAX_BUDS 2 - -struct zbud_hdr { - uint32_t pool_id; - struct tmem_oid oid; - uint32_t index; - uint16_t size; /* compressed size in bytes, zero means unused */ - DECL_SENTINEL -}; - -struct zbud_page { - struct list_head bud_list; - spinlock_t lock; - struct zbud_hdr buddy[ZBUD_MAX_BUDS]; - DECL_SENTINEL - /* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */ -}; - -#define CHUNK_SHIFT 6 -#define CHUNK_SIZE (1 << CHUNK_SHIFT) -#define CHUNK_MASK (~(CHUNK_SIZE-1)) -#define NCHUNKS (((PAGE_SIZE - sizeof(struct zbud_page)) & \ - CHUNK_MASK) >> CHUNK_SHIFT) -#define MAX_CHUNK (NCHUNKS-1) - -static struct { - struct list_head list; - unsigned count; -} zbud_unbuddied[NCHUNKS]; -/* list N contains pages with N chunks USED and NCHUNKS-N unused */ -/* element 0 is never used but optimizing that isn't worth it */ -static unsigned long zbud_cumul_chunk_counts[NCHUNKS]; - -struct list_head zbud_buddied_list; -static unsigned long zcache_zbud_buddied_count; - -/* protects the buddied list and all unbuddied lists */ -static DEFINE_SPINLOCK(zbud_budlists_spinlock); - -static LIST_HEAD(zbpg_unused_list); -static unsigned long zcache_zbpg_unused_list_count; - -/* protects the unused page list */ -static DEFINE_SPINLOCK(zbpg_unused_list_spinlock); - -static atomic_t zcache_zbud_curr_raw_pages; -static atomic_t zcache_zbud_curr_zpages; -static unsigned long zcache_zbud_curr_zbytes; -static unsigned long zcache_zbud_cumul_zpages; -static unsigned long zcache_zbud_cumul_zbytes; -static unsigned long zcache_compress_poor; - -/* forward references */ -static void *zcache_get_free_page(void); -static void zcache_free_page(void *p); - -/* - * zbud helper functions - */ - -static inline unsigned zbud_max_buddy_size(void) -{ - return MAX_CHUNK << CHUNK_SHIFT; -} - -static inline unsigned zbud_size_to_chunks(unsigned size) -{ - BUG_ON(size == 0 || size > zbud_max_buddy_size()); - return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT; -} - -static inline int zbud_budnum(struct zbud_hdr *zh) -{ - unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1); - struct zbud_page *zbpg = NULL; - unsigned budnum = -1U; - int i; - - for (i = 0; i < ZBUD_MAX_BUDS; i++) - if (offset == offsetof(typeof(*zbpg), buddy[i])) { - budnum = i; - break; - } - BUG_ON(budnum == -1U); - return budnum; -} - -static char *zbud_data(struct zbud_hdr *zh, unsigned size) -{ - struct zbud_page *zbpg; - char *p; - unsigned budnum; - - ASSERT_SENTINEL(zh, ZBH); - budnum = zbud_budnum(zh); - BUG_ON(size == 0 || size > zbud_max_buddy_size()); - zbpg = container_of(zh, struct zbud_page, buddy[budnum]); - ASSERT_SPINLOCK(&zbpg->lock); - p = (char *)zbpg; - if (budnum == 0) - p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) & - CHUNK_MASK); - else if (budnum == 1) - p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK); - return p; -} - -/* - * zbud raw page management - */ - -static struct zbud_page *zbud_alloc_raw_page(void) -{ - struct zbud_page *zbpg = NULL; - struct zbud_hdr *zh0, *zh1; - bool recycled = 0; - - /* if any pages on the zbpg list, use one */ - spin_lock(&zbpg_unused_list_spinlock); - if (!list_empty(&zbpg_unused_list)) { - zbpg = list_first_entry(&zbpg_unused_list, - struct zbud_page, bud_list); - list_del_init(&zbpg->bud_list); - zcache_zbpg_unused_list_count--; - recycled = 1; - } - spin_unlock(&zbpg_unused_list_spinlock); - if (zbpg == NULL) - /* none on zbpg list, try to get a kernel page */ - zbpg = zcache_get_free_page(); - if (likely(zbpg != NULL)) { - INIT_LIST_HEAD(&zbpg->bud_list); - zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1]; - spin_lock_init(&zbpg->lock); - if (recycled) { - ASSERT_INVERTED_SENTINEL(zbpg, ZBPG); - SET_SENTINEL(zbpg, ZBPG); - BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid)); - BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid)); - } else { - atomic_inc(&zcache_zbud_curr_raw_pages); - INIT_LIST_HEAD(&zbpg->bud_list); - SET_SENTINEL(zbpg, ZBPG); - zh0->size = 0; zh1->size = 0; - tmem_oid_set_invalid(&zh0->oid); - tmem_oid_set_invalid(&zh1->oid); - } - } - return zbpg; -} - -static void zbud_free_raw_page(struct zbud_page *zbpg) -{ - struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1]; - - ASSERT_SENTINEL(zbpg, ZBPG); - BUG_ON(!list_empty(&zbpg->bud_list)); - ASSERT_SPINLOCK(&zbpg->lock); - BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid)); - BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid)); - INVERT_SENTINEL(zbpg, ZBPG); - spin_unlock(&zbpg->lock); - spin_lock(&zbpg_unused_list_spinlock); - list_add(&zbpg->bud_list, &zbpg_unused_list); - zcache_zbpg_unused_list_count++; - spin_unlock(&zbpg_unused_list_spinlock); -} - -/* - * core zbud handling routines - */ - -static unsigned zbud_free(struct zbud_hdr *zh) -{ - unsigned size; - - ASSERT_SENTINEL(zh, ZBH); - BUG_ON(!tmem_oid_valid(&zh->oid)); - size = zh->size; - BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size()); - zh->size = 0; - tmem_oid_set_invalid(&zh->oid); - INVERT_SENTINEL(zh, ZBH); - zcache_zbud_curr_zbytes -= size; - atomic_dec(&zcache_zbud_curr_zpages); - return size; -} - -static void zbud_free_and_delist(struct zbud_hdr *zh) -{ - unsigned chunks; - struct zbud_hdr *zh_other; - unsigned budnum = zbud_budnum(zh), size; - struct zbud_page *zbpg = - container_of(zh, struct zbud_page, buddy[budnum]); - - spin_lock(&zbpg->lock); - if (list_empty(&zbpg->bud_list)) { - /* ignore zombie page... see zbud_evict_pages() */ - spin_unlock(&zbpg->lock); - return; - } - size = zbud_free(zh); - ASSERT_SPINLOCK(&zbpg->lock); - zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0]; - if (zh_other->size == 0) { /* was unbuddied: unlist and free */ - chunks = zbud_size_to_chunks(size) ; - spin_lock(&zbud_budlists_spinlock); - BUG_ON(list_empty(&zbud_unbuddied[chunks].list)); - list_del_init(&zbpg->bud_list); - zbud_unbuddied[chunks].count--; - spin_unlock(&zbud_budlists_spinlock); - zbud_free_raw_page(zbpg); - } else { /* was buddied: move remaining buddy to unbuddied list */ - chunks = zbud_size_to_chunks(zh_other->size) ; - spin_lock(&zbud_budlists_spinlock); - list_del_init(&zbpg->bud_list); - zcache_zbud_buddied_count--; - list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list); - zbud_unbuddied[chunks].count++; - spin_unlock(&zbud_budlists_spinlock); - spin_unlock(&zbpg->lock); - } -} - -static struct zbud_hdr *zbud_create(uint32_t pool_id, struct tmem_oid *oid, - uint32_t index, struct page *page, - void *cdata, unsigned size) -{ - struct zbud_hdr *zh0, *zh1, *zh = NULL; - struct zbud_page *zbpg = NULL, *ztmp; - unsigned nchunks; - char *to; - int i, found_good_buddy = 0; - - nchunks = zbud_size_to_chunks(size) ; - for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) { - spin_lock(&zbud_budlists_spinlock); - if (!list_empty(&zbud_unbuddied[i].list)) { - list_for_each_entry_safe(zbpg, ztmp, - &zbud_unbuddied[i].list, bud_list) { - if (spin_trylock(&zbpg->lock)) { - found_good_buddy = i; - goto found_unbuddied; - } - } - } - spin_unlock(&zbud_budlists_spinlock); - } - /* didn't find a good buddy, try allocating a new page */ - zbpg = zbud_alloc_raw_page(); - if (unlikely(zbpg == NULL)) - goto out; - /* ok, have a page, now compress the data before taking locks */ - spin_lock(&zbpg->lock); - spin_lock(&zbud_budlists_spinlock); - list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list); - zbud_unbuddied[nchunks].count++; - zh = &zbpg->buddy[0]; - goto init_zh; - -found_unbuddied: - ASSERT_SPINLOCK(&zbpg->lock); - zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1]; - BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0))); - if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */ - ASSERT_SENTINEL(zh0, ZBH); - zh = zh1; - } else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */ - ASSERT_SENTINEL(zh1, ZBH); - zh = zh0; - } else - BUG(); - list_del_init(&zbpg->bud_list); - zbud_unbuddied[found_good_buddy].count--; - list_add_tail(&zbpg->bud_list, &zbud_buddied_list); - zcache_zbud_buddied_count++; - -init_zh: - SET_SENTINEL(zh, ZBH); - zh->size = size; - zh->index = index; - zh->oid = *oid; - zh->pool_id = pool_id; - /* can wait to copy the data until the list locks are dropped */ - spin_unlock(&zbud_budlists_spinlock); - - to = zbud_data(zh, size); - memcpy(to, cdata, size); - spin_unlock(&zbpg->lock); - zbud_cumul_chunk_counts[nchunks]++; - atomic_inc(&zcache_zbud_curr_zpages); - zcache_zbud_cumul_zpages++; - zcache_zbud_curr_zbytes += size; - zcache_zbud_cumul_zbytes += size; -out: - return zh; -} - -static int zbud_decompress(struct page *page, struct zbud_hdr *zh) -{ - struct zbud_page *zbpg; - unsigned budnum = zbud_budnum(zh); - size_t out_len = PAGE_SIZE; - char *to_va, *from_va; - unsigned size; - int ret = 0; - - zbpg = container_of(zh, struct zbud_page, buddy[budnum]); - spin_lock(&zbpg->lock); - if (list_empty(&zbpg->bud_list)) { - /* ignore zombie page... see zbud_evict_pages() */ - ret = -EINVAL; - goto out; - } - ASSERT_SENTINEL(zh, ZBH); - BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size()); - to_va = kmap_atomic(page, KM_USER0); - size = zh->size; - from_va = zbud_data(zh, size); - ret = lzo1x_decompress_safe(from_va, size, to_va, &out_len); - BUG_ON(ret != LZO_E_OK); - BUG_ON(out_len != PAGE_SIZE); - kunmap_atomic(to_va, KM_USER0); -out: - spin_unlock(&zbpg->lock); - return ret; -} - -/* - * The following routines handle shrinking of ephemeral pages by evicting - * pages "least valuable" first. - */ - -static unsigned long zcache_evicted_raw_pages; -static unsigned long zcache_evicted_buddied_pages; -static unsigned long zcache_evicted_unbuddied_pages; - -static struct tmem_pool *zcache_get_pool_by_id(uint32_t poolid); -static void zcache_put_pool(struct tmem_pool *pool); - -/* - * Flush and free all zbuds in a zbpg, then free the pageframe - */ -static void zbud_evict_zbpg(struct zbud_page *zbpg) -{ - struct zbud_hdr *zh; - int i, j; - uint32_t pool_id[ZBUD_MAX_BUDS], index[ZBUD_MAX_BUDS]; - struct tmem_oid oid[ZBUD_MAX_BUDS]; - struct tmem_pool *pool; - - ASSERT_SPINLOCK(&zbpg->lock); - BUG_ON(!list_empty(&zbpg->bud_list)); - for (i = 0, j = 0; i < ZBUD_MAX_BUDS; i++) { - zh = &zbpg->buddy[i]; - if (zh->size) { - pool_id[j] = zh->pool_id; - oid[j] = zh->oid; - index[j] = zh->index; - j++; - zbud_free(zh); - } - } - spin_unlock(&zbpg->lock); - for (i = 0; i < j; i++) { - pool = zcache_get_pool_by_id(pool_id[i]); - if (pool != NULL) { - tmem_flush_page(pool, &oid[i], index[i]); - zcache_put_pool(pool); - } - } - ASSERT_SENTINEL(zbpg, ZBPG); - spin_lock(&zbpg->lock); - zbud_free_raw_page(zbpg); -} - -/* - * Free nr pages. This code is funky because we want to hold the locks - * protecting various lists for as short a time as possible, and in some - * circumstances the list may change asynchronously when the list lock is - * not held. In some cases we also trylock not only to avoid waiting on a - * page in use by another cpu, but also to avoid potential deadlock due to - * lock inversion. - */ -static void zbud_evict_pages(int nr) -{ - struct zbud_page *zbpg; - int i; - - /* first try freeing any pages on unused list */ -retry_unused_list: - spin_lock_bh(&zbpg_unused_list_spinlock); - if (!list_empty(&zbpg_unused_list)) { - /* can't walk list here, since it may change when unlocked */ - zbpg = list_first_entry(&zbpg_unused_list, - struct zbud_page, bud_list); - list_del_init(&zbpg->bud_list); - zcache_zbpg_unused_list_count--; - atomic_dec(&zcache_zbud_curr_raw_pages); - spin_unlock_bh(&zbpg_unused_list_spinlock); - zcache_free_page(zbpg); - zcache_evicted_raw_pages++; - if (--nr <= 0) - goto out; - goto retry_unused_list; - } - spin_unlock_bh(&zbpg_unused_list_spinlock); - - /* now try freeing unbuddied pages, starting with least space avail */ - for (i = 0; i < MAX_CHUNK; i++) { -retry_unbud_list_i: - spin_lock_bh(&zbud_budlists_spinlock); - if (list_empty(&zbud_unbuddied[i].list)) { - spin_unlock_bh(&zbud_budlists_spinlock); - continue; - } - list_for_each_entry(zbpg, &zbud_unbuddied[i].list, bud_list) { - if (unlikely(!spin_trylock(&zbpg->lock))) - continue; - list_del_init(&zbpg->bud_list); - zbud_unbuddied[i].count--; - spin_unlock(&zbud_budlists_spinlock); - zcache_evicted_unbuddied_pages++; - /* want budlists unlocked when doing zbpg eviction */ - zbud_evict_zbpg(zbpg); - local_bh_enable(); - if (--nr <= 0) - goto out; - goto retry_unbud_list_i; - } - spin_unlock_bh(&zbud_budlists_spinlock); - } - - /* as a last resort, free buddied pages */ -retry_bud_list: - spin_lock_bh(&zbud_budlists_spinlock); - if (list_empty(&zbud_buddied_list)) { - spin_unlock_bh(&zbud_budlists_spinlock); - goto out; - } - list_for_each_entry(zbpg, &zbud_buddied_list, bud_list) { - if (unlikely(!spin_trylock(&zbpg->lock))) - continue; - list_del_init(&zbpg->bud_list); - zcache_zbud_buddied_count--; - spin_unlock(&zbud_budlists_spinlock); - zcache_evicted_buddied_pages++; - /* want budlists unlocked when doing zbpg eviction */ - zbud_evict_zbpg(zbpg); - local_bh_enable(); - if (--nr <= 0) - goto out; - goto retry_bud_list; - } - spin_unlock_bh(&zbud_budlists_spinlock); -out: - return; -} - -static void zbud_init(void) -{ - int i; - - INIT_LIST_HEAD(&zbud_buddied_list); - zcache_zbud_buddied_count = 0; - for (i = 0; i < NCHUNKS; i++) { - INIT_LIST_HEAD(&zbud_unbuddied[i].list); - zbud_unbuddied[i].count = 0; - } -} - -#ifdef CONFIG_SYSFS -/* - * These sysfs routines show a nice distribution of how many zbpg's are - * currently (and have ever been placed) in each unbuddied list. It's fun - * to watch but can probably go away before final merge. - */ -static int zbud_show_unbuddied_list_counts(char *buf) -{ - int i; - char *p = buf; - - for (i = 0; i < NCHUNKS - 1; i++) - p += sprintf(p, "%u ", zbud_unbuddied[i].count); - p += sprintf(p, "%d\n", zbud_unbuddied[i].count); - return p - buf; -} - -static int zbud_show_cumul_chunk_counts(char *buf) -{ - unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0; - unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0; - unsigned long total_chunks_lte_42 = 0; - char *p = buf; - - for (i = 0; i < NCHUNKS; i++) { - p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]); - chunks += zbud_cumul_chunk_counts[i]; - total_chunks += zbud_cumul_chunk_counts[i]; - sum_total_chunks += i * zbud_cumul_chunk_counts[i]; - if (i == 21) - total_chunks_lte_21 = total_chunks; - if (i == 32) - total_chunks_lte_32 = total_chunks; - if (i == 42) - total_chunks_lte_42 = total_chunks; - } - p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n", - total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42, - chunks == 0 ? 0 : sum_total_chunks / chunks); - return p - buf; -} -#endif - -/********** - * This "zv" PAM implementation combines the TLSF-based xvMalloc - * with lzo1x compression to maximize the amount of data that can - * be packed into a physical page. - * - * Zv represents a PAM page with the index and object (plus a "size" value - * necessary for decompression) immediately preceding the compressed data. - */ - -#define ZVH_SENTINEL 0x43214321 - -struct zv_hdr { - uint32_t pool_id; - struct tmem_oid oid; - uint32_t index; - DECL_SENTINEL -}; - -static const int zv_max_page_size = (PAGE_SIZE / 8) * 7; - -static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, - struct tmem_oid *oid, uint32_t index, - void *cdata, unsigned clen) -{ - struct page *page; - struct zv_hdr *zv = NULL; - uint32_t offset; - int ret; - - BUG_ON(!irqs_disabled()); - ret = xv_malloc(xvpool, clen + sizeof(struct zv_hdr), - &page, &offset, ZCACHE_GFP_MASK); - if (unlikely(ret)) - goto out; - zv = kmap_atomic(page, KM_USER0) + offset; - zv->index = index; - zv->oid = *oid; - zv->pool_id = pool_id; - SET_SENTINEL(zv, ZVH); - memcpy((char *)zv + sizeof(struct zv_hdr), cdata, clen); - kunmap_atomic(zv, KM_USER0); -out: - return zv; -} - -static void zv_free(struct xv_pool *xvpool, struct zv_hdr *zv) -{ - unsigned long flags; - struct page *page; - uint32_t offset; - uint16_t size; - - ASSERT_SENTINEL(zv, ZVH); - size = xv_get_object_size(zv) - sizeof(*zv); - BUG_ON(size == 0 || size > zv_max_page_size); - INVERT_SENTINEL(zv, ZVH); - page = virt_to_page(zv); - offset = (unsigned long)zv & ~PAGE_MASK; - local_irq_save(flags); - xv_free(xvpool, page, offset); - local_irq_restore(flags); -} - -static void zv_decompress(struct page *page, struct zv_hdr *zv) -{ - size_t clen = PAGE_SIZE; - char *to_va; - unsigned size; - int ret; - - ASSERT_SENTINEL(zv, ZVH); - size = xv_get_object_size(zv) - sizeof(*zv); - BUG_ON(size == 0 || size > zv_max_page_size); - to_va = kmap_atomic(page, KM_USER0); - ret = lzo1x_decompress_safe((char *)zv + sizeof(*zv), - size, to_va, &clen); - kunmap_atomic(to_va, KM_USER0); - BUG_ON(ret != LZO_E_OK); - BUG_ON(clen != PAGE_SIZE); -} - -/* - * zcache core code starts here - */ - -/* useful stats not collected by cleancache or frontswap */ -static unsigned long zcache_flush_total; -static unsigned long zcache_flush_found; -static unsigned long zcache_flobj_total; -static unsigned long zcache_flobj_found; -static unsigned long zcache_failed_eph_puts; -static unsigned long zcache_failed_pers_puts; - -#define MAX_POOLS_PER_CLIENT 16 - -static struct { - struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT]; - struct xv_pool *xvpool; -} zcache_client; - -/* - * Tmem operations assume the poolid implies the invoking client. - * Zcache only has one client (the kernel itself), so translate - * the poolid into the tmem_pool allocated for it. A KVM version - * of zcache would have one client per guest and each client might - * have a poolid==N. - */ -static struct tmem_pool *zcache_get_pool_by_id(uint32_t poolid) -{ - struct tmem_pool *pool = NULL; - - if (poolid >= 0) { - pool = zcache_client.tmem_pools[poolid]; - if (pool != NULL) - atomic_inc(&pool->refcount); - } - return pool; -} - -static void zcache_put_pool(struct tmem_pool *pool) -{ - if (pool != NULL) - atomic_dec(&pool->refcount); -} - -/* counters for debugging */ -static unsigned long zcache_failed_get_free_pages; -static unsigned long zcache_failed_alloc; -static unsigned long zcache_put_to_flush; -static unsigned long zcache_aborted_preload; -static unsigned long zcache_aborted_shrink; - -/* - * Ensure that memory allocation requests in zcache don't result - * in direct reclaim requests via the shrinker, which would cause - * an infinite loop. Maybe a GFP flag would be better? - */ -static DEFINE_SPINLOCK(zcache_direct_reclaim_lock); - -/* - * for now, used named slabs so can easily track usage; later can - * either just use kmalloc, or perhaps add a slab-like allocator - * to more carefully manage total memory utilization - */ -static struct kmem_cache *zcache_objnode_cache; -static struct kmem_cache *zcache_obj_cache; -static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_obj_count_max; -static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_objnode_count_max; - -/* - * to avoid memory allocation recursion (e.g. due to direct reclaim), we - * preload all necessary data structures so the hostops callbacks never - * actually do a malloc - */ -struct zcache_preload { - void *page; - struct tmem_obj *obj; - int nr; - struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH]; -}; -static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, }; - -static int zcache_do_preload(struct tmem_pool *pool) -{ - struct zcache_preload *kp; - struct tmem_objnode *objnode; - struct tmem_obj *obj; - void *page; - int ret = -ENOMEM; - - if (unlikely(zcache_objnode_cache == NULL)) - goto out; - if (unlikely(zcache_obj_cache == NULL)) - goto out; - if (!spin_trylock(&zcache_direct_reclaim_lock)) { - zcache_aborted_preload++; - goto out; - } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - while (kp->nr < ARRAY_SIZE(kp->objnodes)) { - preempt_enable_no_resched(); - objnode = kmem_cache_alloc(zcache_objnode_cache, - ZCACHE_GFP_MASK); - if (unlikely(objnode == NULL)) { - zcache_failed_alloc++; - goto unlock_out; - } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - if (kp->nr < ARRAY_SIZE(kp->objnodes)) - kp->objnodes[kp->nr++] = objnode; - else - kmem_cache_free(zcache_objnode_cache, objnode); - } - preempt_enable_no_resched(); - obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK); - if (unlikely(obj == NULL)) { - zcache_failed_alloc++; - goto unlock_out; - } - page = (void *)__get_free_page(ZCACHE_GFP_MASK); - if (unlikely(page == NULL)) { - zcache_failed_get_free_pages++; - kmem_cache_free(zcache_obj_cache, obj); - goto unlock_out; - } - preempt_disable(); - kp = &__get_cpu_var(zcache_preloads); - if (kp->obj == NULL) - kp->obj = obj; - else - kmem_cache_free(zcache_obj_cache, obj); - if (kp->page == NULL) - kp->page = page; - else - free_page((unsigned long)page); - ret = 0; -unlock_out: - spin_unlock(&zcache_direct_reclaim_lock); -out: - return ret; -} - -static void *zcache_get_free_page(void) -{ - struct zcache_preload *kp; - void *page; - - kp = &__get_cpu_var(zcache_preloads); - page = kp->page; - BUG_ON(page == NULL); - kp->page = NULL; - return page; -} - -static void zcache_free_page(void *p) -{ - free_page((unsigned long)p); -} - -/* - * zcache implementation for tmem host ops - */ - -static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool) -{ - struct tmem_objnode *objnode = NULL; - unsigned long count; - struct zcache_preload *kp; - - kp = &__get_cpu_var(zcache_preloads); - if (kp->nr <= 0) - goto out; - objnode = kp->objnodes[kp->nr - 1]; - BUG_ON(objnode == NULL); - kp->objnodes[kp->nr - 1] = NULL; - kp->nr--; - count = atomic_inc_return(&zcache_curr_objnode_count); - if (count > zcache_curr_objnode_count_max) - zcache_curr_objnode_count_max = count; -out: - return objnode; -} - -static void zcache_objnode_free(struct tmem_objnode *objnode, - struct tmem_pool *pool) -{ - atomic_dec(&zcache_curr_objnode_count); - BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0); - kmem_cache_free(zcache_objnode_cache, objnode); -} - -static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool) -{ - struct tmem_obj *obj = NULL; - unsigned long count; - struct zcache_preload *kp; - - kp = &__get_cpu_var(zcache_preloads); - obj = kp->obj; - BUG_ON(obj == NULL); - kp->obj = NULL; - count = atomic_inc_return(&zcache_curr_obj_count); - if (count > zcache_curr_obj_count_max) - zcache_curr_obj_count_max = count; - return obj; -} - -static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool) -{ - atomic_dec(&zcache_curr_obj_count); - BUG_ON(atomic_read(&zcache_curr_obj_count) < 0); - kmem_cache_free(zcache_obj_cache, obj); -} - -static struct tmem_hostops zcache_hostops = { - .obj_alloc = zcache_obj_alloc, - .obj_free = zcache_obj_free, - .objnode_alloc = zcache_objnode_alloc, - .objnode_free = zcache_objnode_free, -}; - -/* - * zcache implementations for PAM page descriptor ops - */ - -static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_eph_pampd_count_max; -static atomic_t zcache_curr_pers_pampd_count = ATOMIC_INIT(0); -static unsigned long zcache_curr_pers_pampd_count_max; - -/* forward reference */ -static int zcache_compress(struct page *from, void **out_va, size_t *out_len); - -static void *zcache_pampd_create(struct tmem_pool *pool, struct tmem_oid *oid, - uint32_t index, struct page *page) -{ - void *pampd = NULL, *cdata; - size_t clen; - int ret; - bool ephemeral = is_ephemeral(pool); - unsigned long count; - - if (ephemeral) { - ret = zcache_compress(page, &cdata, &clen); - if (ret == 0) - - goto out; - if (clen == 0 || clen > zbud_max_buddy_size()) { - zcache_compress_poor++; - goto out; - } - pampd = (void *)zbud_create(pool->pool_id, oid, index, - page, cdata, clen); - if (pampd != NULL) { - count = atomic_inc_return(&zcache_curr_eph_pampd_count); - if (count > zcache_curr_eph_pampd_count_max) - zcache_curr_eph_pampd_count_max = count; - } - } else { - /* - * FIXME: This is all the "policy" there is for now. - * 3/4 totpages should allow ~37% of RAM to be filled with - * compressed frontswap pages - */ - if (atomic_read(&zcache_curr_pers_pampd_count) > - 3 * totalram_pages / 4) - goto out; - ret = zcache_compress(page, &cdata, &clen); - if (ret == 0) - goto out; - if (clen > zv_max_page_size) { - zcache_compress_poor++; - goto out; - } - pampd = (void *)zv_create(zcache_client.xvpool, pool->pool_id, - oid, index, cdata, clen); - if (pampd == NULL) - goto out; - count = atomic_inc_return(&zcache_curr_pers_pampd_count); - if (count > zcache_curr_pers_pampd_count_max) - zcache_curr_pers_pampd_count_max = count; - } -out: - return pampd; -} - -/* - * fill the pageframe corresponding to the struct page with the data - * from the passed pampd - */ -static int zcache_pampd_get_data(struct page *page, void *pampd, - struct tmem_pool *pool) -{ - int ret = 0; - - if (is_ephemeral(pool)) - ret = zbud_decompress(page, pampd); - else - zv_decompress(page, pampd); - return ret; -} - -/* - * free the pampd and remove it from any zcache lists - * pampd must no longer be pointed to from any tmem data structures! - */ -static void zcache_pampd_free(void *pampd, struct tmem_pool *pool) -{ - if (is_ephemeral(pool)) { - zbud_free_and_delist((struct zbud_hdr *)pampd); - atomic_dec(&zcache_curr_eph_pampd_count); - BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0); - } else { - zv_free(zcache_client.xvpool, (struct zv_hdr *)pampd); - atomic_dec(&zcache_curr_pers_pampd_count); - BUG_ON(atomic_read(&zcache_curr_pers_pampd_count) < 0); - } -} - -static struct tmem_pamops zcache_pamops = { - .create = zcache_pampd_create, - .get_data = zcache_pampd_get_data, - .free = zcache_pampd_free, -}; - -/* - * zcache compression/decompression and related per-cpu stuff - */ - -#define LZO_WORKMEM_BYTES LZO1X_1_MEM_COMPRESS -#define LZO_DSTMEM_PAGE_ORDER 1 -static DEFINE_PER_CPU(unsigned char *, zcache_workmem); -static DEFINE_PER_CPU(unsigned char *, zcache_dstmem); - -static int zcache_compress(struct page *from, void **out_va, size_t *out_len) -{ - int ret = 0; - unsigned char *dmem = __get_cpu_var(zcache_dstmem); - unsigned char *wmem = __get_cpu_var(zcache_workmem); - char *from_va; - - BUG_ON(!irqs_disabled()); - if (unlikely(dmem == NULL || wmem == NULL)) - goto out; /* no buffer, so can't compress */ - from_va = kmap_atomic(from, KM_USER0); - mb(); - ret = lzo1x_1_compress(from_va, PAGE_SIZE, dmem, out_len, wmem); - BUG_ON(ret != LZO_E_OK); - *out_va = dmem; - kunmap_atomic(from_va, KM_USER0); - ret = 1; -out: - return ret; -} - - -static int zcache_cpu_notifier(struct notifier_block *nb, - unsigned long action, void *pcpu) -{ - int cpu = (long)pcpu; - struct zcache_preload *kp; - - switch (action) { - case CPU_UP_PREPARE: - per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages( - GFP_KERNEL | __GFP_REPEAT, - LZO_DSTMEM_PAGE_ORDER), - per_cpu(zcache_workmem, cpu) = - kzalloc(LZO1X_MEM_COMPRESS, - GFP_KERNEL | __GFP_REPEAT); - break; - case CPU_DEAD: - case CPU_UP_CANCELED: - free_pages((unsigned long)per_cpu(zcache_dstmem, cpu), - LZO_DSTMEM_PAGE_ORDER); - per_cpu(zcache_dstmem, cpu) = NULL; - kfree(per_cpu(zcache_workmem, cpu)); - per_cpu(zcache_workmem, cpu) = NULL; - kp = &per_cpu(zcache_preloads, cpu); - while (kp->nr) { - kmem_cache_free(zcache_objnode_cache, - kp->objnodes[kp->nr - 1]); - kp->objnodes[kp->nr - 1] = NULL; - kp->nr--; - } - kmem_cache_free(zcache_obj_cache, kp->obj); - free_page((unsigned long)kp->page); - break; - default: - break; - } - return NOTIFY_OK; -} - -static struct notifier_block zcache_cpu_notifier_block = { - .notifier_call = zcache_cpu_notifier -}; - -#ifdef CONFIG_SYSFS -#define ZCACHE_SYSFS_RO(_name) \ - static ssize_t zcache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ - { \ - return sprintf(buf, "%lu\n", zcache_##_name); \ - } \ - static struct kobj_attribute zcache_##_name##_attr = { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = zcache_##_name##_show, \ - } - -#define ZCACHE_SYSFS_RO_ATOMIC(_name) \ - static ssize_t zcache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ - { \ - return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \ - } \ - static struct kobj_attribute zcache_##_name##_attr = { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = zcache_##_name##_show, \ - } - -#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \ - static ssize_t zcache_##_name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ - { \ - return _func(buf); \ - } \ - static struct kobj_attribute zcache_##_name##_attr = { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = zcache_##_name##_show, \ - } - -ZCACHE_SYSFS_RO(curr_obj_count_max); -ZCACHE_SYSFS_RO(curr_objnode_count_max); -ZCACHE_SYSFS_RO(flush_total); -ZCACHE_SYSFS_RO(flush_found); -ZCACHE_SYSFS_RO(flobj_total); -ZCACHE_SYSFS_RO(flobj_found); -ZCACHE_SYSFS_RO(failed_eph_puts); -ZCACHE_SYSFS_RO(failed_pers_puts); -ZCACHE_SYSFS_RO(zbud_curr_zbytes); -ZCACHE_SYSFS_RO(zbud_cumul_zpages); -ZCACHE_SYSFS_RO(zbud_cumul_zbytes); -ZCACHE_SYSFS_RO(zbud_buddied_count); -ZCACHE_SYSFS_RO(zbpg_unused_list_count); -ZCACHE_SYSFS_RO(evicted_raw_pages); -ZCACHE_SYSFS_RO(evicted_unbuddied_pages); -ZCACHE_SYSFS_RO(evicted_buddied_pages); -ZCACHE_SYSFS_RO(failed_get_free_pages); -ZCACHE_SYSFS_RO(failed_alloc); -ZCACHE_SYSFS_RO(put_to_flush); -ZCACHE_SYSFS_RO(aborted_preload); -ZCACHE_SYSFS_RO(aborted_shrink); -ZCACHE_SYSFS_RO(compress_poor); -ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages); -ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages); -ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count); -ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count); -ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts, - zbud_show_unbuddied_list_counts); -ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts, - zbud_show_cumul_chunk_counts); - -static struct attribute *zcache_attrs[] = { - &zcache_curr_obj_count_attr.attr, - &zcache_curr_obj_count_max_attr.attr, - &zcache_curr_objnode_count_attr.attr, - &zcache_curr_objnode_count_max_attr.attr, - &zcache_flush_total_attr.attr, - &zcache_flobj_total_attr.attr, - &zcache_flush_found_attr.attr, - &zcache_flobj_found_attr.attr, - &zcache_failed_eph_puts_attr.attr, - &zcache_failed_pers_puts_attr.attr, - &zcache_compress_poor_attr.attr, - &zcache_zbud_curr_raw_pages_attr.attr, - &zcache_zbud_curr_zpages_attr.attr, - &zcache_zbud_curr_zbytes_attr.attr, - &zcache_zbud_cumul_zpages_attr.attr, - &zcache_zbud_cumul_zbytes_attr.attr, - &zcache_zbud_buddied_count_attr.attr, - &zcache_zbpg_unused_list_count_attr.attr, - &zcache_evicted_raw_pages_attr.attr, - &zcache_evicted_unbuddied_pages_attr.attr, - &zcache_evicted_buddied_pages_attr.attr, - &zcache_failed_get_free_pages_attr.attr, - &zcache_failed_alloc_attr.attr, - &zcache_put_to_flush_attr.attr, - &zcache_aborted_preload_attr.attr, - &zcache_aborted_shrink_attr.attr, - &zcache_zbud_unbuddied_list_counts_attr.attr, - &zcache_zbud_cumul_chunk_counts_attr.attr, - NULL, -}; - -static struct attribute_group zcache_attr_group = { - .attrs = zcache_attrs, - .name = "zcache", -}; - -#endif /* CONFIG_SYSFS */ -/* - * When zcache is disabled ("frozen"), pools can be created and destroyed, - * but all puts (and thus all other operations that require memory allocation) - * must fail. If zcache is unfrozen, accepts puts, then frozen again, - * data consistency requires all puts while frozen to be converted into - * flushes. - */ -static bool zcache_freeze; - -/* - * zcache shrinker interface (only useful for ephemeral pages, so zbud only) - */ -static int shrink_zcache_memory(struct shrinker *shrink, - struct shrink_control *sc) -{ - int ret = -1; - int nr = sc->nr_to_scan; - gfp_t gfp_mask = sc->gfp_mask; - - if (nr >= 0) { - if (!(gfp_mask & __GFP_FS)) - /* does this case really need to be skipped? */ - goto out; - if (spin_trylock(&zcache_direct_reclaim_lock)) { - zbud_evict_pages(nr); - spin_unlock(&zcache_direct_reclaim_lock); - } else - zcache_aborted_shrink++; - } - ret = (int)atomic_read(&zcache_zbud_curr_raw_pages); -out: - return ret; -} - -static struct shrinker zcache_shrinker = { - .shrink = shrink_zcache_memory, - .seeks = DEFAULT_SEEKS, -}; - -/* - * zcache shims between cleancache/frontswap ops and tmem - */ - -static int zcache_put_page(int pool_id, struct tmem_oid *oidp, - uint32_t index, struct page *page) -{ - struct tmem_pool *pool; - int ret = -1; - - BUG_ON(!irqs_disabled()); - pool = zcache_get_pool_by_id(pool_id); - if (unlikely(pool == NULL)) - goto out; - if (!zcache_freeze && zcache_do_preload(pool) == 0) { - /* preload does preempt_disable on success */ - ret = tmem_put(pool, oidp, index, page); - if (ret < 0) { - if (is_ephemeral(pool)) - zcache_failed_eph_puts++; - else - zcache_failed_pers_puts++; - } - zcache_put_pool(pool); - preempt_enable_no_resched(); - } else { - zcache_put_to_flush++; - if (atomic_read(&pool->obj_count) > 0) - /* the put fails whether the flush succeeds or not */ - (void)tmem_flush_page(pool, oidp, index); - zcache_put_pool(pool); - } -out: - return ret; -} - -static int zcache_get_page(int pool_id, struct tmem_oid *oidp, - uint32_t index, struct page *page) -{ - struct tmem_pool *pool; - int ret = -1; - unsigned long flags; - - local_irq_save(flags); - pool = zcache_get_pool_by_id(pool_id); - if (likely(pool != NULL)) { - if (atomic_read(&pool->obj_count) > 0) - ret = tmem_get(pool, oidp, index, page); - zcache_put_pool(pool); - } - local_irq_restore(flags); - return ret; -} - -static int zcache_flush_page(int pool_id, struct tmem_oid *oidp, uint32_t index) -{ - struct tmem_pool *pool; - int ret = -1; - unsigned long flags; - - local_irq_save(flags); - zcache_flush_total++; - pool = zcache_get_pool_by_id(pool_id); - if (likely(pool != NULL)) { - if (atomic_read(&pool->obj_count) > 0) - ret = tmem_flush_page(pool, oidp, index); - zcache_put_pool(pool); - } - if (ret >= 0) - zcache_flush_found++; - local_irq_restore(flags); - return ret; -} - -static int zcache_flush_object(int pool_id, struct tmem_oid *oidp) -{ - struct tmem_pool *pool; - int ret = -1; - unsigned long flags; - - local_irq_save(flags); - zcache_flobj_total++; - pool = zcache_get_pool_by_id(pool_id); - if (likely(pool != NULL)) { - if (atomic_read(&pool->obj_count) > 0) - ret = tmem_flush_object(pool, oidp); - zcache_put_pool(pool); - } - if (ret >= 0) - zcache_flobj_found++; - local_irq_restore(flags); - return ret; -} - -static int zcache_destroy_pool(int pool_id) -{ - struct tmem_pool *pool = NULL; - int ret = -1; - - if (pool_id < 0) - goto out; - pool = zcache_client.tmem_pools[pool_id]; - if (pool == NULL) - goto out; - zcache_client.tmem_pools[pool_id] = NULL; - /* wait for pool activity on other cpus to quiesce */ - while (atomic_read(&pool->refcount) != 0) - ; - local_bh_disable(); - ret = tmem_destroy_pool(pool); - local_bh_enable(); - kfree(pool); - pr_info("zcache: destroyed pool id=%d\n", pool_id); -out: - return ret; -} - -static int zcache_new_pool(uint32_t flags) -{ - int poolid = -1; - struct tmem_pool *pool; - - pool = kmalloc(sizeof(struct tmem_pool), GFP_KERNEL); - if (pool == NULL) { - pr_info("zcache: pool creation failed: out of memory\n"); - goto out; - } - - for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++) - if (zcache_client.tmem_pools[poolid] == NULL) - break; - if (poolid >= MAX_POOLS_PER_CLIENT) { - pr_info("zcache: pool creation failed: max exceeded\n"); - kfree(pool); - poolid = -1; - goto out; - } - atomic_set(&pool->refcount, 0); - pool->client = &zcache_client; - pool->pool_id = poolid; - tmem_new_pool(pool, flags); - zcache_client.tmem_pools[poolid] = pool; - pr_info("zcache: created %s tmem pool, id=%d\n", - flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral", - poolid); -out: - return poolid; -} - -/********** - * Two kernel functionalities currently can be layered on top of tmem. - * These are "cleancache" which is used as a second-chance cache for clean - * page cache pages; and "frontswap" which is used for swap pages - * to avoid writes to disk. A generic "shim" is provided here for each - * to translate in-kernel semantics to zcache semantics. - */ - -#ifdef CONFIG_CLEANCACHE -static void zcache_cleancache_put_page(int pool_id, - struct cleancache_filekey key, - pgoff_t index, struct page *page) -{ - u32 ind = (u32) index; - struct tmem_oid oid = *(struct tmem_oid *)&key; - - if (likely(ind == index)) - (void)zcache_put_page(pool_id, &oid, index, page); -} - -static int zcache_cleancache_get_page(int pool_id, - struct cleancache_filekey key, - pgoff_t index, struct page *page) -{ - u32 ind = (u32) index; - struct tmem_oid oid = *(struct tmem_oid *)&key; - int ret = -1; - - if (likely(ind == index)) - ret = zcache_get_page(pool_id, &oid, index, page); - return ret; -} - -static void zcache_cleancache_flush_page(int pool_id, - struct cleancache_filekey key, - pgoff_t index) -{ - u32 ind = (u32) index; - struct tmem_oid oid = *(struct tmem_oid *)&key; - - if (likely(ind == index)) - (void)zcache_flush_page(pool_id, &oid, ind); -} - -static void zcache_cleancache_flush_inode(int pool_id, - struct cleancache_filekey key) -{ - struct tmem_oid oid = *(struct tmem_oid *)&key; - - (void)zcache_flush_object(pool_id, &oid); -} - -static void zcache_cleancache_flush_fs(int pool_id) -{ - if (pool_id >= 0) - (void)zcache_destroy_pool(pool_id); -} - -static int zcache_cleancache_init_fs(size_t pagesize) -{ - BUG_ON(sizeof(struct cleancache_filekey) != - sizeof(struct tmem_oid)); - BUG_ON(pagesize != PAGE_SIZE); - return zcache_new_pool(0); -} - -static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize) -{ - /* shared pools are unsupported and map to private */ - BUG_ON(sizeof(struct cleancache_filekey) != - sizeof(struct tmem_oid)); - BUG_ON(pagesize != PAGE_SIZE); - return zcache_new_pool(0); -} - -static struct cleancache_ops zcache_cleancache_ops = { - .put_page = zcache_cleancache_put_page, - .get_page = zcache_cleancache_get_page, - .flush_page = zcache_cleancache_flush_page, - .flush_inode = zcache_cleancache_flush_inode, - .flush_fs = zcache_cleancache_flush_fs, - .init_shared_fs = zcache_cleancache_init_shared_fs, - .init_fs = zcache_cleancache_init_fs -}; - -struct cleancache_ops zcache_cleancache_register_ops(void) -{ - struct cleancache_ops old_ops = - cleancache_register_ops(&zcache_cleancache_ops); - - return old_ops; -} -#endif - -#ifdef CONFIG_FRONTSWAP -/* a single tmem poolid is used for all frontswap "types" (swapfiles) */ -static int zcache_frontswap_poolid = -1; - -/* - * Swizzling increases objects per swaptype, increasing tmem concurrency - * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS - */ -#define SWIZ_BITS 4 -#define SWIZ_MASK ((1 << SWIZ_BITS) - 1) -#define _oswiz(_type, _ind) ((_type << SWIZ_BITS) | (_ind & SWIZ_MASK)) -#define iswiz(_ind) (_ind >> SWIZ_BITS) - -static inline struct tmem_oid oswiz(unsigned type, u32 ind) -{ - struct tmem_oid oid = { .oid = { 0 } }; - oid.oid[0] = _oswiz(type, ind); - return oid; -} - -static int zcache_frontswap_put_page(unsigned type, pgoff_t offset, - struct page *page) -{ - u64 ind64 = (u64)offset; - u32 ind = (u32)offset; - struct tmem_oid oid = oswiz(type, ind); - int ret = -1; - unsigned long flags; - - BUG_ON(!PageLocked(page)); - if (likely(ind64 == ind)) { - local_irq_save(flags); - ret = zcache_put_page(zcache_frontswap_poolid, &oid, - iswiz(ind), page); - local_irq_restore(flags); - } - return ret; -} - -/* returns 0 if the page was successfully gotten from frontswap, -1 if - * was not present (should never happen!) */ -static int zcache_frontswap_get_page(unsigned type, pgoff_t offset, - struct page *page) -{ - u64 ind64 = (u64)offset; - u32 ind = (u32)offset; - struct tmem_oid oid = oswiz(type, ind); - int ret = -1; - - BUG_ON(!PageLocked(page)); - if (likely(ind64 == ind)) - ret = zcache_get_page(zcache_frontswap_poolid, &oid, - iswiz(ind), page); - return ret; -} - -/* flush a single page from frontswap */ -static void zcache_frontswap_flush_page(unsigned type, pgoff_t offset) -{ - u64 ind64 = (u64)offset; - u32 ind = (u32)offset; - struct tmem_oid oid = oswiz(type, ind); - - if (likely(ind64 == ind)) - (void)zcache_flush_page(zcache_frontswap_poolid, &oid, - iswiz(ind)); -} - -/* flush all pages from the passed swaptype */ -static void zcache_frontswap_flush_area(unsigned type) -{ - struct tmem_oid oid; - int ind; - - for (ind = SWIZ_MASK; ind >= 0; ind--) { - oid = oswiz(type, ind); - (void)zcache_flush_object(zcache_frontswap_poolid, &oid); - } -} - -static void zcache_frontswap_init(unsigned ignored) -{ - /* a single tmem poolid is used for all frontswap "types" (swapfiles) */ - if (zcache_frontswap_poolid < 0) - zcache_frontswap_poolid = zcache_new_pool(TMEM_POOL_PERSIST); -} - -static struct frontswap_ops zcache_frontswap_ops = { - .put_page = zcache_frontswap_put_page, - .get_page = zcache_frontswap_get_page, - .flush_page = zcache_frontswap_flush_page, - .flush_area = zcache_frontswap_flush_area, - .init = zcache_frontswap_init -}; - -struct frontswap_ops zcache_frontswap_register_ops(void) -{ - struct frontswap_ops old_ops = - frontswap_register_ops(&zcache_frontswap_ops); - - return old_ops; -} -#endif - -/* - * zcache initialization - * NOTE FOR NOW zcache MUST BE PROVIDED AS A KERNEL BOOT PARAMETER OR - * NOTHING HAPPENS! - */ - -static int zcache_enabled; - -static int __init enable_zcache(char *s) -{ - zcache_enabled = 1; - return 1; -} -__setup("zcache", enable_zcache); - -/* allow independent dynamic disabling of cleancache and frontswap */ - -static int use_cleancache = 1; - -static int __init no_cleancache(char *s) -{ - use_cleancache = 0; - return 1; -} - -__setup("nocleancache", no_cleancache); - -static int use_frontswap = 1; - -static int __init no_frontswap(char *s) -{ - use_frontswap = 0; - return 1; -} - -__setup("nofrontswap", no_frontswap); - -static int __init zcache_init(void) -{ -#ifdef CONFIG_SYSFS - int ret = 0; - - ret = sysfs_create_group(mm_kobj, &zcache_attr_group); - if (ret) { - pr_err("zcache: can't create sysfs\n"); - goto out; - } -#endif /* CONFIG_SYSFS */ -#if defined(CONFIG_CLEANCACHE) || defined(CONFIG_FRONTSWAP) - if (zcache_enabled) { - unsigned int cpu; - - tmem_register_hostops(&zcache_hostops); - tmem_register_pamops(&zcache_pamops); - ret = register_cpu_notifier(&zcache_cpu_notifier_block); - if (ret) { - pr_err("zcache: can't register cpu notifier\n"); - goto out; - } - for_each_online_cpu(cpu) { - void *pcpu = (void *)(long)cpu; - zcache_cpu_notifier(&zcache_cpu_notifier_block, - CPU_UP_PREPARE, pcpu); - } - } - zcache_objnode_cache = kmem_cache_create("zcache_objnode", - sizeof(struct tmem_objnode), 0, 0, NULL); - zcache_obj_cache = kmem_cache_create("zcache_obj", - sizeof(struct tmem_obj), 0, 0, NULL); -#endif -#ifdef CONFIG_CLEANCACHE - if (zcache_enabled && use_cleancache) { - struct cleancache_ops old_ops; - - zbud_init(); - register_shrinker(&zcache_shrinker); - old_ops = zcache_cleancache_register_ops(); - pr_info("zcache: cleancache enabled using kernel " - "transcendent memory and compression buddies\n"); - if (old_ops.init_fs != NULL) - pr_warning("zcache: cleancache_ops overridden"); - } -#endif -#ifdef CONFIG_FRONTSWAP - if (zcache_enabled && use_frontswap) { - struct frontswap_ops old_ops; - - zcache_client.xvpool = xv_create_pool(); - if (zcache_client.xvpool == NULL) { - pr_err("zcache: can't create xvpool\n"); - goto out; - } - old_ops = zcache_frontswap_register_ops(); - pr_info("zcache: frontswap enabled using kernel " - "transcendent memory and xvmalloc\n"); - if (old_ops.init != NULL) - pr_warning("ktmem: frontswap_ops overridden"); - } -#endif -out: - return ret; -} - -module_init(zcache_init) -- cgit v1.1 From 95dd8144f28d56ea9f73e59ee81fb553e6f18cf4 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Mon, 9 Jan 2012 16:51:56 -0600 Subject: staging: zsmalloc: zsmalloc memory allocation library This patch creates a new memory allocation library named zsmalloc. NOTE: zsmalloc currently depends on SPARSEMEM for the MAX_PHYSMEM_BITS value needed to determine the format of the object handle. There may be a better way to do this. Feedback is welcome. Signed-off-by: Nitin Gupta Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/Kconfig | 11 + drivers/staging/zsmalloc/Makefile | 3 + drivers/staging/zsmalloc/zsmalloc-main.c | 756 +++++++++++++++++++++++++++++++ drivers/staging/zsmalloc/zsmalloc.h | 31 ++ drivers/staging/zsmalloc/zsmalloc_int.h | 126 ++++++ 5 files changed, 927 insertions(+) create mode 100644 drivers/staging/zsmalloc/Kconfig create mode 100644 drivers/staging/zsmalloc/Makefile create mode 100644 drivers/staging/zsmalloc/zsmalloc-main.c create mode 100644 drivers/staging/zsmalloc/zsmalloc.h create mode 100644 drivers/staging/zsmalloc/zsmalloc_int.h diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig new file mode 100644 index 0000000..3e7a8d4 --- /dev/null +++ b/drivers/staging/zsmalloc/Kconfig @@ -0,0 +1,11 @@ +config ZSMALLOC + tristate "Memory allocator for compressed pages" + depends on SPARSEMEM + default n + help + zsmalloc is a slab-based memory allocator designed to store + compressed RAM pages. zsmalloc uses virtual memory mapping + in order to reduce fragmentation. However, this results in a + non-standard allocator interface where a handle, not a pointer, is + returned by an alloc(). This handle must be mapped in order to + access the allocated space. diff --git a/drivers/staging/zsmalloc/Makefile b/drivers/staging/zsmalloc/Makefile new file mode 100644 index 0000000..b134848 --- /dev/null +++ b/drivers/staging/zsmalloc/Makefile @@ -0,0 +1,3 @@ +zsmalloc-y := zsmalloc-main.o + +obj-$(CONFIG_ZSMALLOC) += zsmalloc.o diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c new file mode 100644 index 0000000..189fb42 --- /dev/null +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -0,0 +1,756 @@ +/* + * zsmalloc memory allocator + * + * Copyright (C) 2011 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the license that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + */ + +#ifdef CONFIG_ZSMALLOC_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zsmalloc.h" +#include "zsmalloc_int.h" + +/* + * A zspage's class index and fullness group + * are encoded in its (first)page->mapping + */ +#define CLASS_IDX_BITS 28 +#define FULLNESS_BITS 4 +#define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) +#define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) + +/* + * Object location (, ) is encoded as + * as single (void *) handle value. + * + * Note that object index is relative to system + * page it is stored in, so for each sub-page belonging + * to a zspage, obj_idx starts with 0. + */ +#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) +#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) +#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) + +/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ +static DEFINE_PER_CPU(struct mapping_area, zs_map_area); + +static int is_first_page(struct page *page) +{ + return test_bit(PG_private, &page->flags); +} + +static int is_last_page(struct page *page) +{ + return test_bit(PG_private_2, &page->flags); +} + +static void get_zspage_mapping(struct page *page, unsigned int *class_idx, + enum fullness_group *fullness) +{ + unsigned long m; + BUG_ON(!is_first_page(page)); + + m = (unsigned long)page->mapping; + *fullness = m & FULLNESS_MASK; + *class_idx = (m >> FULLNESS_BITS) & CLASS_IDX_MASK; +} + +static void set_zspage_mapping(struct page *page, unsigned int class_idx, + enum fullness_group fullness) +{ + unsigned long m; + BUG_ON(!is_first_page(page)); + + m = ((class_idx & CLASS_IDX_MASK) << FULLNESS_BITS) | + (fullness & FULLNESS_MASK); + page->mapping = (struct address_space *)m; +} + +static int get_size_class_index(int size) +{ + int idx = 0; + + if (likely(size > ZS_MIN_ALLOC_SIZE)) + idx = DIV_ROUND_UP(size - ZS_MIN_ALLOC_SIZE, + ZS_SIZE_CLASS_DELTA); + + return idx; +} + +static enum fullness_group get_fullness_group(struct page *page) +{ + int inuse, max_objects; + enum fullness_group fg; + BUG_ON(!is_first_page(page)); + + inuse = page->inuse; + max_objects = page->objects; + + if (inuse == 0) + fg = ZS_EMPTY; + else if (inuse == max_objects) + fg = ZS_FULL; + else if (inuse <= max_objects / fullness_threshold_frac) + fg = ZS_ALMOST_EMPTY; + else + fg = ZS_ALMOST_FULL; + + return fg; +} + +static void insert_zspage(struct page *page, struct size_class *class, + enum fullness_group fullness) +{ + struct page **head; + + BUG_ON(!is_first_page(page)); + + if (fullness >= _ZS_NR_FULLNESS_GROUPS) + return; + + head = &class->fullness_list[fullness]; + if (*head) + list_add_tail(&page->lru, &(*head)->lru); + + *head = page; +} + +static void remove_zspage(struct page *page, struct size_class *class, + enum fullness_group fullness) +{ + struct page **head; + + BUG_ON(!is_first_page(page)); + + if (fullness >= _ZS_NR_FULLNESS_GROUPS) + return; + + head = &class->fullness_list[fullness]; + BUG_ON(!*head); + if (list_empty(&(*head)->lru)) + *head = NULL; + else if (*head == page) + *head = (struct page *)list_entry((*head)->lru.next, + struct page, lru); + + list_del_init(&page->lru); +} + +static enum fullness_group fix_fullness_group(struct zs_pool *pool, + struct page *page) +{ + int class_idx; + struct size_class *class; + enum fullness_group currfg, newfg; + + BUG_ON(!is_first_page(page)); + + get_zspage_mapping(page, &class_idx, &currfg); + newfg = get_fullness_group(page); + if (newfg == currfg) + goto out; + + class = &pool->size_class[class_idx]; + remove_zspage(page, class, currfg); + insert_zspage(page, class, newfg); + set_zspage_mapping(page, class_idx, newfg); + +out: + return newfg; +} + +/* + * We have to decide on how many pages to link together + * to form a zspage for each size class. This is important + * to reduce wastage due to unusable space left at end of + * each zspage which is given as: + * wastage = Zp - Zp % size_class + * where Zp = zspage size = k * PAGE_SIZE where k = 1, 2, ... + * + * For example, for size class of 3/8 * PAGE_SIZE, we should + * link together 3 PAGE_SIZE sized pages to form a zspage + * since then we can perfectly fit in 8 such objects. + */ +static int get_zspage_order(int class_size) +{ + int i, max_usedpc = 0; + /* zspage order which gives maximum used size per KB */ + int max_usedpc_order = 1; + + for (i = 1; i <= max_zspage_order; i++) { + int zspage_size; + int waste, usedpc; + + zspage_size = i * PAGE_SIZE; + waste = zspage_size % class_size; + usedpc = (zspage_size - waste) * 100 / zspage_size; + + if (usedpc > max_usedpc) { + max_usedpc = usedpc; + max_usedpc_order = i; + } + } + + return max_usedpc_order; +} + +/* + * A single 'zspage' is composed of many system pages which are + * linked together using fields in struct page. This function finds + * the first/head page, given any component page of a zspage. + */ +static struct page *get_first_page(struct page *page) +{ + if (is_first_page(page)) + return page; + else + return page->first_page; +} + +static struct page *get_next_page(struct page *page) +{ + struct page *next; + + if (is_last_page(page)) + next = NULL; + else if (is_first_page(page)) + next = (struct page *)page->private; + else + next = list_entry(page->lru.next, struct page, lru); + + return next; +} + +/* Encode as a single handle value */ +static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) +{ + unsigned long handle; + + if (!page) { + BUG_ON(obj_idx); + return NULL; + } + + handle = page_to_pfn(page) << OBJ_INDEX_BITS; + handle |= (obj_idx & OBJ_INDEX_MASK); + + return (void *)handle; +} + +/* Decode pair from the given object handle */ +static void obj_handle_to_location(void *handle, struct page **page, + unsigned long *obj_idx) +{ + unsigned long hval = (unsigned long)handle; + + *page = pfn_to_page(hval >> OBJ_INDEX_BITS); + *obj_idx = hval & OBJ_INDEX_MASK; +} + +static unsigned long obj_idx_to_offset(struct page *page, + unsigned long obj_idx, int class_size) +{ + unsigned long off = 0; + + if (!is_first_page(page)) + off = page->index; + + return off + obj_idx * class_size; +} + +static void free_zspage(struct page *first_page) +{ + struct page *nextp, *tmp; + + BUG_ON(!is_first_page(first_page)); + BUG_ON(first_page->inuse); + + nextp = (struct page *)page_private(first_page); + + clear_bit(PG_private, &first_page->flags); + clear_bit(PG_private_2, &first_page->flags); + set_page_private(first_page, 0); + first_page->mapping = NULL; + first_page->freelist = NULL; + reset_page_mapcount(first_page); + __free_page(first_page); + + /* zspage with only 1 system page */ + if (!nextp) + return; + + list_for_each_entry_safe(nextp, tmp, &nextp->lru, lru) { + list_del(&nextp->lru); + clear_bit(PG_private_2, &nextp->flags); + nextp->index = 0; + __free_page(nextp); + } +} + +/* Initialize a newly allocated zspage */ +static void init_zspage(struct page *first_page, struct size_class *class) +{ + unsigned long off = 0; + struct page *page = first_page; + + BUG_ON(!is_first_page(first_page)); + while (page) { + struct page *next_page; + struct link_free *link; + unsigned int i, objs_on_page; + + /* + * page->index stores offset of first object starting + * in the page. For the first page, this is always 0, + * so we use first_page->index (aka ->freelist) to store + * head of corresponding zspage's freelist. + */ + if (page != first_page) + page->index = off; + + link = (struct link_free *)kmap_atomic(page) + + off / sizeof(*link); + objs_on_page = (PAGE_SIZE - off) / class->size; + + for (i = 1; i <= objs_on_page; i++) { + off += class->size; + if (off < PAGE_SIZE) { + link->next = obj_location_to_handle(page, i); + link += class->size / sizeof(*link); + } + } + + /* + * We now come to the last (full or partial) object on this + * page, which must point to the first object on the next + * page (if present) + */ + next_page = get_next_page(page); + link->next = obj_location_to_handle(next_page, 0); + kunmap_atomic(link); + page = next_page; + off = (off + class->size) % PAGE_SIZE; + } +} + +/* + * Allocate a zspage for the given size class + */ +static struct page *alloc_zspage(struct size_class *class, gfp_t flags) +{ + int i, error; + struct page *first_page = NULL; + + /* + * Allocate individual pages and link them together as: + * 1. first page->private = first sub-page + * 2. all sub-pages are linked together using page->lru + * 3. each sub-page is linked to the first page using page->first_page + * + * For each size class, First/Head pages are linked together using + * page->lru. Also, we set PG_private to identify the first page + * (i.e. no other sub-page has this flag set) and PG_private_2 to + * identify the last page. + */ + error = -ENOMEM; + for (i = 0; i < class->zspage_order; i++) { + struct page *page, *prev_page; + + page = alloc_page(flags); + if (!page) + goto cleanup; + + INIT_LIST_HEAD(&page->lru); + if (i == 0) { /* first page */ + set_bit(PG_private, &page->flags); + set_page_private(page, 0); + first_page = page; + first_page->inuse = 0; + } + if (i == 1) + first_page->private = (unsigned long)page; + if (i >= 1) + page->first_page = first_page; + if (i >= 2) + list_add(&page->lru, &prev_page->lru); + if (i == class->zspage_order - 1) /* last page */ + set_bit(PG_private_2, &page->flags); + + prev_page = page; + } + + init_zspage(first_page, class); + + first_page->freelist = obj_location_to_handle(first_page, 0); + /* Maximum number of objects we can store in this zspage */ + first_page->objects = class->zspage_order * PAGE_SIZE / class->size; + + error = 0; /* Success */ + +cleanup: + if (unlikely(error) && first_page) { + free_zspage(first_page); + first_page = NULL; + } + + return first_page; +} + +static struct page *find_get_zspage(struct size_class *class) +{ + int i; + struct page *page; + + for (i = 0; i < _ZS_NR_FULLNESS_GROUPS; i++) { + page = class->fullness_list[i]; + if (page) + break; + } + + return page; +} + + +/* + * If this becomes a separate module, register zs_init() with + * module_init(), zs_exit with module_exit(), and remove zs_initialized +*/ +static int zs_initialized; + +static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, + void *pcpu) +{ + int cpu = (long)pcpu; + struct mapping_area *area; + + switch (action) { + case CPU_UP_PREPARE: + area = &per_cpu(zs_map_area, cpu); + if (area->vm) + break; + area->vm = alloc_vm_area(2 * PAGE_SIZE, area->vm_ptes); + if (!area->vm) + return notifier_from_errno(-ENOMEM); + break; + case CPU_DEAD: + case CPU_UP_CANCELED: + area = &per_cpu(zs_map_area, cpu); + if (area->vm) + free_vm_area(area->vm); + area->vm = NULL; + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block zs_cpu_nb = { + .notifier_call = zs_cpu_notifier +}; + +static void zs_exit(void) +{ + int cpu; + + for_each_online_cpu(cpu) + zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu); + unregister_cpu_notifier(&zs_cpu_nb); +} + +static int zs_init(void) +{ + int cpu, ret; + + register_cpu_notifier(&zs_cpu_nb); + for_each_online_cpu(cpu) { + ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu); + if (notifier_to_errno(ret)) + goto fail; + } + return 0; +fail: + zs_exit(); + return notifier_to_errno(ret); +} + +struct zs_pool *zs_create_pool(const char *name, gfp_t flags) +{ + int i, error, ovhd_size; + struct zs_pool *pool; + + if (!name) + return NULL; + + ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); + pool = kzalloc(ovhd_size, GFP_KERNEL); + if (!pool) + return NULL; + + for (i = 0; i < ZS_SIZE_CLASSES; i++) { + int size; + struct size_class *class; + + size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; + if (size > ZS_MAX_ALLOC_SIZE) + size = ZS_MAX_ALLOC_SIZE; + + class = &pool->size_class[i]; + class->size = size; + class->index = i; + spin_lock_init(&class->lock); + class->zspage_order = get_zspage_order(size); + + } + + /* + * If this becomes a separate module, register zs_init with + * module_init, and remove this block + */ + if (!zs_initialized) { + error = zs_init(); + if (error) + goto cleanup; + zs_initialized = 1; + } + + pool->flags = flags; + pool->name = name; + + error = 0; /* Success */ + +cleanup: + if (error) { + zs_destroy_pool(pool); + pool = NULL; + } + + return pool; +} +EXPORT_SYMBOL_GPL(zs_create_pool); + +void zs_destroy_pool(struct zs_pool *pool) +{ + int i; + + for (i = 0; i < ZS_SIZE_CLASSES; i++) { + int fg; + struct size_class *class = &pool->size_class[i]; + + for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { + if (class->fullness_list[fg]) { + pr_info("Freeing non-empty class with size " + "%db, fullness group %d\n", + class->size, fg); + } + } + } + kfree(pool); +} +EXPORT_SYMBOL_GPL(zs_destroy_pool); + +/** + * zs_malloc - Allocate block of given size from pool. + * @pool: pool to allocate from + * @size: size of block to allocate + * @page: page no. that holds the object + * @offset: location of object within page + * + * On success, identifies block allocated + * and 0 is returned. On failure, is set to + * 0 and -ENOMEM is returned. + * + * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail. + */ +void *zs_malloc(struct zs_pool *pool, size_t size) +{ + void *obj; + struct link_free *link; + int class_idx; + struct size_class *class; + + struct page *first_page, *m_page; + unsigned long m_objidx, m_offset; + + if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) + return NULL; + + class_idx = get_size_class_index(size); + class = &pool->size_class[class_idx]; + BUG_ON(class_idx != class->index); + + spin_lock(&class->lock); + first_page = find_get_zspage(class); + + if (!first_page) { + spin_unlock(&class->lock); + first_page = alloc_zspage(class, pool->flags); + if (unlikely(!first_page)) + return NULL; + + set_zspage_mapping(first_page, class->index, ZS_EMPTY); + spin_lock(&class->lock); + class->pages_allocated += class->zspage_order; + } + + obj = first_page->freelist; + obj_handle_to_location(obj, &m_page, &m_objidx); + m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); + + link = (struct link_free *)kmap_atomic(m_page) + + m_offset / sizeof(*link); + first_page->freelist = link->next; + memset(link, POISON_INUSE, sizeof(*link)); + kunmap_atomic(link); + + first_page->inuse++; + /* Now move the zspage to another fullness group, if required */ + fix_fullness_group(pool, first_page); + spin_unlock(&class->lock); + + return obj; +} +EXPORT_SYMBOL_GPL(zs_malloc); + +void zs_free(struct zs_pool *pool, void *obj) +{ + struct link_free *link; + struct page *first_page, *f_page; + unsigned long f_objidx, f_offset; + + int class_idx; + struct size_class *class; + enum fullness_group fullness; + + if (unlikely(!obj)) + return; + + obj_handle_to_location(obj, &f_page, &f_objidx); + first_page = get_first_page(f_page); + + get_zspage_mapping(first_page, &class_idx, &fullness); + class = &pool->size_class[class_idx]; + f_offset = obj_idx_to_offset(f_page, f_objidx, class->size); + + spin_lock(&class->lock); + + /* Insert this object in containing zspage's freelist */ + link = (struct link_free *)((unsigned char *)kmap_atomic(f_page) + + f_offset); + link->next = first_page->freelist; + kunmap_atomic(link); + first_page->freelist = obj; + + first_page->inuse--; + fullness = fix_fullness_group(pool, first_page); + + if (fullness == ZS_EMPTY) + class->pages_allocated -= class->zspage_order; + + spin_unlock(&class->lock); + + if (fullness == ZS_EMPTY) + free_zspage(first_page); +} +EXPORT_SYMBOL_GPL(zs_free); + +void *zs_map_object(struct zs_pool *pool, void *handle) +{ + struct page *page; + unsigned long obj_idx, off; + + unsigned int class_idx; + enum fullness_group fg; + struct size_class *class; + struct mapping_area *area; + + BUG_ON(!handle); + + obj_handle_to_location(handle, &page, &obj_idx); + get_zspage_mapping(get_first_page(page), &class_idx, &fg); + class = &pool->size_class[class_idx]; + off = obj_idx_to_offset(page, obj_idx, class->size); + + area = &get_cpu_var(zs_map_area); + if (off + class->size <= PAGE_SIZE) { + /* this object is contained entirely within a page */ + area->vm_addr = kmap_atomic(page); + } else { + /* this object spans two pages */ + struct page *nextp; + + nextp = get_next_page(page); + BUG_ON(!nextp); + + + set_pte(area->vm_ptes[0], mk_pte(page, PAGE_KERNEL)); + set_pte(area->vm_ptes[1], mk_pte(nextp, PAGE_KERNEL)); + + /* We pre-allocated VM area so mapping can never fail */ + area->vm_addr = area->vm->addr; + } + + return area->vm_addr + off; +} +EXPORT_SYMBOL_GPL(zs_map_object); + +void zs_unmap_object(struct zs_pool *pool, void *handle) +{ + struct page *page; + unsigned long obj_idx, off; + + unsigned int class_idx; + enum fullness_group fg; + struct size_class *class; + struct mapping_area *area; + + BUG_ON(!handle); + + obj_handle_to_location(handle, &page, &obj_idx); + get_zspage_mapping(get_first_page(page), &class_idx, &fg); + class = &pool->size_class[class_idx]; + off = obj_idx_to_offset(page, obj_idx, class->size); + + area = &__get_cpu_var(zs_map_area); + if (off + class->size <= PAGE_SIZE) { + kunmap_atomic(area->vm_addr); + } else { + set_pte(area->vm_ptes[0], __pte(0)); + set_pte(area->vm_ptes[1], __pte(0)); + __flush_tlb_one((unsigned long)area->vm_addr); + __flush_tlb_one((unsigned long)area->vm_addr + PAGE_SIZE); + } + put_cpu_var(zs_map_area); +} +EXPORT_SYMBOL_GPL(zs_unmap_object); + +u64 zs_get_total_size_bytes(struct zs_pool *pool) +{ + int i; + u64 npages = 0; + + for (i = 0; i < ZS_SIZE_CLASSES; i++) + npages += pool->size_class[i].pages_allocated; + + return npages << PAGE_SHIFT; +} +EXPORT_SYMBOL_GPL(zs_get_total_size_bytes); diff --git a/drivers/staging/zsmalloc/zsmalloc.h b/drivers/staging/zsmalloc/zsmalloc.h new file mode 100644 index 0000000..949384e --- /dev/null +++ b/drivers/staging/zsmalloc/zsmalloc.h @@ -0,0 +1,31 @@ +/* + * zsmalloc memory allocator + * + * Copyright (C) 2011 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the license that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + */ + +#ifndef _ZS_MALLOC_H_ +#define _ZS_MALLOC_H_ + +#include + +struct zs_pool; + +struct zs_pool *zs_create_pool(const char *name, gfp_t flags); +void zs_destroy_pool(struct zs_pool *pool); + +void *zs_malloc(struct zs_pool *pool, size_t size); +void zs_free(struct zs_pool *pool, void *obj); + +void *zs_map_object(struct zs_pool *pool, void *handle); +void zs_unmap_object(struct zs_pool *pool, void *handle); + +u64 zs_get_total_size_bytes(struct zs_pool *pool); + +#endif diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h new file mode 100644 index 0000000..354a020 --- /dev/null +++ b/drivers/staging/zsmalloc/zsmalloc_int.h @@ -0,0 +1,126 @@ +/* + * zsmalloc memory allocator + * + * Copyright (C) 2011 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the license that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + */ + +#ifndef _ZS_MALLOC_INT_H_ +#define _ZS_MALLOC_INT_H_ + +#include +#include +#include + +/* + * This must be power of 2 and greater than of equal to sizeof(link_free). + * These two conditions ensure that any 'struct link_free' itself doesn't + * span more than 1 page which avoids complex case of mapping 2 pages simply + * to restore link_free pointer values. + */ +#define ZS_ALIGN 8 + +/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ +#define ZS_MIN_ALLOC_SIZE 32 +#define ZS_MAX_ALLOC_SIZE PAGE_SIZE + +/* + * On systems with 4K page size, this gives 254 size classes! There is a + * trader-off here: + * - Large number of size classes is potentially wasteful as free page are + * spread across these classes + * - Small number of size classes causes large internal fragmentation + * - Probably its better to use specific size classes (empirically + * determined). NOTE: all those class sizes must be set as multiple of + * ZS_ALIGN to make sure link_free itself never has to span 2 pages. + * + * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN + * (reason above) + */ +#define ZS_SIZE_CLASS_DELTA 16 +#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \ + ZS_SIZE_CLASS_DELTA + 1) + +/* + * A single 'zspage' is composed of N discontiguous 0-order (single) pages. + * This defines upper limit on N. + */ +static const int max_zspage_order = 4; + +/* + * We do not maintain any list for completely empty or full pages + */ +enum fullness_group { + ZS_ALMOST_FULL, + ZS_ALMOST_EMPTY, + _ZS_NR_FULLNESS_GROUPS, + + ZS_EMPTY, + ZS_FULL +}; + +/* + * We assign a page to ZS_ALMOST_EMPTY fullness group when: + * n <= N / f, where + * n = number of allocated objects + * N = total number of objects zspage can store + * f = 1/fullness_threshold_frac + * + * Similarly, we assign zspage to: + * ZS_ALMOST_FULL when n > N / f + * ZS_EMPTY when n == 0 + * ZS_FULL when n == N + * + * (see: fix_fullness_group()) + */ +static const int fullness_threshold_frac = 4; + +struct mapping_area { + struct vm_struct *vm; + pte_t *vm_ptes[2]; + char *vm_addr; +}; + +struct size_class { + /* + * Size of objects stored in this class. Must be multiple + * of ZS_ALIGN. + */ + int size; + unsigned int index; + + /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ + int zspage_order; + + spinlock_t lock; + + /* stats */ + u64 pages_allocated; + + struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS]; +}; + +/* + * Placed within free objects to form a singly linked list. + * For every zspage, first_page->freelist gives head of this list. + * + * This must be power of 2 and less than or equal to ZS_ALIGN + */ +struct link_free { + /* Handle of next free chunk (encodes ) */ + void *next; +}; + +struct zs_pool { + struct size_class size_class[ZS_SIZE_CLASSES]; + + gfp_t flags; /* allocation flags used when growing pool */ + const char *name; +}; + +#endif -- cgit v1.1 From cf008df70f38566a56ecc0e857b48fd02b772a5c Mon Sep 17 00:00:00 2001 From: Emerson Pinter Date: Mon, 9 Sep 2013 10:55:16 -0300 Subject: staging: fix powerpc linux-next break on zsmalloc linux/vmalloc.h added to zsmalloc-main.c to resolve implicit declaration errors. X86 dependency added to zsmalloc and dependent drivers zcache and zram. This X86 only requirement is not ideal. Working to find portable functions for __flush_tlb_one and set_pte. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zcache/Kconfig drivers/staging/zram/Kconfig Change-Id: I9a1eaf873c5ca734625c62f704c32e66229492e4 --- drivers/staging/zcache/Kconfig | 6 ++++-- drivers/staging/zsmalloc/Kconfig | 5 ++++- drivers/staging/zsmalloc/zsmalloc-main.c | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 1b7bba7..5a36fcf 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -1,7 +1,9 @@ config ZCACHE tristate "Dynamic compression of swap pages and clean pagecache pages" - depends on (CLEANCACHE || FRONTSWAP) && CRYPTO - select XVMALLOC + # X86 dependency is because zsmalloc uses non-portable pte/tlb + # functions + depends on (CLEANCACHE || FRONTSWAP) && CRYPTO && X86 + select ZSMALLOC select CRYPTO_LZO default n help diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig index 3e7a8d4..8e2c6a0 100644 --- a/drivers/staging/zsmalloc/Kconfig +++ b/drivers/staging/zsmalloc/Kconfig @@ -1,6 +1,9 @@ config ZSMALLOC tristate "Memory allocator for compressed pages" - depends on SPARSEMEM + # X86 dependency is because of the use of __flush_tlb_one and set_pte + # in zsmalloc-main.c. + # TODO: convert these to portable functions + depends on SPARSEMEM && X86 default n help zsmalloc is a slab-based memory allocator designed to store diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 189fb42..455fc2f 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "zsmalloc.h" #include "zsmalloc_int.h" -- cgit v1.1 From 7466addbc6391d1b1798ce0fbe2285d47650bd40 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 5 Mar 2012 11:33:20 -0600 Subject: staging: zsmalloc: move object/handle masking defines This patch moves the definitions of _PFN_BITS, OBJ_INDEX_BITS and OBJ_INDEX_MASK from zsmalloc-main.c to zsmalloc_int.h They will be needed to determine ZS_MIN_ALLOC_SIZE in the next patch Signed-off-by: Seth Jennings Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc-main.c | 12 ------------ drivers/staging/zsmalloc/zsmalloc_int.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 455fc2f..240bcbf 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -40,18 +40,6 @@ #define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) #define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) -/* - * Object location (, ) is encoded as - * as single (void *) handle value. - * - * Note that object index is relative to system - * page it is stored in, so for each sub-page belonging - * to a zspage, obj_idx starts with 0. - */ -#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) -#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) -#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) - /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ static DEFINE_PER_CPU(struct mapping_area, zs_map_area); diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h index 354a020..e06e142 100644 --- a/drivers/staging/zsmalloc/zsmalloc_int.h +++ b/drivers/staging/zsmalloc/zsmalloc_int.h @@ -25,6 +25,18 @@ */ #define ZS_ALIGN 8 +/* + * Object location (, ) is encoded as + * as single (void *) handle value. + * + * Note that object index is relative to system + * page it is stored in, so for each sub-page belonging + * to a zspage, obj_idx starts with 0. + */ +#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) +#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) +#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) + /* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ #define ZS_MIN_ALLOC_SIZE 32 #define ZS_MAX_ALLOC_SIZE PAGE_SIZE -- cgit v1.1 From da881f204fe954f5c1c8b058f0205c5a397e3d67 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 5 Mar 2012 11:33:21 -0600 Subject: staging: zsmalloc: add ZS_MAX_PAGES_PER_ZSPAGE This patch moves where max_zspage_order is declared and changes its meaning. "Order" typically implies 2^order of something; however, it is currently being used as the "maximum number of single pages in a zspage". To add clarity, ZS_MAX_ZSPAGE_ORDER is now used to calculate ZS_MAX_PAGES_PER_ZSPAGE, which is 2^ZS_MAX_ZSPAGE_ORDER and is the upper bound on the number of pages in a zspage. Signed-off-by: Seth Jennings Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc-main.c | 2 +- drivers/staging/zsmalloc/zsmalloc_int.h | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 240bcbf..09caa4f 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -186,7 +186,7 @@ static int get_zspage_order(int class_size) /* zspage order which gives maximum used size per KB */ int max_usedpc_order = 1; - for (i = 1; i <= max_zspage_order; i++) { + for (i = 1; i <= ZS_MAX_PAGES_PER_ZSPAGE; i++) { int zspage_size; int waste, usedpc; diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h index e06e142..4d66d2d 100644 --- a/drivers/staging/zsmalloc/zsmalloc_int.h +++ b/drivers/staging/zsmalloc/zsmalloc_int.h @@ -26,6 +26,13 @@ #define ZS_ALIGN 8 /* + * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single) + * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N. + */ +#define ZS_MAX_ZSPAGE_ORDER 2 +#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) + +/* * Object location (, ) is encoded as * as single (void *) handle value. * @@ -59,12 +66,6 @@ ZS_SIZE_CLASS_DELTA + 1) /* - * A single 'zspage' is composed of N discontiguous 0-order (single) pages. - * This defines upper limit on N. - */ -static const int max_zspage_order = 4; - -/* * We do not maintain any list for completely empty or full pages */ enum fullness_group { -- cgit v1.1 From 821ddf6a289a6aab85efc476238d6bac895c1d76 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 5 Mar 2012 11:33:22 -0600 Subject: staging: zsmalloc: calculate MAX_PHYSMEM_BITS if not defined This patch provides a way to determine or "set a reasonable value for" MAX_PHYSMEM_BITS in the case that it is not defined (i.e. !SPARSEMEM) Signed-off-by: Seth Jennings Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc_int.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h index 4d66d2d..ffb272f 100644 --- a/drivers/staging/zsmalloc/zsmalloc_int.h +++ b/drivers/staging/zsmalloc/zsmalloc_int.h @@ -39,7 +39,21 @@ * Note that object index is relative to system * page it is stored in, so for each sub-page belonging * to a zspage, obj_idx starts with 0. + * + * This is made more complicated by various memory models and PAE. + */ + +#ifndef MAX_PHYSMEM_BITS +#ifdef CONFIG_HIGHMEM64G +#define MAX_PHYSMEM_BITS 36 +#else /* !CONFIG_HIGHMEM64G */ +/* + * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just + * be PAGE_SHIFT */ +#define MAX_PHYSMEM_BITS BITS_PER_LONG +#endif +#endif #define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) #define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) #define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) -- cgit v1.1 From 9347527471e981cda870e8839c3cce6ebc99b743 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 5 Mar 2012 11:33:23 -0600 Subject: staging: zsmalloc: change ZS_MIN_ALLOC_SIZE This patch ensures that the value of ZS_MIN_ALLOC_SIZE, for the PAGE_SIZE and MAX_PHYSMEM_BITS on the system, allows for all possible object ids in the lowest storage class to be encoded in the object handle. Signed-off-by: Seth Jennings Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc_int.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h index ffb272f..92eefc6 100644 --- a/drivers/staging/zsmalloc/zsmalloc_int.h +++ b/drivers/staging/zsmalloc/zsmalloc_int.h @@ -58,8 +58,10 @@ #define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) #define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) /* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ -#define ZS_MIN_ALLOC_SIZE 32 +#define ZS_MIN_ALLOC_SIZE \ + MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) #define ZS_MAX_ALLOC_SIZE PAGE_SIZE /* -- cgit v1.1 From bd731ece8cc4d84666b9dd99b33e7054d7f2c671 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Mon, 2 Apr 2012 09:13:56 -0500 Subject: staging: zsmalloc: fix memory leak This patch fixes a memory leak in zsmalloc where the first subpage of each zspage is leaked when the zspage is freed. Signed-off-by: Nitin Gupta Acked-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc-main.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 09caa4f..917461c 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -267,33 +267,39 @@ static unsigned long obj_idx_to_offset(struct page *page, return off + obj_idx * class_size; } +static void reset_page(struct page *page) +{ + clear_bit(PG_private, &page->flags); + clear_bit(PG_private_2, &page->flags); + set_page_private(page, 0); + page->mapping = NULL; + page->freelist = NULL; + reset_page_mapcount(page); +} + static void free_zspage(struct page *first_page) { - struct page *nextp, *tmp; + struct page *nextp, *tmp, *head_extra; BUG_ON(!is_first_page(first_page)); BUG_ON(first_page->inuse); - nextp = (struct page *)page_private(first_page); + head_extra = (struct page *)page_private(first_page); - clear_bit(PG_private, &first_page->flags); - clear_bit(PG_private_2, &first_page->flags); - set_page_private(first_page, 0); - first_page->mapping = NULL; - first_page->freelist = NULL; - reset_page_mapcount(first_page); + reset_page(first_page); __free_page(first_page); /* zspage with only 1 system page */ - if (!nextp) + if (!head_extra) return; - list_for_each_entry_safe(nextp, tmp, &nextp->lru, lru) { + list_for_each_entry_safe(nextp, tmp, &head_extra->lru, lru) { list_del(&nextp->lru); - clear_bit(PG_private_2, &nextp->flags); - nextp->index = 0; + reset_page(nextp); __free_page(nextp); } + reset_page(head_extra); + __free_page(head_extra); } /* Initialize a newly allocated zspage */ -- cgit v1.1 From a6376ca6e6efa5cb3c6c2dc07796772cb8f8853f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 20 Jun 2012 02:31:11 +0100 Subject: staging: zsmalloc: Finish conversion to a separate module commit 069f101fa463351f528773d73b74e9b606b3f66a upstream. ZSMALLOC is tristate, but the code has no MODULE_LICENSE and since it depends on GPL-only symbols it cannot be loaded as a module. This in turn breaks zram which now depends on it. I assume it's meant to be Dual BSD/GPL like the other z-stuff. There is also no module_exit, which will make it impossible to unload. Add the appropriate module_init and module_exit declarations suggested by comments. Reported-by: Christian Ohm References: http://bugs.debian.org/677273 Signed-off-by: Ben Hutchings Reviewed-by: Jonathan Nieder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc-main.c | 33 +++++++------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 917461c..175b3c9 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -426,12 +426,6 @@ static struct page *find_get_zspage(struct size_class *class) } -/* - * If this becomes a separate module, register zs_init() with - * module_init(), zs_exit with module_exit(), and remove zs_initialized -*/ -static int zs_initialized; - static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, void *pcpu) { @@ -490,7 +484,7 @@ fail: struct zs_pool *zs_create_pool(const char *name, gfp_t flags) { - int i, error, ovhd_size; + int i, ovhd_size; struct zs_pool *pool; if (!name) @@ -517,28 +511,9 @@ struct zs_pool *zs_create_pool(const char *name, gfp_t flags) } - /* - * If this becomes a separate module, register zs_init with - * module_init, and remove this block - */ - if (!zs_initialized) { - error = zs_init(); - if (error) - goto cleanup; - zs_initialized = 1; - } - pool->flags = flags; pool->name = name; - error = 0; /* Success */ - -cleanup: - if (error) { - zs_destroy_pool(pool); - pool = NULL; - } - return pool; } EXPORT_SYMBOL_GPL(zs_create_pool); @@ -749,3 +724,9 @@ u64 zs_get_total_size_bytes(struct zs_pool *pool) return npages << PAGE_SHIFT; } EXPORT_SYMBOL_GPL(zs_get_total_size_bytes); + +module_init(zs_init); +module_exit(zs_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Nitin Gupta "); -- cgit v1.1 From 4c7cee5b45d355000398330faddd9e91a51dc643 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 2 Jul 2012 16:15:49 -0500 Subject: CHROMIUM: UPSTREAM: staging: zsmalloc: remove x86 dependency This patch replaces the page table assisted object mapping method, which has x86 dependencies, with a arch-independent method that does a simple copy into a temporary per-cpu buffer. While a copy seems like it would be worse than mapping the pages, tests demonstrate the copying is always faster and, in the case of running inside a KVM guest, roughly 4x faster. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman BUG=chromium-os:36829 TEST=extensive manual testing Change-Id: I8b41c92eb146c5cf0af40682802ba443f7810a0b Signed-off-by: Luigi Semenzato Reviewed-on: https://gerrit.chromium.org/gerrit/38999 Reviewed-by: Olof Johansson Conflicts: drivers/staging/zsmalloc/Kconfig --- drivers/staging/zsmalloc/Kconfig | 4 -- drivers/staging/zsmalloc/zsmalloc-main.c | 99 ++++++++++++++++++++++---------- drivers/staging/zsmalloc/zsmalloc_int.h | 5 +- 3 files changed, 72 insertions(+), 36 deletions(-) diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig index 8e2c6a0..9084565 100644 --- a/drivers/staging/zsmalloc/Kconfig +++ b/drivers/staging/zsmalloc/Kconfig @@ -1,9 +1,5 @@ config ZSMALLOC tristate "Memory allocator for compressed pages" - # X86 dependency is because of the use of __flush_tlb_one and set_pte - # in zsmalloc-main.c. - # TODO: convert these to portable functions - depends on SPARSEMEM && X86 default n help zsmalloc is a slab-based memory allocator designed to store diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 175b3c9..4cd0f04 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -425,6 +425,57 @@ static struct page *find_get_zspage(struct size_class *class) return page; } +static void zs_copy_map_object(char *buf, struct page *firstpage, + int off, int size) +{ + struct page *pages[2]; + int sizes[2]; + void *addr; + + pages[0] = firstpage; + pages[1] = get_next_page(firstpage); + BUG_ON(!pages[1]); + + sizes[0] = PAGE_SIZE - off; + sizes[1] = size - sizes[0]; + + /* disable page faults to match kmap_atomic() return conditions */ + pagefault_disable(); + + /* copy object to per-cpu buffer */ + addr = kmap_atomic(pages[0]); + memcpy(buf, addr + off, sizes[0]); + kunmap_atomic(addr); + addr = kmap_atomic(pages[1]); + memcpy(buf + sizes[0], addr, sizes[1]); + kunmap_atomic(addr); +} + +static void zs_copy_unmap_object(char *buf, struct page *firstpage, + int off, int size) +{ + struct page *pages[2]; + int sizes[2]; + void *addr; + + pages[0] = firstpage; + pages[1] = get_next_page(firstpage); + BUG_ON(!pages[1]); + + sizes[0] = PAGE_SIZE - off; + sizes[1] = size - sizes[0]; + + /* copy per-cpu buffer to object */ + addr = kmap_atomic(pages[0]); + memcpy(addr + off, buf, sizes[0]); + kunmap_atomic(addr); + addr = kmap_atomic(pages[1]); + memcpy(addr, buf + sizes[0], sizes[1]); + kunmap_atomic(addr); + + /* enable page faults to match kunmap_atomic() return conditions */ + pagefault_enable(); +} static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, void *pcpu) @@ -435,18 +486,23 @@ static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, switch (action) { case CPU_UP_PREPARE: area = &per_cpu(zs_map_area, cpu); - if (area->vm) - break; - area->vm = alloc_vm_area(2 * PAGE_SIZE, area->vm_ptes); - if (!area->vm) - return notifier_from_errno(-ENOMEM); + /* + * Make sure we don't leak memory if a cpu UP notification + * and zs_init() race and both call zs_cpu_up() on the same cpu + */ + if (area->vm_buf) + return 0; + area->vm_buf = (char *)__get_free_page(GFP_KERNEL); + if (!area->vm_buf) + return -ENOMEM; + return 0; break; case CPU_DEAD: case CPU_UP_CANCELED: area = &per_cpu(zs_map_area, cpu); - if (area->vm) - free_vm_area(area->vm); - area->vm = NULL; + if (area->vm_buf) + free_page((unsigned long)area->vm_buf); + area->vm_buf = NULL; break; } @@ -664,22 +720,11 @@ void *zs_map_object(struct zs_pool *pool, void *handle) if (off + class->size <= PAGE_SIZE) { /* this object is contained entirely within a page */ area->vm_addr = kmap_atomic(page); - } else { - /* this object spans two pages */ - struct page *nextp; - - nextp = get_next_page(page); - BUG_ON(!nextp); - - - set_pte(area->vm_ptes[0], mk_pte(page, PAGE_KERNEL)); - set_pte(area->vm_ptes[1], mk_pte(nextp, PAGE_KERNEL)); - - /* We pre-allocated VM area so mapping can never fail */ - area->vm_addr = area->vm->addr; + return area->vm_addr + off; } - return area->vm_addr + off; + zs_copy_map_object(area->vm_buf, page, off, class->size); + return area->vm_buf; } EXPORT_SYMBOL_GPL(zs_map_object); @@ -701,14 +746,10 @@ void zs_unmap_object(struct zs_pool *pool, void *handle) off = obj_idx_to_offset(page, obj_idx, class->size); area = &__get_cpu_var(zs_map_area); - if (off + class->size <= PAGE_SIZE) { + if (off + class->size <= PAGE_SIZE) kunmap_atomic(area->vm_addr); - } else { - set_pte(area->vm_ptes[0], __pte(0)); - set_pte(area->vm_ptes[1], __pte(0)); - __flush_tlb_one((unsigned long)area->vm_addr); - __flush_tlb_one((unsigned long)area->vm_addr + PAGE_SIZE); - } + else + zs_copy_unmap_object(area->vm_buf, page, off, class->size); put_cpu_var(zs_map_area); } EXPORT_SYMBOL_GPL(zs_unmap_object); diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h index 92eefc6..a6f3109 100644 --- a/drivers/staging/zsmalloc/zsmalloc_int.h +++ b/drivers/staging/zsmalloc/zsmalloc_int.h @@ -110,9 +110,8 @@ enum fullness_group { static const int fullness_threshold_frac = 4; struct mapping_area { - struct vm_struct *vm; - pte_t *vm_ptes[2]; - char *vm_addr; + char *vm_buf; /* copy buffer for objects that span pages */ + char *vm_addr; /* address of kmap_atomic()'ed pages */ }; struct size_class { -- cgit v1.1 From 06cdf4f03cc2a0beb383782320c4b02df7a0d94d Mon Sep 17 00:00:00 2001 From: Dan Magenheimer Date: Thu, 12 Jan 2012 14:03:25 -0500 Subject: mm: zcache/tmem/cleancache: s/flush/invalidate/ Complete the renaming from "flush" to "invalidate" across both tmem frontends (cleancache and frontswap) and both tmem backends (Xen and zcache), as required by akpm. This change is completely cosmetic. [v10: no change] [v9: akpm@linux-foundation.org: change "flush" to "invalidate", part 3] Signed-off-by: Dan Magenheimer Cc: Kamezawa Hiroyuki Cc: Jan Beulich Acked-by: Seth Jennings Cc: Jeremy Fitzhardinge Cc: Hugh Dickins Cc: Johannes Weiner Cc: Nitin Gupta Cc: Matthew Wilcox Cc: Chris Mason Cc: Rik Riel Cc: Andrew Morton [v11: Remove the frontswap part] Signed-off-by: Konrad Rzeszutek Wilk Conflicts: drivers/xen/tmem.c include/linux/cleancache.h Change-Id: Id9661e5fc4bb6f416129f38c1e3df80319653041 --- drivers/staging/zcache/zcache-main.c | 10 +++++----- drivers/xen/tmem.c | 6 +++--- include/linux/cleancache.h | 6 +++--- mm/cleancache.c | 7 ++++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 276940d..676e251 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -1810,9 +1810,9 @@ static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize) static struct cleancache_ops zcache_cleancache_ops = { .put_page = zcache_cleancache_put_page, .get_page = zcache_cleancache_get_page, - .flush_page = zcache_cleancache_flush_page, - .flush_inode = zcache_cleancache_flush_inode, - .flush_fs = zcache_cleancache_flush_fs, + .invalidate_page = zcache_cleancache_flush_page, + .invalidate_inode = zcache_cleancache_flush_inode, + .invalidate_fs = zcache_cleancache_flush_fs, .init_shared_fs = zcache_cleancache_init_shared_fs, .init_fs = zcache_cleancache_init_fs }; @@ -1920,8 +1920,8 @@ static void zcache_frontswap_init(unsigned ignored) static struct frontswap_ops zcache_frontswap_ops = { .put_page = zcache_frontswap_put_page, .get_page = zcache_frontswap_get_page, - .flush_page = zcache_frontswap_flush_page, - .flush_area = zcache_frontswap_flush_area, + .invalidate_page = zcache_frontswap_flush_page, + .invalidate_area = zcache_frontswap_flush_area, .init = zcache_frontswap_init }; diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c index 816a449..4cac54a 100644 --- a/drivers/xen/tmem.c +++ b/drivers/xen/tmem.c @@ -234,9 +234,9 @@ __setup("nocleancache", no_cleancache); static struct cleancache_ops tmem_cleancache_ops = { .put_page = tmem_cleancache_put_page, .get_page = tmem_cleancache_get_page, - .flush_page = tmem_cleancache_flush_page, - .flush_inode = tmem_cleancache_flush_inode, - .flush_fs = tmem_cleancache_flush_fs, + .invalidate_page = tmem_cleancache_flush_page, + .invalidate_inode = tmem_cleancache_flush_inode, + .invalidate_fs = tmem_cleancache_flush_fs, .init_shared_fs = tmem_cleancache_init_shared_fs, .init_fs = tmem_cleancache_init_fs }; diff --git a/include/linux/cleancache.h b/include/linux/cleancache.h index 04ffb2e..1ccdcea 100644 --- a/include/linux/cleancache.h +++ b/include/linux/cleancache.h @@ -28,9 +28,9 @@ struct cleancache_ops { pgoff_t, struct page *); void (*put_page)(int, struct cleancache_filekey, pgoff_t, struct page *); - void (*flush_page)(int, struct cleancache_filekey, pgoff_t); - void (*flush_inode)(int, struct cleancache_filekey); - void (*flush_fs)(int); + void (*invalidate_page)(int, struct cleancache_filekey, pgoff_t); + void (*invalidate_inode)(int, struct cleancache_filekey); + void (*invalidate_fs)(int); }; extern struct cleancache_ops diff --git a/mm/cleancache.c b/mm/cleancache.c index bcaae4c..83a8241 100644 --- a/mm/cleancache.c +++ b/mm/cleancache.c @@ -160,7 +160,8 @@ void __cleancache_flush_page(struct address_space *mapping, struct page *page) if (pool_id >= 0) { VM_BUG_ON(!PageLocked(page)); if (cleancache_get_key(mapping->host, &key) >= 0) { - (*cleancache_ops.flush_page)(pool_id, key, page->index); + (*cleancache_ops.invalidate_page)(pool_id, + key, page->index); cleancache_flushes++; } } @@ -178,7 +179,7 @@ void __cleancache_flush_inode(struct address_space *mapping) struct cleancache_filekey key = { .u.key = { 0 } }; if (pool_id >= 0 && cleancache_get_key(mapping->host, &key) >= 0) - (*cleancache_ops.flush_inode)(pool_id, key); + (*cleancache_ops.invalidate_inode)(pool_id, key); } EXPORT_SYMBOL(__cleancache_flush_inode); @@ -192,7 +193,7 @@ void __cleancache_flush_fs(struct super_block *sb) if (sb->cleancache_poolid >= 0) { int old_poolid = sb->cleancache_poolid; sb->cleancache_poolid = -1; - (*cleancache_ops.flush_fs)(old_poolid); + (*cleancache_ops.invalidate_fs)(old_poolid); } } EXPORT_SYMBOL(__cleancache_flush_fs); -- cgit v1.1 From d65ee03e9699231f40df8bd5a976f0dac16204e0 Mon Sep 17 00:00:00 2001 From: Emerson Pinter Date: Mon, 9 Sep 2013 10:58:33 -0300 Subject: staging: zcache: replace xvmalloc with zsmalloc Replaces xvmalloc with zsmalloc as the persistent memory allocator for zcache Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zcache/Kconfig Change-Id: Ib8fc7e0f88d749fc1d4e32d9d98ccfb62de6d883 --- drivers/staging/zcache/zcache-main.c | 83 +++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 676e251..1013ecc 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -9,7 +9,7 @@ * page-accessible memory [1] interfaces, both utilizing the crypto compression * API: * 1) "compression buddies" ("zbud") is used for ephemeral pages - * 2) xvmalloc is used for persistent pages. + * 2) zsmalloc is used for persistent pages. * Xvmalloc (based on the TLSF allocator) has very low fragmentation * so maximizes space efficiency, while zbud allows pairs (and potentially, * in the future, more than a pair of) compressed pages to be closely linked @@ -33,7 +33,7 @@ #include #include "tmem.h" -#include "../zram/xvmalloc.h" /* if built in drivers/staging */ +#include "../zsmalloc/zsmalloc.h" #if (!defined(CONFIG_CLEANCACHE) && !defined(CONFIG_FRONTSWAP)) #error "zcache is useless without CONFIG_CLEANCACHE or CONFIG_FRONTSWAP" @@ -62,7 +62,7 @@ MODULE_LICENSE("GPL"); struct zcache_client { struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT]; - struct xv_pool *xvpool; + struct zs_pool *zspool; bool allocated; atomic_t refcount; }; @@ -657,7 +657,7 @@ static int zbud_show_cumul_chunk_counts(char *buf) #endif /********** - * This "zv" PAM implementation combines the TLSF-based xvMalloc + * This "zv" PAM implementation combines the slab-based zsmalloc * with the crypto compression API to maximize the amount of data that can * be packed into a physical page. * @@ -671,6 +671,7 @@ struct zv_hdr { uint32_t pool_id; struct tmem_oid oid; uint32_t index; + size_t size; DECL_SENTINEL }; @@ -692,71 +693,73 @@ static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5; static atomic_t zv_curr_dist_counts[NCHUNKS]; static atomic_t zv_cumul_dist_counts[NCHUNKS]; -static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, +static struct zv_hdr *zv_create(struct zs_pool *pool, uint32_t pool_id, struct tmem_oid *oid, uint32_t index, void *cdata, unsigned clen) { - struct page *page; - struct zv_hdr *zv = NULL; - uint32_t offset; - int alloc_size = clen + sizeof(struct zv_hdr); - int chunks = (alloc_size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; - int ret; + struct zv_hdr *zv; + u32 size = clen + sizeof(struct zv_hdr); + int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; + void *handle = NULL; + char *buf; BUG_ON(!irqs_disabled()); BUG_ON(chunks >= NCHUNKS); - ret = xv_malloc(xvpool, alloc_size, - &page, &offset, ZCACHE_GFP_MASK); - if (unlikely(ret)) + handle = zs_malloc(pool, size); + if (!handle) goto out; atomic_inc(&zv_curr_dist_counts[chunks]); atomic_inc(&zv_cumul_dist_counts[chunks]); - zv = kmap_atomic(page, KM_USER0) + offset; + zv = (struct zv_hdr *)((char *)cdata - sizeof(*zv)); zv->index = index; zv->oid = *oid; zv->pool_id = pool_id; + zv->size = clen; SET_SENTINEL(zv, ZVH); - memcpy((char *)zv + sizeof(struct zv_hdr), cdata, clen); - kunmap_atomic(zv, KM_USER0); + buf = zs_map_object(pool, handle); + memcpy(buf, zv, clen + sizeof(*zv)); + zs_unmap_object(pool, handle); out: - return zv; + return handle; } -static void zv_free(struct xv_pool *xvpool, struct zv_hdr *zv) +static void zv_free(struct zs_pool *pool, void *handle) { unsigned long flags; - struct page *page; - uint32_t offset; - uint16_t size = xv_get_object_size(zv); - int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; + struct zv_hdr *zv; + uint16_t size; + int chunks; + zv = zs_map_object(pool, handle); ASSERT_SENTINEL(zv, ZVH); + size = zv->size + sizeof(struct zv_hdr); + INVERT_SENTINEL(zv, ZVH); + zs_unmap_object(pool, handle); + + chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; BUG_ON(chunks >= NCHUNKS); atomic_dec(&zv_curr_dist_counts[chunks]); - size -= sizeof(*zv); - BUG_ON(size == 0); - INVERT_SENTINEL(zv, ZVH); - page = virt_to_page(zv); - offset = (unsigned long)zv & ~PAGE_MASK; + local_irq_save(flags); - xv_free(xvpool, page, offset); + zs_free(pool, handle); local_irq_restore(flags); } -static void zv_decompress(struct page *page, struct zv_hdr *zv) +static void zv_decompress(struct page *page, void *handle) { unsigned int clen = PAGE_SIZE; char *to_va; - unsigned size; int ret; + struct zv_hdr *zv; + zv = zs_map_object(zcache_host.zspool, handle); + BUG_ON(zv->size == 0); ASSERT_SENTINEL(zv, ZVH); - size = xv_get_object_size(zv) - sizeof(*zv); - BUG_ON(size == 0); to_va = kmap_atomic(page, KM_USER0); ret = zcache_comp_op(ZCACHE_COMPOP_DECOMPRESS, (char *)zv + sizeof(*zv), - size, to_va, &clen); + zv->size, to_va, &clen); kunmap_atomic(to_va, KM_USER0); + zs_unmap_object(zcache_host.zspool, handle); BUG_ON(ret); BUG_ON(clen != PAGE_SIZE); } @@ -983,8 +986,8 @@ int zcache_new_client(uint16_t cli_id) goto out; cli->allocated = 1; #ifdef CONFIG_FRONTSWAP - cli->xvpool = xv_create_pool(); - if (cli->xvpool == NULL) + cli->zspool = zs_create_pool("zcache", ZCACHE_GFP_MASK); + if (cli->zspool == NULL) goto out; #endif ret = 0; @@ -1215,7 +1218,7 @@ static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph, } /* reject if mean compression is too poor */ if ((clen > zv_max_mean_zsize) && (curr_pers_pampd_count > 0)) { - total_zsize = xv_get_total_size_bytes(cli->xvpool); + total_zsize = zs_get_total_size_bytes(cli->zspool); zv_mean_zsize = div_u64(total_zsize, curr_pers_pampd_count); if (zv_mean_zsize > zv_max_mean_zsize) { @@ -1223,7 +1226,7 @@ static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph, goto out; } } - pampd = (void *)zv_create(cli->xvpool, pool->pool_id, + pampd = (void *)zv_create(cli->zspool, pool->pool_id, oid, index, cdata, clen); if (pampd == NULL) goto out; @@ -1281,7 +1284,7 @@ static void zcache_pampd_free(void *pampd, struct tmem_pool *pool, atomic_dec(&zcache_curr_eph_pampd_count); BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0); } else { - zv_free(cli->xvpool, (struct zv_hdr *)pampd); + zv_free(cli->zspool, pampd); atomic_dec(&zcache_curr_pers_pampd_count); BUG_ON(atomic_read(&zcache_curr_pers_pampd_count) < 0); } @@ -2071,7 +2074,7 @@ static int __init zcache_init(void) old_ops = zcache_frontswap_register_ops(); pr_info("zcache: frontswap enabled using kernel " - "transcendent memory and xvmalloc\n"); + "transcendent memory and zsmalloc\n"); if (old_ops.init != NULL) pr_warning("zcache: frontswap_ops overridden"); } -- cgit v1.1 From 454f258d0665aa882dd7eae0f33b5c9fff625d1c Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 28 Feb 2012 16:01:37 -0600 Subject: staging: zcache: fix length type mismatch This fixes a type mismatch in the compression code where a size_t pointer was cast to a unsigned int pointer. On little endian archs, there is no issue. However on big endian archs, the value is incorrect, taking the high order bits and truncating the lower order bits. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zcache/zcache-main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 1013ecc..7915ecb 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -1170,14 +1170,14 @@ static atomic_t zcache_curr_pers_pampd_count = ATOMIC_INIT(0); static unsigned long zcache_curr_pers_pampd_count_max; /* forward reference */ -static int zcache_compress(struct page *from, void **out_va, size_t *out_len); +static int zcache_compress(struct page *from, void **out_va, unsigned *out_len); static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph, struct tmem_pool *pool, struct tmem_oid *oid, uint32_t index) { void *pampd = NULL, *cdata; - size_t clen; + unsigned clen; int ret; unsigned long count; struct page *page = (struct page *)(data); @@ -1326,7 +1326,7 @@ static struct tmem_pamops zcache_pamops = { static DEFINE_PER_CPU(unsigned char *, zcache_dstmem); #define ZCACHE_DSTMEM_ORDER 1 -static int zcache_compress(struct page *from, void **out_va, size_t *out_len) +static int zcache_compress(struct page *from, void **out_va, unsigned *out_len) { int ret = 0; unsigned char *dmem = __get_cpu_var(zcache_dstmem); @@ -1339,7 +1339,7 @@ static int zcache_compress(struct page *from, void **out_va, size_t *out_len) from_va = kmap_atomic(from, KM_USER0); mb(); ret = zcache_comp_op(ZCACHE_COMPOP_COMPRESS, from_va, PAGE_SIZE, dmem, - (unsigned int *)out_len); + out_len); BUG_ON(ret); *out_va = dmem; kunmap_atomic(from_va, KM_USER0); -- cgit v1.1 From 9adc7eaacb36f7d89051e604bd2f7ea6d38bf288 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 28 Feb 2012 16:02:23 -0600 Subject: staging: zcache: fix memory corruption bug This patch fixes a bug where the zv code writes before the allocated buffer, resulting in system memory corruption. This was introduced during the switch from xvmalloc to zsmalloc. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zcache/zcache-main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 7915ecb..b698464 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -701,7 +701,6 @@ static struct zv_hdr *zv_create(struct zs_pool *pool, uint32_t pool_id, u32 size = clen + sizeof(struct zv_hdr); int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; void *handle = NULL; - char *buf; BUG_ON(!irqs_disabled()); BUG_ON(chunks >= NCHUNKS); @@ -710,14 +709,13 @@ static struct zv_hdr *zv_create(struct zs_pool *pool, uint32_t pool_id, goto out; atomic_inc(&zv_curr_dist_counts[chunks]); atomic_inc(&zv_cumul_dist_counts[chunks]); - zv = (struct zv_hdr *)((char *)cdata - sizeof(*zv)); + zv = zs_map_object(pool, handle); zv->index = index; zv->oid = *oid; zv->pool_id = pool_id; zv->size = clen; SET_SENTINEL(zv, ZVH); - buf = zs_map_object(pool, handle); - memcpy(buf, zv, clen + sizeof(*zv)); + memcpy((char *)zv + sizeof(struct zv_hdr), cdata, clen); zs_unmap_object(pool, handle); out: return handle; -- cgit v1.1 From c145ef3a9e6b841ad573d0f8186c4dba9e3e97e0 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 16 Mar 2012 12:00:57 -0700 Subject: staging/zmem: Use lockdep_assert_held instead of spin_is_locked WARN_ON(!spin_is_locked()) will always trigger on UP. Use lockdep_assert_held instead. Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zcache/tmem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zcache/tmem.h b/drivers/staging/zcache/tmem.h index ed147c4..0d4aa82 100644 --- a/drivers/staging/zcache/tmem.h +++ b/drivers/staging/zcache/tmem.h @@ -47,7 +47,7 @@ #define ASSERT_INVERTED_SENTINEL(_x, _y) do { } while (0) #endif -#define ASSERT_SPINLOCK(_l) WARN_ON(!spin_is_locked(_l)) +#define ASSERT_SPINLOCK(_l) lockdep_assert_held(_l) /* * A pool is the highest-level data structure managed by tmem and -- cgit v1.1 From d25b478f24b26f313e150daaa7c452f5a0b91792 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 23 Apr 2012 20:33:50 -0500 Subject: staging: zcache: fix Kconfig crypto dependency ZCACHE is a boolean in the Kconfig. When selected, it should require that CRYPTO be builtin (=y). Currently, ZCACHE=y and CRYPTO=m is a valid configuration when it should not be. This patch changes the zcache Kconfig to enforce this dependency. Signed-off-by: Seth Jennings Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zcache/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 5a36fcf..575d4bf 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -2,7 +2,7 @@ config ZCACHE tristate "Dynamic compression of swap pages and clean pagecache pages" # X86 dependency is because zsmalloc uses non-portable pte/tlb # functions - depends on (CLEANCACHE || FRONTSWAP) && CRYPTO && X86 + depends on (CLEANCACHE || FRONTSWAP) && CRYPTO=y && X86 select ZSMALLOC select CRYPTO_LZO default n -- cgit v1.1 From 125790986c7fef051c65aff2ef71c5ea727073ab Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 25 Jun 2012 11:14:36 -0500 Subject: CHROMIUM: UPSTREAM: staging: zram/zcache: swtich Kconfig dependency from X86 to ZSMALLOC upstream: commit 6e2361720b9da9ec830d407da058ca1827e62b12 This patch switches zcache and zram dependency to ZSMALLOC rather than X86. There is no net change since ZSMALLOC depends on X86, however, this prevent further changes to these files as zsmalloc dependencies change. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman BUG=chromium-os:36829 TEST=compile, verify that zram module is updated and /boot/config is correct Change-Id: I91f6ed46549fe62a9fe3035063d98beb2f5d966a Signed-off-by: Luigi Semenzato Reviewed-on: https://gerrit.chromium.org/gerrit/39289 Tested-by: Luigi Semenzato Reviewed-by: Sonny Rao Commit-Ready: Luigi Semenzato Conflicts: drivers/staging/zcache/Kconfig --- drivers/staging/zcache/Kconfig | 7 ++----- drivers/staging/zram/Kconfig | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/staging/zcache/Kconfig b/drivers/staging/zcache/Kconfig index 575d4bf..4881839 100644 --- a/drivers/staging/zcache/Kconfig +++ b/drivers/staging/zcache/Kconfig @@ -1,9 +1,6 @@ config ZCACHE - tristate "Dynamic compression of swap pages and clean pagecache pages" - # X86 dependency is because zsmalloc uses non-portable pte/tlb - # functions - depends on (CLEANCACHE || FRONTSWAP) && CRYPTO=y && X86 - select ZSMALLOC + bool "Dynamic compression of swap pages and clean pagecache pages" + depends on (CLEANCACHE || FRONTSWAP) && CRYPTO=y && ZSMALLOC=y select CRYPTO_LZO default n help diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index 9d11a4c..be5abe8 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -1,9 +1,6 @@ config ZRAM tristate "Compressed RAM block device support" - # X86 dependency is because zsmalloc uses non-portable pte/tlb - # functions - depends on BLOCK && SYSFS && X86 - select ZSMALLOC + depends on BLOCK && SYSFS && ZSMALLOC select LZO_COMPRESS select LZO_DECOMPRESS default n -- cgit v1.1 From 2d2bf2aa3ee28795f6a3bc69b1de58e777e22f07 Mon Sep 17 00:00:00 2001 From: Larry Bassel Date: Fri, 11 Oct 2013 15:36:02 -0700 Subject: zram: use 3.10 version of zram commit 25eeb667599b192ea850a062d69383ee864c06ab Author: Wanpeng Li Date: Wed Mar 13 15:06:16 2013 +0800 zram: fix zram_bvec_read duplicate dump failure message and stat accumulation When zram decompress fails, the code unnecessarily dumps failure messages and does stat accumulation in function zram_decompress_page(), this work is already done in function zram_decompress_page, the patch skips the redundant work. Signed-off-by: Wanpeng Li Signed-off-by: Greg Kroah-Hartman commit 78110bb8dc4a7ff331bfa3cfe7d4e287cfb3f22b Author: Joe Perches Date: Mon Feb 11 09:41:29 2013 -0800 staging: Remove unnecessary OOM messages alloc failures already get standardized OOM messages and a dump_stack. For the affected mallocs around these OOM messages: Converted kzallocs with multiplies to kcalloc. Converted kmallocs with multiplies to kmalloc_array. Converted a kmalloc/strlen/strncpy to kstrdup. Moved a spin_lock below a removed OOM message and removed a now unnecessary spin_unlock. Neatened alignment and whitespace. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman commit 1e927711c5ecabe76010ed9249f8f3747829f04f Author: Fengguang Wu Date: Fri Feb 8 10:15:10 2013 +0800 staging: zram: __zram_reset_device() can be static Signed-off-by: Fengguang Wu Cc: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 8b3cc3edb745cbc131560d19e3f32d81c07454b1 Author: Minchan Kim Date: Wed Feb 6 08:48:53 2013 +0900 zram: get rid of lockdep warning Lockdep complains about recursive deadlock of zram->init_lock. [1] made it false positive because we can't request IO to zram before setting disksize. Anyway, we should shut lockdep up to avoid many reporting from user. [1] : zram: force disksize setting before using zram Acked-by: Jerome Marchand Acked-by: Nitin Gupta Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 152bce6bdd6e6866ff83166ac75177d001c4360d Author: Minchan Kim Date: Wed Feb 6 08:45:22 2013 +0900 zram: fix warning of print format kbuild bot whinges due to print format mistmatch caused by zram: force disksize setting before using zram. This patch fixes it. Reported-by: Wu Fengguang Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 3de738cd30306f754ea35d35b5dad29fdbec84c9 Author: Minchan Kim Date: Wed Jan 30 11:41:41 2013 +0900 zram: give up lazy initialization of zram metadata 1) User of zram normally do mkfs.xxx or mkswap before using the zram block device(ex, normally, do it at booting time) It ends up allocating such metadata of zram before real usage so benefit of lazy initialzation would be mitigated. 2) Some user want to use zram when memory pressure is high.(ie, load zram dynamically, NOT booting time). It does make sense because people don't want to waste memory until memory pressure is high(ie, where zram is really helpful time). In this case, lazy initialzation could be failed easily because we will use GFP_NOIO instead of GFP_KERNEL for avoiding deadlock. So the benefit of lazy initialzation would be mitigated, too. 3) Metadata overhead is not critical and Nitin has a plan to diet it. 4K : 12 byte(64bit machine) -> 64G : 192M so 0.3% isn't big overhead If insane user use such big zram device up to 20, it could consume 6% of ram but efficieny of zram will cover the waste. So this patch gives up lazy initialization and instead we initialize metadata at disksize setting time. Acked-by: Jerome Marchand Acked-by: Nitin Gupta Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 0231c403bb065307493fe997ad170487b4d55eb8 Author: Minchan Kim Date: Wed Jan 30 11:41:40 2013 +0900 zram: force disksize setting before using zram Now zram document syas "set disksize is optional" but partly it's wrong. When you try to use zram firstly after booting, you must set disksize, otherwise zram can't work because zram gendisk's size is 0. But once you do it, you can use zram freely after reset because reset doesn't reset to zero paradoxically. So in this time, disksize setting is optional.:( It's inconsitent for user behavior and not straightforward. This patch forces always setting disksize firstly before using zram. Yes. It changes current behavior so someone could complain when he upgrades zram. Apparently it could be a problem if zram is mainline but it still lives in staging so behavior could be changed for right way to go. Let them excuse. Acked-by: Jerome Marchand Acked-by: Nitin Gupta Acked-by: Dan Magenheimer Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 7e5a5104c6af709a8d97d5f4711e7c917761d464 Author: Minchan Kim Date: Wed Jan 30 11:41:39 2013 +0900 zram: Fix deadlock bug in partial read/write Now zram allocates new page with GFP_KERNEL in zram I/O path if IO is partial. Unfortunately, It may cause deadlock with reclaim path like below. write_page from fs fs_lock allocation(GFP_KERNEL) reclaim pageout write_page from fs fs_lock <-- deadlock This patch fixes it by using GFP_NOIO. In read path, we reorganize code flow so that kmap_atomic is called after the GFP_NOIO allocation. Cc: stable@vger.kernel.org Acked-by: Jerome Marchand Acked-by: Nitin Gupta [ penberg@kernel.org: don't use GFP_ATOMIC ] Signed-off-by: Pekka Enberg Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 0d145a501778042d0411c843ed5b468b41f8a171 Author: Seth Jennings Date: Wed Jan 30 09:36:52 2013 -0600 staging: zsmalloc: remove unused pool name zs_create_pool() currently takes a name argument which is never used in any useful way. This patch removes it. Signed-off-by: Seth Jennings Acked-by: Nitin Gupta Acked-by: Rik van Riel Signed-off-by: Greg Kroah-Hartman commit 8f5f90a872c38b4e78f3cc95e8a25434b98e4db2 Merge: c0cd2da 949db15 Author: Greg Kroah-Hartman Date: Fri Jan 25 21:25:02 2013 -0800 Merge 3.8-rc5 into staging-next This resolves a merge issue with a iio driver, and the zram code. Signed-off-by: Greg Kroah-Hartman commit d178a07c4bd380492b6aca9e5d3985c19ca88fdb Author: Davidlohr Bueso Date: Tue Jan 1 21:24:29 2013 -0800 staging: zram: drop zram_stat_dec/inc functions It seems like an overkill to have adding and subtracting 1 functions from the 32bit counters. Just do it directly. Signed-off-by: Davidlohr Bueso Signed-off-by: Greg Kroah-Hartman commit cad683fb9d53fa2cbcf82660e663fa1180f86797 Author: Davidlohr Bueso Date: Tue Jan 1 21:24:22 2013 -0800 staging: zram: show correct disksize The ->disksize variable stores values in units of bytes, print the correct size in Kb Signed-off-by: Davidlohr Bueso Signed-off-by: Greg Kroah-Hartman commit ca3d70bd68455133eabcb8a0ae1b40254d87188b Author: Davidlohr Bueso Date: Tue Jan 1 21:24:13 2013 -0800 staging: zram: simplify num_devices paramater Simplify dealing with num_devices when initializing zram. Also cleanup some of the output messages. Signed-off-by: Davidlohr Bueso Signed-off-by: Greg Kroah-Hartman commit 397c60668aa5ae7130b5ad4e73870d7b8a787085 Author: Nitin Gupta Date: Wed Jan 2 08:53:41 2013 -0800 staging: zram: fix invalid memory references during disk write Fixes a bug introduced by commit c8f2f0db1 ("zram: Fix handling of incompressible pages") which caused invalid memory references during disk write. Invalid references could occur in two cases: - Incoming data expands on compression: In this case, reference was made to kunmap()'ed bio page. - Partial (non PAGE_SIZE) write with incompressible data: In this case, reference was made to a kfree()'ed buffer. Fixes bug 50081: https://bugzilla.kernel.org/show_bug.cgi?id=50081 Signed-off-by: Nitin Gupta Cc: stable Reported-by: Mihail Kasadjikov Reported-by: Tomas M Reviewed-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit dbc320f7490933eb6a073d82aceac75d5e7ce82b Author: Masanari Iida Date: Mon Jan 7 23:28:10 2013 +0900 staging: Add angle bracket before and after the URL Add missing angle bracket before and after the URL. Signed-off-by: Masanari Iida Signed-off-by: Greg Kroah-Hartman commit 26907840e63b3187266db1865e76ea9e98b01b19 Author: Sergey Senozhatsky Date: Tue Oct 30 22:42:31 2012 +0300 staging: zram: handle mem suffixes in disk size zram_sysfs parameter Use memparse() to allow mem suffixes in disksize sysfs number. Examples: echo 256K > /sys/block/zram0/disksize echo 512M > /sys/block/zram0/disksize echo 1G > /sys/block/zram0/disksize Signed-off-by: Sergey Senozhatsky Reviewed-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit 37b51fdddf64e7ba0971d070428655f8d6f36578 Author: Sergey Senozhatsky Date: Tue Oct 30 22:40:23 2012 +0300 staging: zram: factor-out zram_decompress_page() function zram_bvec_read() shared decompress functionality with zram_read_before_write() function. Factor-out and make commonly used zram_decompress_page() function, which also simplified error handling in zram_bvec_read(). Signed-off-by: Sergey Senozhatsky Reviewed-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit cb1f6268acd7f1bca7153fa9ca187ffb73f60ab8 Merge: d9ff393 8f0d816 Author: Greg Kroah-Hartman Date: Mon Oct 29 08:37:12 2012 -0700 Merge 3.7-rc3 into staging-next This resolves the conflict with: drivers/staging/comedi/drivers/amplc_dio200.c and syncs up the changes that happened in the staging directory for 3.7-rc3. Signed-off-by: Greg Kroah-Hartman commit c8f2f0db1d0294aaf37e8a85bea9bbc4aaf5c0fe Author: Nitin Gupta Date: Wed Oct 10 17:42:18 2012 -0700 staging: zram: Fix handling of incompressible pages Change 130f315a (staging: zram: remove special handle of uncompressed page) introduced a bug in the handling of incompressible pages which resulted in memory allocation failure for such pages. When a page expands on compression, say from 4K to 4K+30, we were trying to do zsmalloc(pool, 4K+30). However, the maximum size which zsmalloc can allocate is PAGE_SIZE (for obvious reasons), so such allocation requests always return failure (0). For a page that has compressed size larger than the original size (this may happen with already compressed or random data), there is no point storing the compressed version as that would take more space and would also require time for decompression when needed again. So, the fix is to store any page, whose compressed size exceeds a threshold (max_zpage_size), as-it-is i.e. without compression. Memory required for storing this uncompressed page can then be requested from zsmalloc which supports PAGE_SIZE sized allocations. Lastly, the fix checks that we do not attempt to "decompress" the page which we stored in the uncompressed form -- we just memcpy() out such pages. Signed-off-by: Nitin Gupta Reported-by: viechweg@gmail.com Reported-by: paerley@gmail.com Reported-by: wu.tommy@gmail.com Acked-by: Minchan Kim Cc: stable Signed-off-by: Greg Kroah-Hartman commit 55dcbbb1bf7eef83bcd3e0ba8de0b359a45804ed Author: Minchan Kim Date: Wed Oct 10 08:49:52 2012 +0900 staging: zram: correct obsolete comment on max_zpage_size Zram doesn't use xv_malloc any more so it doesn't have limitation about zobj_header. Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit b74185108668ef966e663878adbad65e03bfcb43 Author: Seth Jennings Date: Mon Jul 2 16:15:52 2012 -0500 staging: zsmalloc: add mapping modes This patch improves mapping performance in zsmalloc by getting usage information from the user in the form of a "mapping mode" and using it to avoid unnecessary copying for objects that span pages. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman commit 6e2361720b9da9ec830d407da058ca1827e62b12 Author: Seth Jennings Date: Mon Jun 25 11:14:36 2012 -0500 staging: zram/zcache: swtich Kconfig dependency from X86 to ZSMALLOC This patch switches zcache and zram dependency to ZSMALLOC rather than X86. There is no net change since ZSMALLOC depends on X86, however, this prevent further changes to these files as zsmalloc dependencies change. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman commit 80677c2538283678c7b2ef7ebbd653a3fa54d086 Author: Sam Hansen Date: Thu Jun 7 16:03:48 2012 -0700 staging: zram: conventions, __aligned() attribute Using the __aligned() attribute in favor of __attribute__((aligned(size))) Signed-off-by: Sam Hansen Signed-off-by: Greg Kroah-Hartman commit 94b8435ff4d46dde75173da45564b0d65889dc64 Author: Sam Hansen Date: Thu Jun 7 16:03:47 2012 -0700 staging: zram: conventions pr_warning -> pr_warn() Porting zram to use the pr_warn() function instead of the deprecated pr_warning(). Signed-off-by: Sam Hansen Signed-off-by: Greg Kroah-Hartman commit 130f315a174d127cbb90d4d1a4a7088dbcf930b5 Author: Minchan Kim Date: Fri Jun 8 15:39:27 2012 +0900 staging: zram: remove special handle of uncompressed page xvmalloc can't handle PAGE_SIZE page so that zram have to handle it specially but zsmalloc can do it so let's remove unnecessary special handling code. Quote from Nitin "I think page vs handle distinction was added since xvmalloc could not handle full page allocation. Now that zsmalloc allows full page allocation, we can just use it for both cases. This would also allow removing the ZRAM_UNCOMPRESSED flag. The only downside will be slightly slower code path for full page allocation but this event is anyways supposed to be rare, so should be fine." 1. This patch reduces code very much. drivers/staging/zram/zram_drv.c | 104 +++++-------------------------------- drivers/staging/zram/zram_drv.h | 17 +----- drivers/staging/zram/zram_sysfs.c | 6 +-- 3 files changed, 15 insertions(+), 112 deletions(-) 2. change pages_expand with bad_compress so it can count bad compression(above 75%) ratio. 3. remove zobj_header which is for back-reference for defragmentation because firstly, it's not used at the moment and zsmalloc can't handle bigger size than PAGE_SIZE so zram can't do it any more without redesign. Cc: Seth Jennings Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit 374a69191d12a0525b7ffe1197abc30c4795a230 Author: Minchan Kim Date: Fri Jun 8 15:39:26 2012 +0900 staging: zram: fix random data read fd1a30de makes a bug that it uses (struct page *) as zsmalloc's handle although it's a uncompressed page so that it can access random page, return random data or even crashed by get_first_page in zs_map_object. Cc: Seth Jennings Cc: Jerome Marchand Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit c234434835b1f4bad9bdbae6710044cba387c9e5 Author: Minchan Kim Date: Fri Jun 8 15:39:25 2012 +0900 staging: zsmalloc: zsmalloc: use unsigned long instead of void * We should use unsigned long as handle instead of void * to avoid any confusion. Without this, users may just treat zs_malloc return value as a pointer and try to deference it. This patch passed compile test(zram, zcache and ramster) and zram is tested on qemu. changelog * from v2 - remove hval pointed out by Nitin - based on next-20120607 * from v1 - change zcache's zv_create return value - baesd on next-20120604 Cc: Dan Magenheimer Acked-by: Seth Jennings Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman Change-Id: I5157794e120bbae7ba1e5d0f122dae4fba4a079b Signed-off-by: Larry Bassel --- drivers/staging/zram/Kconfig | 2 +- drivers/staging/zram/zram.txt | 27 +- drivers/staging/zram/zram_drv.c | 514 +++++++++++++++----------------------- drivers/staging/zram/zram_drv.h | 46 ++-- drivers/staging/zram/zram_sysfs.c | 28 +-- 5 files changed, 245 insertions(+), 372 deletions(-) diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index be5abe8..983314c 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -14,7 +14,7 @@ config ZRAM disks and maybe many more. See zram.txt for more information. - Project home: http://compcache.googlecode.com/ + Project home: config ZRAM_DEBUG bool "Compressed RAM block device debug support" diff --git a/drivers/staging/zram/zram.txt b/drivers/staging/zram/zram.txt index 5f75d29..765d790 100644 --- a/drivers/staging/zram/zram.txt +++ b/drivers/staging/zram/zram.txt @@ -23,17 +23,17 @@ Following shows a typical sequence of steps for using zram. This creates 4 devices: /dev/zram{0,1,2,3} (num_devices parameter is optional. Default: 1) -2) Set Disksize (Optional): - Set disk size by writing the value to sysfs node 'disksize' - (in bytes). If disksize is not given, default value of 25% - of RAM is used. - - # Initialize /dev/zram0 with 50MB disksize - echo $((50*1024*1024)) > /sys/block/zram0/disksize - - NOTE: disksize cannot be changed if the disk contains any - data. So, for such a disk, you need to issue 'reset' (see below) - before you can change its disksize. +2) Set Disksize + Set disk size by writing the value to sysfs node 'disksize'. + The value can be either in bytes or you can use mem suffixes. + Examples: + # Initialize /dev/zram0 with 50MB disksize + echo $((50*1024*1024)) > /sys/block/zram0/disksize + + # Using mem suffixes + echo 256K > /sys/block/zram0/disksize + echo 512M > /sys/block/zram0/disksize + echo 1G > /sys/block/zram0/disksize 3) Activate: mkswap /dev/zram0 @@ -65,8 +65,9 @@ Following shows a typical sequence of steps for using zram. echo 1 > /sys/block/zram0/reset echo 1 > /sys/block/zram1/reset - (This frees all the memory allocated for the given device). - + This frees all the memory allocated for the given device and + resets the disksize to zero. You must set the disksize again + before reusing the device. Please report any problems at: - Mailing list: linux-mm-cc at laptop dot org diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index df26f6c..e34e3fe 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -40,17 +40,7 @@ static int zram_major; struct zram *zram_devices; /* Module params (documentation at end) */ -static unsigned int num_devices; - -static void zram_stat_inc(u32 *v) -{ - *v = *v + 1; -} - -static void zram_stat_dec(u32 *v) -{ - *v = *v - 1; -} +static unsigned int num_devices = 1; static void zram_stat64_add(struct zram *zram, u64 *v, u64 inc) { @@ -71,22 +61,22 @@ static void zram_stat64_inc(struct zram *zram, u64 *v) zram_stat64_add(zram, v, 1); } -static int zram_test_flag(struct zram *zram, u32 index, +static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - return zram->table[index].flags & BIT(flag); + return meta->table[index].flags & BIT(flag); } -static void zram_set_flag(struct zram *zram, u32 index, +static void zram_set_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - zram->table[index].flags |= BIT(flag); + meta->table[index].flags |= BIT(flag); } -static void zram_clear_flag(struct zram *zram, u32 index, +static void zram_clear_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - zram->table[index].flags &= ~BIT(flag); + meta->table[index].flags &= ~BIT(flag); } static int page_zero_filled(void *ptr) @@ -104,70 +94,38 @@ static int page_zero_filled(void *ptr) return 1; } -static void zram_set_disksize(struct zram *zram, size_t totalram_bytes) -{ - if (!zram->disksize) { - pr_info( - "disk size not provided. You can use disksize_kb module " - "param to specify size.\nUsing default: (%u%% of RAM).\n", - default_disksize_perc_ram - ); - zram->disksize = default_disksize_perc_ram * - (totalram_bytes / 100); - } - - if (zram->disksize > 2 * (totalram_bytes)) { - pr_info( - "There is little point creating a zram of greater than " - "twice the size of memory since we expect a 2:1 compression " - "ratio. Note that zram uses about 0.1%% of the size of " - "the disk when not in use so a huge zram is " - "wasteful.\n" - "\tMemory Size: %zu kB\n" - "\tSize you selected: %llu kB\n" - "Continuing anyway ...\n", - totalram_bytes >> 10, zram->disksize - ); - } - - zram->disksize &= PAGE_MASK; -} - static void zram_free_page(struct zram *zram, size_t index) { - void *handle = zram->table[index].handle; + struct zram_meta *meta = zram->meta; + unsigned long handle = meta->table[index].handle; + u16 size = meta->table[index].size; if (unlikely(!handle)) { /* * No memory is allocated for zero filled pages. * Simply clear zero page flag. */ - if (zram_test_flag(zram, index, ZRAM_ZERO)) { - zram_clear_flag(zram, index, ZRAM_ZERO); - zram_stat_dec(&zram->stats.pages_zero); + if (zram_test_flag(meta, index, ZRAM_ZERO)) { + zram_clear_flag(meta, index, ZRAM_ZERO); + zram->stats.pages_zero--; } return; } - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - __free_page(handle); - zram_clear_flag(zram, index, ZRAM_UNCOMPRESSED); - zram_stat_dec(&zram->stats.pages_expand); - goto out; - } + if (unlikely(size > max_zpage_size)) + zram->stats.bad_compress--; - zs_free(zram->mem_pool, handle); + zs_free(meta->mem_pool, handle); - if (zram->table[index].size <= PAGE_SIZE / 2) - zram_stat_dec(&zram->stats.good_compress); + if (size <= PAGE_SIZE / 2) + zram->stats.good_compress--; -out: zram_stat64_sub(zram, &zram->stats.compr_size, - zram->table[index].size); - zram_stat_dec(&zram->stats.pages_stored); + meta->table[index].size); + zram->stats.pages_stored--; - zram->table[index].handle = NULL; - zram->table[index].size = 0; + meta->table[index].handle = 0; + meta->table[index].size = 0; } static void handle_zero_page(struct bio_vec *bvec) @@ -175,25 +133,9 @@ static void handle_zero_page(struct bio_vec *bvec) struct page *page = bvec->bv_page; void *user_mem; - user_mem = kmap_atomic(page, KM_USER0); + user_mem = kmap_atomic(page); memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); - kunmap_atomic(user_mem, KM_USER0); - - flush_dcache_page(page); -} - -static void handle_uncompressed_page(struct zram *zram, struct bio_vec *bvec, - u32 index, int offset) -{ - struct page *page = bvec->bv_page; - unsigned char *user_mem, *cmem; - - user_mem = kmap_atomic(page, KM_USER0); - cmem = kmap_atomic(zram->table[index].handle, KM_USER1); - - memcpy(user_mem + bvec->bv_offset, cmem + offset, bvec->bv_len); - kunmap_atomic(user_mem, KM_USER0); - kunmap_atomic(cmem, KM_USER1); + kunmap_atomic(user_mem); flush_dcache_page(page); } @@ -203,64 +145,26 @@ static inline int is_partial_io(struct bio_vec *bvec) return bvec->bv_len != PAGE_SIZE; } -static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, - u32 index, int offset, struct bio *bio) +static int zram_decompress_page(struct zram *zram, char *mem, u32 index) { - int ret; - size_t clen; - struct page *page; - struct zobj_header *zheader; - unsigned char *user_mem, *cmem, *uncmem = NULL; - - page = bvec->bv_page; - - if (zram_test_flag(zram, index, ZRAM_ZERO)) { - handle_zero_page(bvec); - return 0; - } - - /* Requested page is not present in compressed area */ - if (unlikely(!zram->table[index].handle)) { - pr_debug("Read before write: sector=%lu, size=%u", - (ulong)(bio->bi_sector), bio->bi_size); - handle_zero_page(bvec); - return 0; - } + int ret = LZO_E_OK; + size_t clen = PAGE_SIZE; + unsigned char *cmem; + struct zram_meta *meta = zram->meta; + unsigned long handle = meta->table[index].handle; - /* Page is stored uncompressed since it's incompressible */ - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - handle_uncompressed_page(zram, bvec, index, offset); + if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { + memset(mem, 0, PAGE_SIZE); return 0; } - if (is_partial_io(bvec)) { - /* Use a temporary buffer to decompress the page */ - uncmem = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!uncmem) { - pr_info("Error allocating temp memory!\n"); - return -ENOMEM; - } - } - - user_mem = kmap_atomic(page, KM_USER0); - if (!is_partial_io(bvec)) - uncmem = user_mem; - clen = PAGE_SIZE; - - cmem = zs_map_object(zram->mem_pool, zram->table[index].handle); - - ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), - zram->table[index].size, - uncmem, &clen); - - if (is_partial_io(bvec)) { - memcpy(user_mem + bvec->bv_offset, uncmem + offset, - bvec->bv_len); - kfree(uncmem); - } - - zs_unmap_object(zram->mem_pool, zram->table[index].handle); - kunmap_atomic(user_mem, KM_USER0); + cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); + if (meta->table[index].size == PAGE_SIZE) + memcpy(mem, cmem, PAGE_SIZE); + else + ret = lzo1x_decompress_safe(cmem, meta->table[index].size, + mem, &clen); + zs_unmap_object(meta->mem_pool, handle); /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret != LZO_E_OK)) { @@ -269,182 +173,165 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, return ret; } - flush_dcache_page(page); - return 0; } -static int zram_read_before_write(struct zram *zram, char *mem, u32 index) +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) { int ret; - size_t clen = PAGE_SIZE; - struct zobj_header *zheader; - unsigned char *cmem; + struct page *page; + unsigned char *user_mem, *uncmem = NULL; + struct zram_meta *meta = zram->meta; + page = bvec->bv_page; - if (zram_test_flag(zram, index, ZRAM_ZERO) || - !zram->table[index].handle) { - memset(mem, 0, PAGE_SIZE); + if (unlikely(!meta->table[index].handle) || + zram_test_flag(meta, index, ZRAM_ZERO)) { + handle_zero_page(bvec); return 0; } - cmem = zs_map_object(zram->mem_pool, zram->table[index].handle); + if (is_partial_io(bvec)) + /* Use a temporary buffer to decompress the page */ + uncmem = kmalloc(PAGE_SIZE, GFP_NOIO); - /* Page is stored uncompressed since it's incompressible */ - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - memcpy(mem, cmem, PAGE_SIZE); - kunmap_atomic(cmem, KM_USER0); - return 0; - } + user_mem = kmap_atomic(page); + if (!is_partial_io(bvec)) + uncmem = user_mem; - ret = lzo1x_decompress_safe(cmem + sizeof(*zheader), - zram->table[index].size, - mem, &clen); - zs_unmap_object(zram->mem_pool, zram->table[index].handle); + if (!uncmem) { + pr_info("Unable to allocate temp memory\n"); + ret = -ENOMEM; + goto out_cleanup; + } + ret = zram_decompress_page(zram, uncmem, index); /* Should NEVER happen. Return bio error if it does. */ - if (unlikely(ret != LZO_E_OK)) { - pr_err("Decompression failed! err=%d, page=%u\n", ret, index); - zram_stat64_inc(zram, &zram->stats.failed_reads); - return ret; - } + if (unlikely(ret != LZO_E_OK)) + goto out_cleanup; - return 0; + if (is_partial_io(bvec)) + memcpy(user_mem + bvec->bv_offset, uncmem + offset, + bvec->bv_len); + + flush_dcache_page(page); + ret = 0; +out_cleanup: + kunmap_atomic(user_mem); + if (is_partial_io(bvec)) + kfree(uncmem); + return ret; } static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, int offset) { - int ret; - u32 store_offset; + int ret = 0; size_t clen; - void *handle; - struct zobj_header *zheader; - struct page *page, *page_store; + unsigned long handle; + struct page *page; unsigned char *user_mem, *cmem, *src, *uncmem = NULL; + struct zram_meta *meta = zram->meta; page = bvec->bv_page; - src = zram->compress_buffer; + src = meta->compress_buffer; if (is_partial_io(bvec)) { /* * This is a partial IO. We need to read the full page * before to write the changes. */ - uncmem = kmalloc(PAGE_SIZE, GFP_KERNEL); + uncmem = kmalloc(PAGE_SIZE, GFP_NOIO); if (!uncmem) { - pr_info("Error allocating temp memory!\n"); ret = -ENOMEM; goto out; } - ret = zram_read_before_write(zram, uncmem, index); - if (ret) { - kfree(uncmem); + ret = zram_decompress_page(zram, uncmem, index); + if (ret) goto out; - } } /* * System overwrites unused sectors. Free memory associated * with this sector now. */ - if (zram->table[index].handle || - zram_test_flag(zram, index, ZRAM_ZERO)) + if (meta->table[index].handle || + zram_test_flag(meta, index, ZRAM_ZERO)) zram_free_page(zram, index); - user_mem = kmap_atomic(page, KM_USER0); + user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) + if (is_partial_io(bvec)) { memcpy(uncmem + offset, user_mem + bvec->bv_offset, bvec->bv_len); - else + kunmap_atomic(user_mem); + user_mem = NULL; + } else { uncmem = user_mem; + } if (page_zero_filled(uncmem)) { - kunmap_atomic(user_mem, KM_USER0); + kunmap_atomic(user_mem); if (is_partial_io(bvec)) kfree(uncmem); - zram_stat_inc(&zram->stats.pages_zero); - zram_set_flag(zram, index, ZRAM_ZERO); + zram->stats.pages_zero++; + zram_set_flag(meta, index, ZRAM_ZERO); ret = 0; goto out; } ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, - zram->compress_workmem); + meta->compress_workmem); - kunmap_atomic(user_mem, KM_USER0); - if (is_partial_io(bvec)) - kfree(uncmem); + if (!is_partial_io(bvec)) { + kunmap_atomic(user_mem); + user_mem = NULL; + uncmem = NULL; + } if (unlikely(ret != LZO_E_OK)) { pr_err("Compression failed! err=%d\n", ret); goto out; } - /* - * Page is incompressible. Store it as-is (uncompressed) - * since we do not want to return too many disk write - * errors which has side effect of hanging the system. - */ if (unlikely(clen > max_zpage_size)) { + zram->stats.bad_compress++; clen = PAGE_SIZE; - page_store = alloc_page(GFP_NOIO | __GFP_HIGHMEM); - if (unlikely(!page_store)) { - pr_info("Error allocating memory for " - "incompressible page: %u\n", index); - ret = -ENOMEM; - goto out; - } - - store_offset = 0; - zram_set_flag(zram, index, ZRAM_UNCOMPRESSED); - zram_stat_inc(&zram->stats.pages_expand); - handle = page_store; - src = kmap_atomic(page, KM_USER0); - cmem = kmap_atomic(page_store, KM_USER1); - goto memstore; + src = NULL; + if (is_partial_io(bvec)) + src = uncmem; } - handle = zs_malloc(zram->mem_pool, clen + sizeof(*zheader)); + handle = zs_malloc(meta->mem_pool, clen); if (!handle) { pr_info("Error allocating memory for compressed " "page: %u, size=%zu\n", index, clen); ret = -ENOMEM; goto out; } - cmem = zs_map_object(zram->mem_pool, handle); - -memstore: -#if 0 - /* Back-reference needed for memory defragmentation */ - if (!zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)) { - zheader = (struct zobj_header *)cmem; - zheader->table_idx = index; - cmem += sizeof(*zheader); - } -#endif + cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO); + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + src = kmap_atomic(page); memcpy(cmem, src, clen); + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + kunmap_atomic(src); - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) { - kunmap_atomic(cmem, KM_USER1); - kunmap_atomic(src, KM_USER0); - } else { - zs_unmap_object(zram->mem_pool, handle); - } + zs_unmap_object(meta->mem_pool, handle); - zram->table[index].handle = handle; - zram->table[index].size = clen; + meta->table[index].handle = handle; + meta->table[index].size = clen; /* Update stats */ zram_stat64_add(zram, &zram->stats.compr_size, clen); - zram_stat_inc(&zram->stats.pages_stored); + zram->stats.pages_stored++; if (clen <= PAGE_SIZE / 2) - zram_stat_inc(&zram->stats.good_compress); - - return 0; + zram->stats.good_compress++; out: + if (is_partial_io(bvec)) + kfree(uncmem); + if (ret) zram_stat64_inc(zram, &zram->stats.failed_writes); return ret; @@ -550,69 +437,56 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) /* * Handler function for all zram I/O requests. */ -static int zram_make_request(struct request_queue *queue, struct bio *bio) +static void zram_make_request(struct request_queue *queue, struct bio *bio) { struct zram *zram = queue->queuedata; - if (unlikely(!zram->init_done) && zram_init_device(zram)) - goto error; - down_read(&zram->init_lock); if (unlikely(!zram->init_done)) - goto error_unlock; + goto error; if (!valid_io_request(zram, bio)) { zram_stat64_inc(zram, &zram->stats.invalid_io); - goto error_unlock; + goto error; } __zram_make_request(zram, bio, bio_data_dir(bio)); up_read(&zram->init_lock); - return 0; + return; -error_unlock: - up_read(&zram->init_lock); error: + up_read(&zram->init_lock); bio_io_error(bio); - return 0; } -void __zram_reset_device(struct zram *zram) +static void __zram_reset_device(struct zram *zram) { size_t index; + struct zram_meta *meta; - zram->init_done = 0; - - /* Free various per-device buffers */ - kfree(zram->compress_workmem); - free_pages((unsigned long)zram->compress_buffer, 1); + if (!zram->init_done) + return; - zram->compress_workmem = NULL; - zram->compress_buffer = NULL; + meta = zram->meta; + zram->init_done = 0; /* Free all pages that are still in this zram device */ for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { - void *handle = zram->table[index].handle; + unsigned long handle = meta->table[index].handle; if (!handle) continue; - if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) - __free_page(handle); - else - zs_free(zram->mem_pool, handle); + zs_free(meta->mem_pool, handle); } - vfree(zram->table); - zram->table = NULL; - - zs_destroy_pool(zram->mem_pool); - zram->mem_pool = NULL; - + zram_meta_free(zram->meta); + zram->meta = NULL; /* Reset stats */ memset(&zram->stats, 0, sizeof(zram->stats)); zram->disksize = 0; + set_capacity(zram->disk, 0); } void zram_reset_device(struct zram *zram) @@ -622,69 +496,84 @@ void zram_reset_device(struct zram *zram) up_write(&zram->init_lock); } -int zram_init_device(struct zram *zram) +void zram_meta_free(struct zram_meta *meta) { - int ret; - size_t num_pages; - - down_write(&zram->init_lock); + zs_destroy_pool(meta->mem_pool); + kfree(meta->compress_workmem); + free_pages((unsigned long)meta->compress_buffer, 1); + vfree(meta->table); + kfree(meta); +} - if (zram->init_done) { - up_write(&zram->init_lock); - return 0; - } +struct zram_meta *zram_meta_alloc(u64 disksize) +{ + size_t num_pages; + struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if (!meta) + goto out; - zram_set_disksize(zram, totalram_pages << PAGE_SHIFT); + meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + if (!meta->compress_workmem) + goto free_meta; - zram->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); - if (!zram->compress_workmem) { - pr_err("Error allocating compressor working memory!\n"); - ret = -ENOMEM; - goto fail_no_table; - } - - zram->compress_buffer = + meta->compress_buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!zram->compress_buffer) { + if (!meta->compress_buffer) { pr_err("Error allocating compressor buffer space\n"); - ret = -ENOMEM; - goto fail_no_table; + goto free_workmem; } - num_pages = zram->disksize >> PAGE_SHIFT; - zram->table = vzalloc(num_pages * sizeof(*zram->table)); - if (!zram->table) { + num_pages = disksize >> PAGE_SHIFT; + meta->table = vzalloc(num_pages * sizeof(*meta->table)); + if (!meta->table) { pr_err("Error allocating zram address table\n"); - ret = -ENOMEM; - goto fail_no_table; + goto free_buffer; } - set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM); + if (!meta->mem_pool) { + pr_err("Error creating memory pool\n"); + goto free_table; + } - /* zram devices sort of resembles non-rotational disks */ - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); + return meta; - zram->mem_pool = zs_create_pool("zram", GFP_NOIO | __GFP_HIGHMEM); - if (!zram->mem_pool) { - pr_err("Error creating memory pool\n"); - ret = -ENOMEM; - goto fail; +free_table: + vfree(meta->table); +free_buffer: + free_pages((unsigned long)meta->compress_buffer, 1); +free_workmem: + kfree(meta->compress_workmem); +free_meta: + kfree(meta); + meta = NULL; +out: + return meta; +} + +void zram_init_device(struct zram *zram, struct zram_meta *meta) +{ + if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { + pr_info( + "There is little point creating a zram of greater than " + "twice the size of memory since we expect a 2:1 compression " + "ratio. Note that zram uses about 0.1%% of the size of " + "the disk when not in use so a huge zram is " + "wasteful.\n" + "\tMemory Size: %lu kB\n" + "\tSize you selected: %llu kB\n" + "Continuing anyway ...\n", + (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 + ); } + /* zram devices sort of resembles non-rotational disks */ + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); + + zram->meta = meta; zram->init_done = 1; - up_write(&zram->init_lock); pr_debug("Initialization done!\n"); - return 0; - -fail_no_table: - /* To prevent accessing table entries during cleanup */ - zram->disksize = 0; -fail: - __zram_reset_device(zram); - up_write(&zram->init_lock); - pr_err("Initialization failed: err=%d\n", ret); - return ret; } static void zram_slot_free_notify(struct block_device *bdev, @@ -725,7 +614,7 @@ static int create_device(struct zram *zram, int device_id) zram->disk = alloc_disk(1); if (!zram->disk) { blk_cleanup_queue(zram->queue); - pr_warning("Error allocating disk structure for device %d\n", + pr_warn("Error allocating disk structure for device %d\n", device_id); ret = -ENOMEM; goto out; @@ -756,7 +645,7 @@ static int create_device(struct zram *zram, int device_id) ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj, &zram_disk_attr_group); if (ret < 0) { - pr_warning("Error creating sysfs group"); + pr_warn("Error creating sysfs group"); goto out; } @@ -790,7 +679,7 @@ static int __init zram_init(void) int ret, dev_id; if (num_devices > max_num_devices) { - pr_warning("Invalid value for num_devices: %u\n", + pr_warn("Invalid value for num_devices: %u\n", num_devices); ret = -EINVAL; goto out; @@ -798,18 +687,12 @@ static int __init zram_init(void) zram_major = register_blkdev(0, "zram"); if (zram_major <= 0) { - pr_warning("Unable to get major number\n"); + pr_warn("Unable to get major number\n"); ret = -EBUSY; goto out; } - if (!num_devices) { - pr_info("num_devices not specified. Using default: 1\n"); - num_devices = 1; - } - /* Allocate the device array and initialize each one */ - pr_info("Creating %u devices ...\n", num_devices); zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); if (!zram_devices) { ret = -ENOMEM; @@ -822,6 +705,8 @@ static int __init zram_init(void) goto free_devices; } + pr_info("Created %u device(s) ...\n", num_devices); + return 0; free_devices: @@ -843,8 +728,7 @@ static void __exit zram_exit(void) zram = &zram_devices[i]; destroy_device(zram); - if (zram->init_done) - zram_reset_device(zram); + zram_reset_device(zram); } unregister_blkdev(zram_major, "zram"); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 880f013..2d1a3f1 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -26,23 +26,8 @@ */ static const unsigned max_num_devices = 32; -/* - * Stored at beginning of each compressed object. - * - * It stores back-reference to table entry which points to this - * object. This is required to support memory defragmentation. - */ -struct zobj_header { -#if 0 - u32 table_idx; -#endif -}; - /*-- Configurable parameters */ -/* Default zram disk size: 25% of total RAM */ -static const unsigned default_disksize_perc_ram = 25; - /* * Pages that compress to size greater than this are stored * uncompressed in memory. @@ -51,8 +36,8 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; /* * NOTE: max_zpage_size must be less than or equal to: - * ZS_MAX_ALLOC_SIZE - sizeof(struct zobj_header) - * otherwise, xv_malloc() would always return failure. + * ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would + * always return failure. */ /*-- End of configurable params */ @@ -68,9 +53,6 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; /* Flags for zram pages (table[page_no].flags) */ enum zram_pageflags { - /* Page is stored uncompressed */ - ZRAM_UNCOMPRESSED, - /* Page consists entirely of zeros */ ZRAM_ZERO, @@ -81,11 +63,11 @@ enum zram_pageflags { /* Allocated for each disk page */ struct table { - void *handle; + unsigned long handle; u16 size; /* object size (excluding header) */ u8 count; /* object ref count (not yet used) */ u8 flags; -} __attribute__((aligned(4))); +} __aligned(4); struct zram_stats { u64 compr_size; /* compressed size of pages stored */ @@ -98,17 +80,21 @@ struct zram_stats { u32 pages_zero; /* no. of zero filled pages */ u32 pages_stored; /* no. of pages currently stored */ u32 good_compress; /* % of pages with compression ratio<=50% */ - u32 pages_expand; /* % of incompressible pages */ + u32 bad_compress; /* % of pages with compression ratio>=75% */ }; -struct zram { - struct zs_pool *mem_pool; +struct zram_meta { void *compress_workmem; void *compress_buffer; struct table *table; + struct zs_pool *mem_pool; +}; + +struct zram { + struct zram_meta *meta; spinlock_t stat64_lock; /* protect 64-bit stats */ - struct rw_semaphore lock; /* protect compression buffers against - * concurrent writes */ + struct rw_semaphore lock; /* protect compression buffers and table + * against concurrent read and writes */ struct request_queue *queue; struct gendisk *disk; int init_done; @@ -129,7 +115,9 @@ unsigned int zram_get_num_devices(void); extern struct attribute_group zram_disk_attr_group; #endif -extern int zram_init_device(struct zram *zram); -extern void __zram_reset_device(struct zram *zram); +extern void zram_reset_device(struct zram *zram); +extern struct zram_meta *zram_meta_alloc(u64 disksize); +extern void zram_meta_free(struct zram_meta *meta); +extern void zram_init_device(struct zram *zram, struct zram_meta *meta); #endif diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 5142d07..68ccad1 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "zram_drv.h" @@ -54,23 +55,27 @@ static ssize_t disksize_show(struct device *dev, static ssize_t disksize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { - int ret; u64 disksize; + struct zram_meta *meta; struct zram *zram = dev_to_zram(dev); - ret = kstrtoull(buf, 10, &disksize); - if (ret) - return ret; + disksize = memparse(buf, NULL); + if (!disksize) + return -EINVAL; + disksize = PAGE_ALIGN(disksize); + meta = zram_meta_alloc(disksize); down_write(&zram->init_lock); if (zram->init_done) { up_write(&zram->init_lock); + zram_meta_free(meta); pr_info("Cannot change disksize for initialized device\n"); return -EBUSY; } - zram->disksize = PAGE_ALIGN(disksize); + zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + zram_init_device(zram, meta); up_write(&zram->init_lock); return len; @@ -117,11 +122,7 @@ static ssize_t reset_store(struct device *dev, if (bdev) fsync_bdev(bdev); - down_write(&zram->init_lock); - if (zram->init_done) - __zram_reset_device(zram); - up_write(&zram->init_lock); - + zram_reset_device(zram); return len; } @@ -192,11 +193,10 @@ static ssize_t mem_used_total_show(struct device *dev, { u64 val = 0; struct zram *zram = dev_to_zram(dev); + struct zram_meta *meta = zram->meta; - if (zram->init_done) { - val = zs_get_total_size_bytes(zram->mem_pool) + - ((u64)(zram->stats.pages_expand) << PAGE_SHIFT); - } + if (zram->init_done) + val = zs_get_total_size_bytes(meta->mem_pool); return sprintf(buf, "%llu\n", val); } -- cgit v1.1 From 34ea3381f760a04dab010cb94c5783c5ac78c201 Mon Sep 17 00:00:00 2001 From: Larry Bassel Date: Fri, 11 Oct 2013 15:32:37 -0700 Subject: zsmalloc: use 3.10 version of zsmalloc commit 796ce5a7e4ef88ee0bfbeaa80070a51570650d57 Author: Arnd Bergmann Date: Tue Apr 23 18:30:48 2013 +0200 staging/zsmalloc: don't use pgtable-mapping from modules Building zsmalloc as a module does not work on ARM because it uses an interface that is not exported: ERROR: "flush_tlb_kernel_range" [drivers/staging/zsmalloc/zsmalloc.ko] undefined! Since this is only used as a performance optimization and only on ARM, we can avoid the problem simply by not using that optimization when building zsmalloc it is a loadable module. flush_tlb_kernel_range is often an inline function, but out of the architectures that use an extern function, only powerpc exports it. Signed-off-by: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Seth Jennings Cc: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit d95abbbb291bf5bce078148f53603ce9c0aa1d44 Author: Joerg Roedel Date: Wed Mar 27 01:43:14 2013 +0100 staging: zsmalloc: Fix link error on ARM Testing the arm chromebook config against the upstream kernel produces a linker error for the zsmalloc module from staging. The symbol flush_tlb_kernel_range is not available there. Fix this by removing the reimplementation of unmap_kernel_range in the zsmalloc module and using the function directly. The unmap_kernel_range function is not usable by modules, so also disallow building the driver as a module for now. Cc: stable Signed-off-by: Joerg Roedel Acked-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 22b751c3d0376e86a377e3a0aa2ddbbe9d2eefc1 Author: Mel Gorman Date: Fri Feb 22 16:34:59 2013 -0800 mm: rename page struct field helpers The function names page_xchg_last_nid(), page_last_nid() and reset_page_last_nid() were judged to be inconsistent so rename them to a struct_field_op style pattern. As it looked jarring to have reset_page_mapcount() and page_nid_reset_last() beside each other in memmap_init_zone(), this patch also renames reset_page_mapcount() to page_mapcount_reset(). There are others like init_page_count() but as it is used throughout the arch code a rename would likely cause more conflicts than it is worth. [akpm@linux-foundation.org: fix zcache] Signed-off-by: Mel Gorman Suggested-by: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds commit 0d145a501778042d0411c843ed5b468b41f8a171 Author: Seth Jennings Date: Wed Jan 30 09:36:52 2013 -0600 staging: zsmalloc: remove unused pool name zs_create_pool() currently takes a name argument which is never used in any useful way. This patch removes it. Signed-off-by: Seth Jennings Acked-by: Nitin Gupta Acked-by: Rik van Riel Signed-off-by: Greg Kroah-Hartman commit 9915518887e83764269d5b617d01782893877ed3 Author: Minchan Kim Date: Mon Jan 28 10:00:08 2013 +0900 staging: zsmalloc: Fix TLB coherency and build problem Recently, Matt Sealey reported he fail to build zsmalloc caused by using of local_flush_tlb_kernel_range which are architecture dependent function so !CONFIG_SMP in ARM couldn't implement it so it ends up build error following as. MODPOST 216 modules LZMA arch/arm/boot/compressed/piggy.lzma AS arch/arm/boot/compressed/lib1funcs.o ERROR: "v7wbi_flush_kern_tlb_range" [drivers/staging/zsmalloc/zsmalloc.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 make: *** Waiting for unfinished jobs.... The reason we used that function is copy method by [1] was really slow in ARM but at that time. More severe problem is ARM can prefetch speculatively on other CPUs so under us, other TLBs can have an entry only if we do flush local CPU. Russell King pointed that. Thanks! We don't have many choices except using flush_tlb_kernel_range. My experiment in ARMv7 processor 4 core didn't make any difference with zsmapbench[2] between local_flush_tlb_kernel_range and flush_tlb_kernel_range but still page-table based is much better than copy-based. * bigger is better. 1. local_flush_tlb_kernel_range: 3918795 mappings 2. flush_tlb_kernel_range : 3989538 mappings 3. copy-based: 635158 mappings This patch replace local_flush_tlb_kernel_range with flush_tlb_kernel_range which are avaialbe in all architectures because we already have used it in vmalloc allocator which are generic one so build problem should go away and performane loss shoud be void. [1] f553646, zsmalloc: add page table mapping method [2] https://github.com/spartacus06/zsmapbench Cc: stable@vger.kernel.org Cc: Dan Magenheimer Cc: Russell King Cc: Konrad Rzeszutek Wilk Cc: Nitin Gupta Cc: Seth Jennings Reported-by: Matt Sealey Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit d662b8eba94e9f6d4c036719dbf629ef0c9309cf Author: Seth Jennings Date: Fri Jan 25 11:46:18 2013 -0600 staging: zsmalloc: make CLASS_DELTA relative to PAGE_SIZE Right now ZS_SIZE_CLASS_DELTA is hardcoded to be 16. This creates 254 classes for systems with 4k pages. However, on PPC64 with 64k pages, it creates 4095 classes which is far too many. This patch makes ZS_SIZE_CLASS_DELTA relative to PAGE_SIZE so that regardless of the page size, there will be the same number of classes. Acked-by: Nitin Gupta Acked-by: Minchan Kim Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman commit 4bbc0bc06b8b0cced31ee17beb753ad51a2e47e7 Author: Davidlohr Bueso Date: Fri Jan 4 12:14:00 2013 -0800 staging: zsmalloc: comment zs_create_pool function Just as with zs_malloc() and zs_map_object(), it is worth formally commenting the zs_create_pool() function. Signed-off-by: Davidlohr Bueso Signed-off-by: Greg Kroah-Hartman commit 0959c63f11c3bbef0a7d6c5011be8d25503f547c Author: Seth Jennings Date: Wed Aug 8 15:12:17 2012 +0900 zsmalloc: collapse internal .h into .c The patch collapses in the internal zsmalloc_int.h into the zsmalloc-main.c file. This is done in preparation for the promotion to mm/ where separate internal headers are discouraged. Signed-off-by: Seth Jennings Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman commit f553646a67cb215577402cb702b67c8cf8fdb46f Author: Seth Jennings Date: Wed Jul 18 11:55:56 2012 -0500 staging: zsmalloc: add page table mapping method This patchset provides page mapping via the page table. On some archs, most notably ARM, this method has been demonstrated to be faster than copying. The logic controlling the method selection (copy vs page table) is controlled by the definition of USE_PGTABLE_MAPPING which is/can be defined for any arch that performs better with page table mapping. Signed-off-by: Seth Jennings Acked-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit c60369f011251c60de506994aab088f1afb90bf4 Author: Seth Jennings Date: Wed Jul 18 11:55:55 2012 -0500 staging: zsmalloc: prevent mappping in interrupt context Because we use per-cpu mapping areas shared among the pools/users, we can't allow mapping in interrupt context because it can corrupt another users mappings. Signed-off-by: Seth Jennings Acked-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit 6539a36c0cb9ec7f1c1633b535ac83b2bdf0ae6d Author: Seth Jennings Date: Wed Jul 18 11:55:54 2012 -0500 staging: zsmalloc: s/firstpage/page in new copy map funcs firstpage already has precedent and meaning the first page of a zspage. In the case of the copy mapping functions, it is the first of a pair of pages needing to be mapped. This patch just renames the firstpage argument to "page" to avoid confusion. Signed-off-by: Seth Jennings Acked-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit b74185108668ef966e663878adbad65e03bfcb43 Author: Seth Jennings Date: Mon Jul 2 16:15:52 2012 -0500 staging: zsmalloc: add mapping modes This patch improves mapping performance in zsmalloc by getting usage information from the user in the form of a "mapping mode" and using it to avoid unnecessary copying for objects that span pages. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman commit 166cfda752ca22eb6912614993b85f9a997dbd8f Author: Seth Jennings Date: Mon Jul 2 16:15:51 2012 -0500 staging: zsmalloc: add details to zs_map_object boiler plate Add information on the usage limits of zs_map_object() Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman commit 103123305c4f695cbb11555d51a32ea14d6bed05 Author: Seth Jennings Date: Mon Jul 2 16:15:50 2012 -0500 staging: zsmalloc: add single-page object fastpath in unmap Improve zs_unmap_object() performance by adding a fast path for objects that don't span pages. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman commit 5f601902c61e6cda627ec71c10609021067ed0fa Author: Seth Jennings Date: Mon Jul 2 16:15:49 2012 -0500 staging: zsmalloc: remove x86 dependency This patch replaces the page table assisted object mapping method, which has x86 dependencies, with a arch-independent method that does a simple copy into a temporary per-cpu buffer. While a copy seems like it would be worse than mapping the pages, tests demonstrate the copying is always faster and, in the case of running inside a KVM guest, roughly 4x faster. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman commit 069f101fa463351f528773d73b74e9b606b3f66a Author: Ben Hutchings Date: Wed Jun 20 02:31:11 2012 +0100 staging: zsmalloc: Finish conversion to a separate module ZSMALLOC is tristate, but the code has no MODULE_LICENSE and since it depends on GPL-only symbols it cannot be loaded as a module. This in turn breaks zram which now depends on it. I assume it's meant to be Dual BSD/GPL like the other z-stuff. There is also no module_exit, which will make it impossible to unload. Add the appropriate module_init and module_exit declarations suggested by comments. Reported-by: Christian Ohm References: http://bugs.debian.org/677273 Cc: stable@vger.kernel.org # v3.4 Signed-off-by: Ben Hutchings Reviewed-by: Jonathan Nieder Signed-off-by: Greg Kroah-Hartman commit b4b700c5a61c6e6db976f60d4eb6ad369e838aa9 Author: Seth Jennings Date: Wed Jun 13 16:03:42 2012 -0500 staging: zsmalloc: fix uninit'ed variable warning This patch fixes an uninitialized variable warning in alloc_zspage(). It also fixes the secondary issue of prev_page leaving scope on each loop iteration. The only reason this ever worked was because prev_page was occupying the same space on the stack on each iteration. Signed-off-by: Seth Jennings Reported-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman commit 2db51dae56240b52fe08ddbb1a2eb47fe7cfd044 Author: Nitin Gupta Date: Sat Jun 9 17:41:14 2012 -0700 staging: zsmalloc documentation Documentation of various struct page fields used by zsmalloc. Changes for v2: - Regroup descriptions as suggested by Konrad Signed-off-by: Nitin Gupta Acked-by: Konrad Rzeszutek Wilk Reviewed-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman commit c234434835b1f4bad9bdbae6710044cba387c9e5 Author: Minchan Kim Date: Fri Jun 8 15:39:25 2012 +0900 staging: zsmalloc: zsmalloc: use unsigned long instead of void * We should use unsigned long as handle instead of void * to avoid any confusion. Without this, users may just treat zs_malloc return value as a pointer and try to deference it. This patch passed compile test(zram, zcache and ramster) and zram is tested on qemu. changelog * from v2 - remove hval pointed out by Nitin - based on next-20120607 * from v1 - change zcache's zv_create return value - baesd on next-20120604 Cc: Dan Magenheimer Acked-by: Seth Jennings Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit 00a61d8618bb7314113eb3ba27c12631cd85c298 Author: Minchan Kim Date: Thu May 3 15:40:40 2012 +0900 staging: zsmalloc: add/fix function comment Add/fix the comment. Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit 2e3b61547191a5934ff6c789aaa4d7e9a9e52f5a Author: Minchan Kim Date: Thu May 3 15:40:39 2012 +0900 staging: zsmalloc: rename zspage_order with zspage_pages zspage_order defines how many pages are needed to make a zspage. So _order_ is rather awkward naming. It already deceive Jonathan - http://lwn.net/Articles/477067/ " For each size, the code calculates an optimum number of pages (up to 16)" Let's change from _order_ to _pages_ and some function names. Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman commit d210267741fb2a8b6d741d9040703683a39087f4 Merge: 69964ea 5bb196a Author: Greg Kroah-Hartman Date: Wed May 2 11:48:07 2012 -0700 Merge 3.4-rc5 into staging-next This resolves the conflict in: drivers/staging/vt6656/ioctl.c Signed-off-by: Greg Kroah-Hartman commit a27545bf0bab9027e5c040901b68956551d9f63e Author: Minchan Kim Date: Wed Apr 25 15:23:09 2012 +0900 zsmalloc: use PageFlag macro instead of [set|test]_bit MM code always uses PageXXX to handle page flags. Let's keep the consistency. Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman Change-Id: I542f0b9eaf0264e1c9e38f9669fec8ea0debae7c Signed-off-by: Larry Bassel --- drivers/staging/zsmalloc/Kconfig | 2 +- drivers/staging/zsmalloc/zsmalloc-main.c | 439 +++++++++++++++++++++++++------ drivers/staging/zsmalloc/zsmalloc.h | 22 +- drivers/staging/zsmalloc/zsmalloc_int.h | 154 ----------- 4 files changed, 383 insertions(+), 234 deletions(-) delete mode 100644 drivers/staging/zsmalloc/zsmalloc_int.h diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig index 9084565..7fab032 100644 --- a/drivers/staging/zsmalloc/Kconfig +++ b/drivers/staging/zsmalloc/Kconfig @@ -1,5 +1,5 @@ config ZSMALLOC - tristate "Memory allocator for compressed pages" + bool "Memory allocator for compressed pages" default n help zsmalloc is a slab-based memory allocator designed to store diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 4cd0f04..f82f7e6 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -10,6 +10,54 @@ * Released under the terms of GNU General Public License Version 2.0 */ + +/* + * This allocator is designed for use with zcache and zram. Thus, the + * allocator is supposed to work well under low memory conditions. In + * particular, it never attempts higher order page allocation which is + * very likely to fail under memory pressure. On the other hand, if we + * just use single (0-order) pages, it would suffer from very high + * fragmentation -- any object of size PAGE_SIZE/2 or larger would occupy + * an entire page. This was one of the major issues with its predecessor + * (xvmalloc). + * + * To overcome these issues, zsmalloc allocates a bunch of 0-order pages + * and links them together using various 'struct page' fields. These linked + * pages act as a single higher-order page i.e. an object can span 0-order + * page boundaries. The code refers to these linked pages as a single entity + * called zspage. + * + * Following is how we use various fields and flags of underlying + * struct page(s) to form a zspage. + * + * Usage of struct page fields: + * page->first_page: points to the first component (0-order) page + * page->index (union with page->freelist): offset of the first object + * starting in this page. For the first page, this is + * always 0, so we use this field (aka freelist) to point + * to the first free object in zspage. + * page->lru: links together all component pages (except the first page) + * of a zspage + * + * For _first_ page only: + * + * page->private (union with page->first_page): refers to the + * component page after the first page + * page->freelist: points to the first free object in zspage. + * Free objects are linked together using in-place + * metadata. + * page->objects: maximum number of objects we can store in this + * zspage (class->zspage_order * PAGE_SIZE / class->size) + * page->lru: links together first pages of various zspages. + * Basically forming list of zspages in a fullness group. + * page->mapping: class index and fullness group of the zspage + * + * Usage of struct page flags: + * PG_private: identifies the first component page + * PG_private2: identifies the last component page + * + */ + #ifdef CONFIG_ZSMALLOC_DEBUG #define DEBUG #endif @@ -27,9 +75,139 @@ #include #include #include +#include +#include +#include #include "zsmalloc.h" -#include "zsmalloc_int.h" + +/* + * This must be power of 2 and greater than of equal to sizeof(link_free). + * These two conditions ensure that any 'struct link_free' itself doesn't + * span more than 1 page which avoids complex case of mapping 2 pages simply + * to restore link_free pointer values. + */ +#define ZS_ALIGN 8 + +/* + * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single) + * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N. + */ +#define ZS_MAX_ZSPAGE_ORDER 2 +#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) + +/* + * Object location (, ) is encoded as + * as single (void *) handle value. + * + * Note that object index is relative to system + * page it is stored in, so for each sub-page belonging + * to a zspage, obj_idx starts with 0. + * + * This is made more complicated by various memory models and PAE. + */ + +#ifndef MAX_PHYSMEM_BITS +#ifdef CONFIG_HIGHMEM64G +#define MAX_PHYSMEM_BITS 36 +#else /* !CONFIG_HIGHMEM64G */ +/* + * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just + * be PAGE_SHIFT + */ +#define MAX_PHYSMEM_BITS BITS_PER_LONG +#endif +#endif +#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) +#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) +#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) + +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) +/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ +#define ZS_MIN_ALLOC_SIZE \ + MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) +#define ZS_MAX_ALLOC_SIZE PAGE_SIZE + +/* + * On systems with 4K page size, this gives 254 size classes! There is a + * trader-off here: + * - Large number of size classes is potentially wasteful as free page are + * spread across these classes + * - Small number of size classes causes large internal fragmentation + * - Probably its better to use specific size classes (empirically + * determined). NOTE: all those class sizes must be set as multiple of + * ZS_ALIGN to make sure link_free itself never has to span 2 pages. + * + * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN + * (reason above) + */ +#define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> 8) +#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \ + ZS_SIZE_CLASS_DELTA + 1) + +/* + * We do not maintain any list for completely empty or full pages + */ +enum fullness_group { + ZS_ALMOST_FULL, + ZS_ALMOST_EMPTY, + _ZS_NR_FULLNESS_GROUPS, + + ZS_EMPTY, + ZS_FULL +}; + +/* + * We assign a page to ZS_ALMOST_EMPTY fullness group when: + * n <= N / f, where + * n = number of allocated objects + * N = total number of objects zspage can store + * f = 1/fullness_threshold_frac + * + * Similarly, we assign zspage to: + * ZS_ALMOST_FULL when n > N / f + * ZS_EMPTY when n == 0 + * ZS_FULL when n == N + * + * (see: fix_fullness_group()) + */ +static const int fullness_threshold_frac = 4; + +struct size_class { + /* + * Size of objects stored in this class. Must be multiple + * of ZS_ALIGN. + */ + int size; + unsigned int index; + + /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ + int pages_per_zspage; + + spinlock_t lock; + + /* stats */ + u64 pages_allocated; + + struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS]; +}; + +/* + * Placed within free objects to form a singly linked list. + * For every zspage, first_page->freelist gives head of this list. + * + * This must be power of 2 and less than or equal to ZS_ALIGN + */ +struct link_free { + /* Handle of next free chunk (encodes ) */ + void *next; +}; + +struct zs_pool { + struct size_class size_class[ZS_SIZE_CLASSES]; + + gfp_t flags; /* allocation flags used when growing pool */ +}; /* * A zspage's class index and fullness group @@ -40,17 +218,39 @@ #define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) #define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) +/* + * By default, zsmalloc uses a copy-based object mapping method to access + * allocations that span two pages. However, if a particular architecture + * performs VM mapping faster than copying, then it should be added here + * so that USE_PGTABLE_MAPPING is defined. This causes zsmalloc to use + * page table mapping rather than copying for object mapping. +*/ +#if defined(CONFIG_ARM) && !defined(MODULE) +#define USE_PGTABLE_MAPPING +#endif + +struct mapping_area { +#ifdef USE_PGTABLE_MAPPING + struct vm_struct *vm; /* vm area for mapping object that span pages */ +#else + char *vm_buf; /* copy buffer for objects that span pages */ +#endif + char *vm_addr; /* address of kmap_atomic()'ed pages */ + enum zs_mapmode vm_mm; /* mapping mode */ +}; + + /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ static DEFINE_PER_CPU(struct mapping_area, zs_map_area); static int is_first_page(struct page *page) { - return test_bit(PG_private, &page->flags); + return PagePrivate(page); } static int is_last_page(struct page *page) { - return test_bit(PG_private_2, &page->flags); + return PagePrivate2(page); } static void get_zspage_mapping(struct page *page, unsigned int *class_idx, @@ -180,7 +380,7 @@ out: * link together 3 PAGE_SIZE sized pages to form a zspage * since then we can perfectly fit in 8 such objects. */ -static int get_zspage_order(int class_size) +static int get_pages_per_zspage(int class_size) { int i, max_usedpc = 0; /* zspage order which gives maximum used size per KB */ @@ -247,13 +447,11 @@ static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) } /* Decode pair from the given object handle */ -static void obj_handle_to_location(void *handle, struct page **page, +static void obj_handle_to_location(unsigned long handle, struct page **page, unsigned long *obj_idx) { - unsigned long hval = (unsigned long)handle; - - *page = pfn_to_page(hval >> OBJ_INDEX_BITS); - *obj_idx = hval & OBJ_INDEX_MASK; + *page = pfn_to_page(handle >> OBJ_INDEX_BITS); + *obj_idx = handle & OBJ_INDEX_MASK; } static unsigned long obj_idx_to_offset(struct page *page, @@ -274,7 +472,7 @@ static void reset_page(struct page *page) set_page_private(page, 0); page->mapping = NULL; page->freelist = NULL; - reset_page_mapcount(page); + page_mapcount_reset(page); } static void free_zspage(struct page *first_page) @@ -354,7 +552,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) static struct page *alloc_zspage(struct size_class *class, gfp_t flags) { int i, error; - struct page *first_page = NULL; + struct page *first_page = NULL, *uninitialized_var(prev_page); /* * Allocate individual pages and link them together as: @@ -368,8 +566,8 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags) * identify the last page. */ error = -ENOMEM; - for (i = 0; i < class->zspage_order; i++) { - struct page *page, *prev_page; + for (i = 0; i < class->pages_per_zspage; i++) { + struct page *page; page = alloc_page(flags); if (!page) @@ -377,7 +575,7 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags) INIT_LIST_HEAD(&page->lru); if (i == 0) { /* first page */ - set_bit(PG_private, &page->flags); + SetPagePrivate(page); set_page_private(page, 0); first_page = page; first_page->inuse = 0; @@ -388,9 +586,8 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags) page->first_page = first_page; if (i >= 2) list_add(&page->lru, &prev_page->lru); - if (i == class->zspage_order - 1) /* last page */ - set_bit(PG_private_2, &page->flags); - + if (i == class->pages_per_zspage - 1) /* last page */ + SetPagePrivate2(page); prev_page = page; } @@ -398,7 +595,7 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags) first_page->freelist = obj_location_to_handle(first_page, 0); /* Maximum number of objects we can store in this zspage */ - first_page->objects = class->zspage_order * PAGE_SIZE / class->size; + first_page->objects = class->pages_per_zspage * PAGE_SIZE / class->size; error = 0; /* Success */ @@ -425,23 +622,84 @@ static struct page *find_get_zspage(struct size_class *class) return page; } -static void zs_copy_map_object(char *buf, struct page *firstpage, - int off, int size) +#ifdef USE_PGTABLE_MAPPING +static inline int __zs_cpu_up(struct mapping_area *area) +{ + /* + * Make sure we don't leak memory if a cpu UP notification + * and zs_init() race and both call zs_cpu_up() on the same cpu + */ + if (area->vm) + return 0; + area->vm = alloc_vm_area(PAGE_SIZE * 2, NULL); + if (!area->vm) + return -ENOMEM; + return 0; +} + +static inline void __zs_cpu_down(struct mapping_area *area) +{ + if (area->vm) + free_vm_area(area->vm); + area->vm = NULL; +} + +static inline void *__zs_map_object(struct mapping_area *area, + struct page *pages[2], int off, int size) +{ + BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, &pages)); + area->vm_addr = area->vm->addr; + return area->vm_addr + off; +} + +static inline void __zs_unmap_object(struct mapping_area *area, + struct page *pages[2], int off, int size) +{ + unsigned long addr = (unsigned long)area->vm_addr; + + unmap_kernel_range(addr, PAGE_SIZE * 2); +} + +#else /* USE_PGTABLE_MAPPING */ + +static inline int __zs_cpu_up(struct mapping_area *area) +{ + /* + * Make sure we don't leak memory if a cpu UP notification + * and zs_init() race and both call zs_cpu_up() on the same cpu + */ + if (area->vm_buf) + return 0; + area->vm_buf = (char *)__get_free_page(GFP_KERNEL); + if (!area->vm_buf) + return -ENOMEM; + return 0; +} + +static inline void __zs_cpu_down(struct mapping_area *area) +{ + if (area->vm_buf) + free_page((unsigned long)area->vm_buf); + area->vm_buf = NULL; +} + +static void *__zs_map_object(struct mapping_area *area, + struct page *pages[2], int off, int size) { - struct page *pages[2]; int sizes[2]; void *addr; + char *buf = area->vm_buf; - pages[0] = firstpage; - pages[1] = get_next_page(firstpage); - BUG_ON(!pages[1]); + /* disable page faults to match kmap_atomic() return conditions */ + pagefault_disable(); + + /* no read fastpath */ + if (area->vm_mm == ZS_MM_WO) + goto out; sizes[0] = PAGE_SIZE - off; sizes[1] = size - sizes[0]; - /* disable page faults to match kmap_atomic() return conditions */ - pagefault_disable(); - /* copy object to per-cpu buffer */ addr = kmap_atomic(pages[0]); memcpy(buf, addr + off, sizes[0]); @@ -449,18 +707,20 @@ static void zs_copy_map_object(char *buf, struct page *firstpage, addr = kmap_atomic(pages[1]); memcpy(buf + sizes[0], addr, sizes[1]); kunmap_atomic(addr); +out: + return area->vm_buf; } -static void zs_copy_unmap_object(char *buf, struct page *firstpage, - int off, int size) +static void __zs_unmap_object(struct mapping_area *area, + struct page *pages[2], int off, int size) { - struct page *pages[2]; int sizes[2]; void *addr; + char *buf = area->vm_buf; - pages[0] = firstpage; - pages[1] = get_next_page(firstpage); - BUG_ON(!pages[1]); + /* no write fastpath */ + if (area->vm_mm == ZS_MM_RO) + goto out; sizes[0] = PAGE_SIZE - off; sizes[1] = size - sizes[0]; @@ -473,36 +733,30 @@ static void zs_copy_unmap_object(char *buf, struct page *firstpage, memcpy(addr, buf + sizes[0], sizes[1]); kunmap_atomic(addr); +out: /* enable page faults to match kunmap_atomic() return conditions */ pagefault_enable(); } +#endif /* USE_PGTABLE_MAPPING */ + static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, void *pcpu) { - int cpu = (long)pcpu; + int ret, cpu = (long)pcpu; struct mapping_area *area; switch (action) { case CPU_UP_PREPARE: area = &per_cpu(zs_map_area, cpu); - /* - * Make sure we don't leak memory if a cpu UP notification - * and zs_init() race and both call zs_cpu_up() on the same cpu - */ - if (area->vm_buf) - return 0; - area->vm_buf = (char *)__get_free_page(GFP_KERNEL); - if (!area->vm_buf) - return -ENOMEM; - return 0; + ret = __zs_cpu_up(area); + if (ret) + return notifier_from_errno(ret); break; case CPU_DEAD: case CPU_UP_CANCELED: area = &per_cpu(zs_map_area, cpu); - if (area->vm_buf) - free_page((unsigned long)area->vm_buf); - area->vm_buf = NULL; + __zs_cpu_down(area); break; } @@ -538,14 +792,21 @@ fail: return notifier_to_errno(ret); } -struct zs_pool *zs_create_pool(const char *name, gfp_t flags) +/** + * zs_create_pool - Creates an allocation pool to work from. + * @flags: allocation flags used to allocate pool metadata + * + * This function must be called before anything when using + * the zsmalloc allocator. + * + * On success, a pointer to the newly created pool is returned, + * otherwise NULL. + */ +struct zs_pool *zs_create_pool(gfp_t flags) { int i, ovhd_size; struct zs_pool *pool; - if (!name) - return NULL; - ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); pool = kzalloc(ovhd_size, GFP_KERNEL); if (!pool) @@ -563,12 +824,11 @@ struct zs_pool *zs_create_pool(const char *name, gfp_t flags) class->size = size; class->index = i; spin_lock_init(&class->lock); - class->zspage_order = get_zspage_order(size); + class->pages_per_zspage = get_pages_per_zspage(size); } pool->flags = flags; - pool->name = name; return pool; } @@ -598,18 +858,14 @@ EXPORT_SYMBOL_GPL(zs_destroy_pool); * zs_malloc - Allocate block of given size from pool. * @pool: pool to allocate from * @size: size of block to allocate - * @page: page no. that holds the object - * @offset: location of object within page - * - * On success, identifies block allocated - * and 0 is returned. On failure, is set to - * 0 and -ENOMEM is returned. * + * On success, handle to the allocated object is returned, + * otherwise 0. * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail. */ -void *zs_malloc(struct zs_pool *pool, size_t size) +unsigned long zs_malloc(struct zs_pool *pool, size_t size) { - void *obj; + unsigned long obj; struct link_free *link; int class_idx; struct size_class *class; @@ -618,7 +874,7 @@ void *zs_malloc(struct zs_pool *pool, size_t size) unsigned long m_objidx, m_offset; if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) - return NULL; + return 0; class_idx = get_size_class_index(size); class = &pool->size_class[class_idx]; @@ -631,14 +887,14 @@ void *zs_malloc(struct zs_pool *pool, size_t size) spin_unlock(&class->lock); first_page = alloc_zspage(class, pool->flags); if (unlikely(!first_page)) - return NULL; + return 0; set_zspage_mapping(first_page, class->index, ZS_EMPTY); spin_lock(&class->lock); - class->pages_allocated += class->zspage_order; + class->pages_allocated += class->pages_per_zspage; } - obj = first_page->freelist; + obj = (unsigned long)first_page->freelist; obj_handle_to_location(obj, &m_page, &m_objidx); m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); @@ -657,7 +913,7 @@ void *zs_malloc(struct zs_pool *pool, size_t size) } EXPORT_SYMBOL_GPL(zs_malloc); -void zs_free(struct zs_pool *pool, void *obj) +void zs_free(struct zs_pool *pool, unsigned long obj) { struct link_free *link; struct page *first_page, *f_page; @@ -684,13 +940,13 @@ void zs_free(struct zs_pool *pool, void *obj) + f_offset); link->next = first_page->freelist; kunmap_atomic(link); - first_page->freelist = obj; + first_page->freelist = (void *)obj; first_page->inuse--; fullness = fix_fullness_group(pool, first_page); if (fullness == ZS_EMPTY) - class->pages_allocated -= class->zspage_order; + class->pages_allocated -= class->pages_per_zspage; spin_unlock(&class->lock); @@ -699,7 +955,22 @@ void zs_free(struct zs_pool *pool, void *obj) } EXPORT_SYMBOL_GPL(zs_free); -void *zs_map_object(struct zs_pool *pool, void *handle) +/** + * zs_map_object - get address of allocated object from handle. + * @pool: pool from which the object was allocated + * @handle: handle returned from zs_malloc + * + * Before using an object allocated from zs_malloc, it must be mapped using + * this function. When done with the object, it must be unmapped using + * zs_unmap_object. + * + * Only one object can be mapped per cpu at a time. There is no protection + * against nested mappings. + * + * This function returns with preemption and page faults disabled. +*/ +void *zs_map_object(struct zs_pool *pool, unsigned long handle, + enum zs_mapmode mm) { struct page *page; unsigned long obj_idx, off; @@ -708,27 +979,40 @@ void *zs_map_object(struct zs_pool *pool, void *handle) enum fullness_group fg; struct size_class *class; struct mapping_area *area; + struct page *pages[2]; BUG_ON(!handle); + /* + * Because we use per-cpu mapping areas shared among the + * pools/users, we can't allow mapping in interrupt context + * because it can corrupt another users mappings. + */ + BUG_ON(in_interrupt()); + obj_handle_to_location(handle, &page, &obj_idx); get_zspage_mapping(get_first_page(page), &class_idx, &fg); class = &pool->size_class[class_idx]; off = obj_idx_to_offset(page, obj_idx, class->size); area = &get_cpu_var(zs_map_area); + area->vm_mm = mm; if (off + class->size <= PAGE_SIZE) { /* this object is contained entirely within a page */ area->vm_addr = kmap_atomic(page); return area->vm_addr + off; } - zs_copy_map_object(area->vm_buf, page, off, class->size); - return area->vm_buf; + /* this object spans two pages */ + pages[0] = page; + pages[1] = get_next_page(page); + BUG_ON(!pages[1]); + + return __zs_map_object(area, pages, off, class->size); } EXPORT_SYMBOL_GPL(zs_map_object); -void zs_unmap_object(struct zs_pool *pool, void *handle) +void zs_unmap_object(struct zs_pool *pool, unsigned long handle) { struct page *page; unsigned long obj_idx, off; @@ -748,8 +1032,15 @@ void zs_unmap_object(struct zs_pool *pool, void *handle) area = &__get_cpu_var(zs_map_area); if (off + class->size <= PAGE_SIZE) kunmap_atomic(area->vm_addr); - else - zs_copy_unmap_object(area->vm_buf, page, off, class->size); + else { + struct page *pages[2]; + + pages[0] = page; + pages[1] = get_next_page(page); + BUG_ON(!pages[1]); + + __zs_unmap_object(area, pages, off, class->size); + } put_cpu_var(zs_map_area); } EXPORT_SYMBOL_GPL(zs_unmap_object); diff --git a/drivers/staging/zsmalloc/zsmalloc.h b/drivers/staging/zsmalloc/zsmalloc.h index 949384e..46dbd05 100644 --- a/drivers/staging/zsmalloc/zsmalloc.h +++ b/drivers/staging/zsmalloc/zsmalloc.h @@ -15,16 +15,28 @@ #include +/* + * zsmalloc mapping modes + * + * NOTE: These only make a difference when a mapped object spans pages +*/ +enum zs_mapmode { + ZS_MM_RW, /* normal read-write mapping */ + ZS_MM_RO, /* read-only (no copy-out at unmap time) */ + ZS_MM_WO /* write-only (no copy-in at map time) */ +}; + struct zs_pool; -struct zs_pool *zs_create_pool(const char *name, gfp_t flags); +struct zs_pool *zs_create_pool(gfp_t flags); void zs_destroy_pool(struct zs_pool *pool); -void *zs_malloc(struct zs_pool *pool, size_t size); -void zs_free(struct zs_pool *pool, void *obj); +unsigned long zs_malloc(struct zs_pool *pool, size_t size); +void zs_free(struct zs_pool *pool, unsigned long obj); -void *zs_map_object(struct zs_pool *pool, void *handle); -void zs_unmap_object(struct zs_pool *pool, void *handle); +void *zs_map_object(struct zs_pool *pool, unsigned long handle, + enum zs_mapmode mm); +void zs_unmap_object(struct zs_pool *pool, unsigned long handle); u64 zs_get_total_size_bytes(struct zs_pool *pool); diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h deleted file mode 100644 index a6f3109..0000000 --- a/drivers/staging/zsmalloc/zsmalloc_int.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * zsmalloc memory allocator - * - * Copyright (C) 2011 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the license that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - */ - -#ifndef _ZS_MALLOC_INT_H_ -#define _ZS_MALLOC_INT_H_ - -#include -#include -#include - -/* - * This must be power of 2 and greater than of equal to sizeof(link_free). - * These two conditions ensure that any 'struct link_free' itself doesn't - * span more than 1 page which avoids complex case of mapping 2 pages simply - * to restore link_free pointer values. - */ -#define ZS_ALIGN 8 - -/* - * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single) - * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N. - */ -#define ZS_MAX_ZSPAGE_ORDER 2 -#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) - -/* - * Object location (, ) is encoded as - * as single (void *) handle value. - * - * Note that object index is relative to system - * page it is stored in, so for each sub-page belonging - * to a zspage, obj_idx starts with 0. - * - * This is made more complicated by various memory models and PAE. - */ - -#ifndef MAX_PHYSMEM_BITS -#ifdef CONFIG_HIGHMEM64G -#define MAX_PHYSMEM_BITS 36 -#else /* !CONFIG_HIGHMEM64G */ -/* - * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just - * be PAGE_SHIFT - */ -#define MAX_PHYSMEM_BITS BITS_PER_LONG -#endif -#endif -#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) -#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) -#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) - -#define MAX(a, b) ((a) >= (b) ? (a) : (b)) -/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ -#define ZS_MIN_ALLOC_SIZE \ - MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) -#define ZS_MAX_ALLOC_SIZE PAGE_SIZE - -/* - * On systems with 4K page size, this gives 254 size classes! There is a - * trader-off here: - * - Large number of size classes is potentially wasteful as free page are - * spread across these classes - * - Small number of size classes causes large internal fragmentation - * - Probably its better to use specific size classes (empirically - * determined). NOTE: all those class sizes must be set as multiple of - * ZS_ALIGN to make sure link_free itself never has to span 2 pages. - * - * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN - * (reason above) - */ -#define ZS_SIZE_CLASS_DELTA 16 -#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \ - ZS_SIZE_CLASS_DELTA + 1) - -/* - * We do not maintain any list for completely empty or full pages - */ -enum fullness_group { - ZS_ALMOST_FULL, - ZS_ALMOST_EMPTY, - _ZS_NR_FULLNESS_GROUPS, - - ZS_EMPTY, - ZS_FULL -}; - -/* - * We assign a page to ZS_ALMOST_EMPTY fullness group when: - * n <= N / f, where - * n = number of allocated objects - * N = total number of objects zspage can store - * f = 1/fullness_threshold_frac - * - * Similarly, we assign zspage to: - * ZS_ALMOST_FULL when n > N / f - * ZS_EMPTY when n == 0 - * ZS_FULL when n == N - * - * (see: fix_fullness_group()) - */ -static const int fullness_threshold_frac = 4; - -struct mapping_area { - char *vm_buf; /* copy buffer for objects that span pages */ - char *vm_addr; /* address of kmap_atomic()'ed pages */ -}; - -struct size_class { - /* - * Size of objects stored in this class. Must be multiple - * of ZS_ALIGN. - */ - int size; - unsigned int index; - - /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ - int zspage_order; - - spinlock_t lock; - - /* stats */ - u64 pages_allocated; - - struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS]; -}; - -/* - * Placed within free objects to form a singly linked list. - * For every zspage, first_page->freelist gives head of this list. - * - * This must be power of 2 and less than or equal to ZS_ALIGN - */ -struct link_free { - /* Handle of next free chunk (encodes ) */ - void *next; -}; - -struct zs_pool { - struct size_class size_class[ZS_SIZE_CLASSES]; - - gfp_t flags; /* allocation flags used when growing pool */ - const char *name; -}; - -#endif -- cgit v1.1 From 67a88accdee63629afb4c0645870ac07e38fec28 Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Tue, 12 Nov 2013 17:52:47 -0800 Subject: staging: zsmalloc: Rename page_mapcount_reset The page_mapcount_reset function was renamed from reset_page_mapcount when back-porting zsmalloc from 3.10 kernel. Revert this change to enable compilation of zsmalloc. Change-Id: Iba09fc3f0294dae746d2e2b026e0015711c22f01 Signed-off-by: Olav Haugan --- drivers/staging/zsmalloc/zsmalloc-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index f82f7e6..1d5c6ba 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -472,7 +472,7 @@ static void reset_page(struct page *page) set_page_private(page, 0); page->mapping = NULL; page->freelist = NULL; - page_mapcount_reset(page); + reset_page_mapcount(page); } static void free_zspage(struct page *first_page) -- cgit v1.1 From f27ad129993f7a8d9447b0be4214fd09096144a4 Mon Sep 17 00:00:00 2001 From: nadlabak Date: Fri, 3 Apr 2015 17:56:34 +0200 Subject: staging: zram: Fix zram_make_request type for 3.0 kernel Change-Id: I7a63948ff64e3d2a2279550ad47f61443a77528d --- drivers/staging/zram/zram_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index e34e3fe..62f2db6 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -437,7 +437,7 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) /* * Handler function for all zram I/O requests. */ -static void zram_make_request(struct request_queue *queue, struct bio *bio) +static int zram_make_request(struct request_queue *queue, struct bio *bio) { struct zram *zram = queue->queuedata; @@ -453,11 +453,13 @@ static void zram_make_request(struct request_queue *queue, struct bio *bio) __zram_make_request(zram, bio, bio_data_dir(bio)); up_read(&zram->init_lock); - return; + return 0; error: up_read(&zram->init_lock); bio_io_error(bio); + + return 0; } static void __zram_reset_device(struct zram *zram) -- cgit v1.1 From a8fd5f33bb56fdff90e72a6cfeb820ed9a1af835 Mon Sep 17 00:00:00 2001 From: Marlies Ruck Date: Thu, 16 May 2013 14:30:39 -0400 Subject: Staging: Fixes string split across lines in zram Fixes the following checkpatch warning in zram_drv.c: WARNING: quoted string split across lines Change-Id: Ie74672146a958c2636052f20a6340f67f2641cde Signed-off-by: Marlies Ruck Signed-off-by: Greg Kroah-Hartman Git-commit: 596b3dd4c8e172db7806372c9d0347a4e7d28bc5 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 62f2db6..2161f89 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -304,8 +304,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, handle = zs_malloc(meta->mem_pool, clen); if (!handle) { - pr_info("Error allocating memory for compressed " - "page: %u, size=%zu\n", index, clen); + pr_info("Error allocating memory for compressed page: %u, size=%zu\n", + index, clen); ret = -ENOMEM; goto out; } -- cgit v1.1 From 6e6f9ee5b3554c5a24e542250738543b7d99f447 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:22 +0800 Subject: zram: avoid invalid memory access in zram_exit() Memory for zram->disk object may have already been freed after returning from destroy_device(zram), then it's unsafe for zram_reset_device(zram) to access zram->disk again. We can't solve this bug by flipping the order of destroy_device(zram) and zram_reset_device(zram), that will cause deadlock issues to the zram sysfs handler. So fix it by holding an extra reference to zram->disk before calling destroy_device(zram). Change-Id: I1022247325ab2ec31a094587e13d5c4c759e5a36 Signed-off-by: Jiang Liu Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Git-commit: 6030ea9b35971a4200062f010341ab832e878ac9 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 2161f89..62dc112 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -729,8 +729,10 @@ static void __exit zram_exit(void) for (i = 0; i < num_devices; i++) { zram = &zram_devices[i]; + get_disk(zram->disk); destroy_device(zram); zram_reset_device(zram); + put_disk(zram->disk); } unregister_blkdev(zram_major, "zram"); -- cgit v1.1 From fca3f62f4a4b39ef3d019506fd41b86c3eb1b7cb Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:23 +0800 Subject: zram: use zram->lock to protect zram_free_page() in swap free notify path zram_slot_free_notify() is free-running without any protection from concurrent operations. So there are race conditions between zram_bvec_read()/zram_bvec_write() and zram_slot_free_notify(), and possible consequences include: 1) Trigger BUG_ON(!handle) on zram_bvec_write() side. 2) Access to freed pages on zram_bvec_read() side. 3) Break some fields (bad_compress, good_compress, pages_stored) in zram->stats if the swap layer makes concurrently call to zram_slot_free_notify(). So enhance zram_slot_free_notify() to acquire writer lock on zram->lock before calling zram_free_page(). Change-Id: I805806668d91aaf4c8be7359cb1fd9c877c41f72 Signed-off-by: Jiang Liu Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Git-commit: 57ab048532c0d975538cebd4456491b5c34248f4 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 2 ++ drivers/staging/zram/zram_drv.h | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 62dc112..14e5430 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -584,7 +584,9 @@ static void zram_slot_free_notify(struct block_device *bdev, struct zram *zram; zram = bdev->bd_disk->private_data; + down_write(&zram->lock); zram_free_page(zram, index); + up_write(&zram->lock); zram_stat64_inc(zram, &zram->stats.notify_free); } diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 2d1a3f1..d542eee 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -93,8 +93,9 @@ struct zram_meta { struct zram { struct zram_meta *meta; spinlock_t stat64_lock; /* protect 64-bit stats */ - struct rw_semaphore lock; /* protect compression buffers and table - * against concurrent read and writes */ + struct rw_semaphore lock; /* protect compression buffers, table, + * 32bit stat counters against concurrent + * notifications, reads and writes */ struct request_queue *queue; struct gendisk *disk; int init_done; -- cgit v1.1 From 750c4a7e63dbb0b893680b5ba77add8258a36f56 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:24 +0800 Subject: zram: destroy all devices on error recovery path in zram_init() On error recovery path of zram_init(), it leaks the zram device object causing the failure. So change create_device() to free allocated resources on error path. Change-Id: Ia8508ab9aad3201c6fc12634b8e134d78d7d1423 Signed-off-by: Jiang Liu Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Git-commit: 39a9b8ac9333e4268ecff7da6c9d1ab3823ff243 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 14e5430..ad5e907 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -597,7 +597,7 @@ static const struct block_device_operations zram_devops = { static int create_device(struct zram *zram, int device_id) { - int ret = 0; + int ret = -ENOMEM; init_rwsem(&zram->lock); init_rwsem(&zram->init_lock); @@ -607,7 +607,6 @@ static int create_device(struct zram *zram, int device_id) if (!zram->queue) { pr_err("Error allocating disk queue for device %d\n", device_id); - ret = -ENOMEM; goto out; } @@ -617,11 +616,9 @@ static int create_device(struct zram *zram, int device_id) /* gendisk structure */ zram->disk = alloc_disk(1); if (!zram->disk) { - blk_cleanup_queue(zram->queue); pr_warn("Error allocating disk structure for device %d\n", device_id); - ret = -ENOMEM; - goto out; + goto out_free_queue; } zram->disk->major = zram_major; @@ -650,11 +647,17 @@ static int create_device(struct zram *zram, int device_id) &zram_disk_attr_group); if (ret < 0) { pr_warn("Error creating sysfs group"); - goto out; + goto out_free_disk; } zram->init_done = 0; + return 0; +out_free_disk: + del_gendisk(zram->disk); + put_disk(zram->disk); +out_free_queue: + blk_cleanup_queue(zram->queue); out: return ret; } -- cgit v1.1 From 0a4ee7ec98dfc4e703a0277dffc8b4733a7a8da5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:25 +0800 Subject: zram: avoid double free in function zram_bvec_write() When doing a patial write and the whole page is filled with zero, zram_bvec_write() will free uncmem twice. Change-Id: I233f44846f29dbf11e05d277506137bc7f11bbd9 Signed-off-by: Jiang Liu Acked-by: Minchan Kim Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Git-commit: 65c484609a3b25c35e4edcd5f2c38f98f5226093 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index ad5e907..1088ca4 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -272,8 +272,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (page_zero_filled(uncmem)) { kunmap_atomic(user_mem); - if (is_partial_io(bvec)) - kfree(uncmem); zram->stats.pages_zero++; zram_set_flag(meta, index, ZRAM_ZERO); ret = 0; -- cgit v1.1 From cc346dabd0dc0e84d19acd4a0d34dbf417ffa968 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:26 +0800 Subject: zram: avoid access beyond the zram device Function valid_io_request() should verify the entire request are within the zram device address range. Otherwise it may cause invalid memory access when accessing/modifying zram->meta->table[index] because the 'index' is out of range. Then it may access non-exist memory, randomly modify memory belong to other subsystems, which is hard to track down. Change-Id: Ic6630e8ed5945b7c0f4f63ccc378f9780dfc4567 Signed-off-by: Jiang Liu Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Git-commit: 12a7ad3b810e77137d0caf97a6dd97591e075b30 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 1088ca4..e4bdf0a 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -420,13 +420,20 @@ out: */ static inline int valid_io_request(struct zram *zram, struct bio *bio) { - if (unlikely( - (bio->bi_sector >= (zram->disksize >> SECTOR_SHIFT)) || - (bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)) || - (bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))) { + u64 start, end, bound; + + /* unaligned request */ + if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) + return 0; + if (unlikely(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) + return 0; + start = bio->bi_sector; + end = start + (bio->bi_size >> SECTOR_SHIFT); + bound = zram->disksize >> SECTOR_SHIFT; + /* out of range range */ + if (unlikely(start >= bound || end >= bound || start > end)) return 0; - } /* I/O request is valid */ return 1; -- cgit v1.1 From f25a7b7217133e22d60c47ac424442ab3d6b5469 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:29 +0800 Subject: zram: kill unused zram_get_num_devices() Now there's no caller of zram_get_num_devices(), so kill it. And change zram_devices to static because it's only used in zram_drv.c. Change-Id: Iaf686cf9ae9673325ba481f001df689cedfa2c66 Signed-off-by: Jiang Liu Signed-off-by: Greg Kroah-Hartman Git-commit: 0f0e3ba346c8d8d2cb409b157df79805931a1c2c Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 7 +------ drivers/staging/zram/zram_drv.h | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index e4bdf0a..49997e7 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -37,7 +37,7 @@ /* Globals */ static int zram_major; -struct zram *zram_devices; +static struct zram *zram_devices; /* Module params (documentation at end) */ static unsigned int num_devices = 1; @@ -681,11 +681,6 @@ static void destroy_device(struct zram *zram) blk_cleanup_queue(zram->queue); } -unsigned int zram_get_num_devices(void) -{ - return num_devices; -} - static int __init zram_init(void) { int ret, dev_id; diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index d542eee..b3a315d 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -110,8 +110,6 @@ struct zram { struct zram_stats stats; }; -extern struct zram *zram_devices; -unsigned int zram_get_num_devices(void); #ifdef CONFIG_SYSFS extern struct attribute_group zram_disk_attr_group; #endif -- cgit v1.1 From 996624881997fbbfe6c815a052142bf854e096f6 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:30 +0800 Subject: zram: optimize memory operations with clear_page()/copy_page() Some architectures provides architecture-specific, optimized version of clear_page()/copy_page(), which may have better performance than memset()/memcpy(). So use clear_page()/copy_page() to optimize zram performance if possible. Change-Id: I6b4530f16d45d0005ff498f48b03f727e73044cb Signed-off-by: Jiang Liu Signed-off-by: Greg Kroah-Hartman Git-commit: 42e99bd975fdd24d2bf1a24ebb8b0b42bab8ba65 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 49997e7..491aed2 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -128,23 +128,26 @@ static void zram_free_page(struct zram *zram, size_t index) meta->table[index].size = 0; } +static inline int is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} + static void handle_zero_page(struct bio_vec *bvec) { struct page *page = bvec->bv_page; void *user_mem; user_mem = kmap_atomic(page); - memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); + if (is_partial_io(bvec)) + memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); + else + clear_page(user_mem); kunmap_atomic(user_mem); flush_dcache_page(page); } -static inline int is_partial_io(struct bio_vec *bvec) -{ - return bvec->bv_len != PAGE_SIZE; -} - static int zram_decompress_page(struct zram *zram, char *mem, u32 index) { int ret = LZO_E_OK; @@ -154,13 +157,13 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) unsigned long handle = meta->table[index].handle; if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { - memset(mem, 0, PAGE_SIZE); + clear_page(mem); return 0; } cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); if (meta->table[index].size == PAGE_SIZE) - memcpy(mem, cmem, PAGE_SIZE); + copy_page(mem, cmem); else ret = lzo1x_decompress_safe(cmem, meta->table[index].size, mem, &clen); @@ -309,11 +312,13 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, } cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO); - if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) { src = kmap_atomic(page); - memcpy(cmem, src, clen); - if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + copy_page(cmem, src); kunmap_atomic(src); + } else { + memcpy(cmem, src, clen); + } zs_unmap_object(meta->mem_pool, handle); -- cgit v1.1 From c067ed4a8a5d6bdaf3ce3ac5fc12e88ddce78af3 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:27 +0800 Subject: zram: protect sysfs handler from invalid memory access Use zram->init_lock to protect access to zram->meta, otherwise it may cause invalid memory access if zram->meta has been freed by zram_reset_device(). This issue may be triggered by: Thread 1: while true; do cat mem_used_total; done Thread 2: while true; do echo 8M > disksize; echo 1 > reset; done Change-Id: Id6105b37f083cfd41d9885a251aeef61f72bb92f Signed-off-by: Jiang Liu Acked-by: Minchan Kim Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Git-commit: 5863e10b441e7ea4b492f930f1be180a97d026f3 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_sysfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 68ccad1..55d5281 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -195,8 +195,10 @@ static ssize_t mem_used_total_show(struct device *dev, struct zram *zram = dev_to_zram(dev); struct zram_meta *meta = zram->meta; + down_read(&zram->init_lock); if (zram->init_done) val = zs_get_total_size_bytes(meta->mem_pool); + up_read(&zram->init_lock); return sprintf(buf, "%llu\n", val); } -- cgit v1.1 From f98e3f70b25066daffaa05b23fecaa3514c65f54 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:28 +0800 Subject: zram: simplify and optimize dev_to_zram() Simplify and optimize dev_to_zram() without walking the zram_devices array. Change-Id: I7d2031baabfd880ee8479abf3657358cd7db57cb Signed-off-by: Jiang Liu Signed-off-by: Greg Kroah-Hartman Git-commit: 80de574dca050b734d8413a98a983fba3d06240b Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_sysfs.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 55d5281..17e614d 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -30,18 +30,9 @@ static u64 zram_stat64_read(struct zram *zram, u64 *v) return val; } -static struct zram *dev_to_zram(struct device *dev) +static inline struct zram *dev_to_zram(struct device *dev) { - int i; - struct zram *zram = NULL; - - for (i = 0; i < zram_get_num_devices(); i++) { - zram = &zram_devices[i]; - if (disk_to_dev(zram->disk) == dev) - break; - } - - return zram; + return (struct zram *)dev_to_disk(dev)->private_data; } static ssize_t disksize_show(struct device *dev, -- cgit v1.1 From 5c55bd18449a867a475d8ca4ea28d86b0afd6d9e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 00:07:31 +0800 Subject: zram: use atomic64_xxx() to replace zram_stat64_xxx() Use atomic64_xxx() to replace open-coded zram_stat64_xxx(). Some architectures have native support of atomic64 operations, so we can get rid of the spin_lock() in zram_stat64_xxx(). On the other hand, for platforms use generic version of atomic64 implement, it may cause an extra save/restore of the interrupt flag. So it's a tradeoff. Change-Id: Ic1e5f79328b998ec2d0b571851199dfd2d5560a6 Signed-off-by: Jiang Liu Signed-off-by: Greg Kroah-Hartman Git-commit: da5cc7d338f97886ebf35be92995460289379b73 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 37 ++++++++----------------------------- drivers/staging/zram/zram_drv.h | 19 +++++++++++-------- drivers/staging/zram/zram_sysfs.c | 21 +++++---------------- 3 files changed, 24 insertions(+), 53 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 491aed2..7556b2f 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -42,25 +42,6 @@ static struct zram *zram_devices; /* Module params (documentation at end) */ static unsigned int num_devices = 1; -static void zram_stat64_add(struct zram *zram, u64 *v, u64 inc) -{ - spin_lock(&zram->stat64_lock); - *v = *v + inc; - spin_unlock(&zram->stat64_lock); -} - -static void zram_stat64_sub(struct zram *zram, u64 *v, u64 dec) -{ - spin_lock(&zram->stat64_lock); - *v = *v - dec; - spin_unlock(&zram->stat64_lock); -} - -static void zram_stat64_inc(struct zram *zram, u64 *v) -{ - zram_stat64_add(zram, v, 1); -} - static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { @@ -120,8 +101,7 @@ static void zram_free_page(struct zram *zram, size_t index) if (size <= PAGE_SIZE / 2) zram->stats.good_compress--; - zram_stat64_sub(zram, &zram->stats.compr_size, - meta->table[index].size); + atomic64_sub(meta->table[index].size, &zram->stats.compr_size); zram->stats.pages_stored--; meta->table[index].handle = 0; @@ -172,7 +152,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret != LZO_E_OK)) { pr_err("Decompression failed! err=%d, page=%u\n", ret, index); - zram_stat64_inc(zram, &zram->stats.failed_reads); + atomic64_inc(&zram->stats.failed_reads); return ret; } @@ -326,7 +306,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, meta->table[index].size = clen; /* Update stats */ - zram_stat64_add(zram, &zram->stats.compr_size, clen); + atomic64_add(clen, &zram->stats.compr_size); zram->stats.pages_stored++; if (clen <= PAGE_SIZE / 2) zram->stats.good_compress++; @@ -336,7 +316,7 @@ out: kfree(uncmem); if (ret) - zram_stat64_inc(zram, &zram->stats.failed_writes); + atomic64_inc(&zram->stats.failed_writes); return ret; } @@ -373,10 +353,10 @@ static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) switch (rw) { case READ: - zram_stat64_inc(zram, &zram->stats.num_reads); + atomic64_inc(&zram->stats.num_reads); break; case WRITE: - zram_stat64_inc(zram, &zram->stats.num_writes); + atomic64_inc(&zram->stats.num_writes); break; } @@ -456,7 +436,7 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) goto error; if (!valid_io_request(zram, bio)) { - zram_stat64_inc(zram, &zram->stats.invalid_io); + atomic64_inc(&zram->stats.invalid_io); goto error; } @@ -597,7 +577,7 @@ static void zram_slot_free_notify(struct block_device *bdev, down_write(&zram->lock); zram_free_page(zram, index); up_write(&zram->lock); - zram_stat64_inc(zram, &zram->stats.notify_free); + atomic64_inc(&zram->stats.notify_free); } static const struct block_device_operations zram_devops = { @@ -611,7 +591,6 @@ static int create_device(struct zram *zram, int device_id) init_rwsem(&zram->lock); init_rwsem(&zram->init_lock); - spin_lock_init(&zram->stat64_lock); zram->queue = blk_alloc_queue(GFP_KERNEL); if (!zram->queue) { diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index b3a315d..11b09fc 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -69,14 +69,18 @@ struct table { u8 flags; } __aligned(4); +/* + * All 64bit fields should only be manipulated by 64bit atomic accessors. + * All modifications to 32bit counter should be protected by zram->lock. + */ struct zram_stats { - u64 compr_size; /* compressed size of pages stored */ - u64 num_reads; /* failed + successful */ - u64 num_writes; /* --do-- */ - u64 failed_reads; /* should NEVER! happen */ - u64 failed_writes; /* can happen when memory is too low */ - u64 invalid_io; /* non-page-aligned I/O requests */ - u64 notify_free; /* no. of swap slot free notifications */ + atomic64_t compr_size; /* compressed size of pages stored */ + atomic64_t num_reads; /* failed + successful */ + atomic64_t num_writes; /* --do-- */ + atomic64_t failed_reads; /* should NEVER! happen */ + atomic64_t failed_writes; /* can happen when memory is too low */ + atomic64_t invalid_io; /* non-page-aligned I/O requests */ + atomic64_t notify_free; /* no. of swap slot free notifications */ u32 pages_zero; /* no. of zero filled pages */ u32 pages_stored; /* no. of pages currently stored */ u32 good_compress; /* % of pages with compression ratio<=50% */ @@ -92,7 +96,6 @@ struct zram_meta { struct zram { struct zram_meta *meta; - spinlock_t stat64_lock; /* protect 64-bit stats */ struct rw_semaphore lock; /* protect compression buffers, table, * 32bit stat counters against concurrent * notifications, reads and writes */ diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 17e614d..7b36be1 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -19,17 +19,6 @@ #include "zram_drv.h" -static u64 zram_stat64_read(struct zram *zram, u64 *v) -{ - u64 val; - - spin_lock(&zram->stat64_lock); - val = *v; - spin_unlock(&zram->stat64_lock); - - return val; -} - static inline struct zram *dev_to_zram(struct device *dev) { return (struct zram *)dev_to_disk(dev)->private_data; @@ -123,7 +112,7 @@ static ssize_t num_reads_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - zram_stat64_read(zram, &zram->stats.num_reads)); + (u64)atomic64_read(&zram->stats.num_reads)); } static ssize_t num_writes_show(struct device *dev, @@ -132,7 +121,7 @@ static ssize_t num_writes_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - zram_stat64_read(zram, &zram->stats.num_writes)); + (u64)atomic64_read(&zram->stats.num_writes)); } static ssize_t invalid_io_show(struct device *dev, @@ -141,7 +130,7 @@ static ssize_t invalid_io_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - zram_stat64_read(zram, &zram->stats.invalid_io)); + (u64)atomic64_read(&zram->stats.invalid_io)); } static ssize_t notify_free_show(struct device *dev, @@ -150,7 +139,7 @@ static ssize_t notify_free_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - zram_stat64_read(zram, &zram->stats.notify_free)); + (u64)atomic64_read(&zram->stats.notify_free)); } static ssize_t zero_pages_show(struct device *dev, @@ -176,7 +165,7 @@ static ssize_t compr_data_size_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - zram_stat64_read(zram, &zram->stats.compr_size)); + (u64)atomic64_read(&zram->stats.compr_size)); } static ssize_t mem_used_total_show(struct device *dev, -- cgit v1.1 From 88b340259d42041c6efb9f21bcfd8d2c29fade48 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Sat, 22 Jun 2013 03:21:18 +0300 Subject: zram: remove zram_sysfs file (v2) Move zram sysfs code to zram drv and remove zram_sysfs.c file. This gives ability to make static a number of previously exported zram functions, used from zram sysfs, e.g. internal zram zram_meta_alloc/free(). We also can drop zram_drv wrapper functions, used from zram sysfs: e.g. zram_reset_device()/__zram_reset_device() pair. v2: as suggested by Greg K-H, move MODULE description to the bottom of the file. Change-Id: I2338b812daa1fef0ec7015082ecccb8ec6c84b8f Signed-off-by: Sergey Senozhatsky Signed-off-by: Greg Kroah-Hartman Git-commit: 9b3bb7abcdf2df0f1b2657e6cbc9d06bc2b3b36f Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan Conflicts: drivers/staging/zram/zram_sysfs.c --- drivers/staging/zram/Makefile | 2 +- drivers/staging/zram/zram_drv.c | 516 ++++++++++++++++++++++++++------------ drivers/staging/zram/zram_drv.h | 10 - drivers/staging/zram/zram_sysfs.c | 216 ---------------- 4 files changed, 350 insertions(+), 394 deletions(-) delete mode 100644 drivers/staging/zram/zram_sysfs.c diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile index 7f4a301..cb0f9ce 100644 --- a/drivers/staging/zram/Makefile +++ b/drivers/staging/zram/Makefile @@ -1,3 +1,3 @@ -zram-y := zram_drv.o zram_sysfs.o +zram-y := zram_drv.o obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 7556b2f..658c8fe 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -42,6 +42,104 @@ static struct zram *zram_devices; /* Module params (documentation at end) */ static unsigned int num_devices = 1; +static inline struct zram *dev_to_zram(struct device *dev) +{ + return (struct zram *)dev_to_disk(dev)->private_data; +} + +static ssize_t disksize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", zram->disksize); +} + +static ssize_t initstate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%u\n", zram->init_done); +} + +static ssize_t num_reads_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.num_reads)); +} + +static ssize_t num_writes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.num_writes)); +} + +static ssize_t invalid_io_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.invalid_io)); +} + +static ssize_t notify_free_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.notify_free)); +} + +static ssize_t zero_pages_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%u\n", zram->stats.pages_zero); +} + +static ssize_t orig_data_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)(zram->stats.pages_stored) << PAGE_SHIFT); +} + +static ssize_t compr_data_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.compr_size)); +} + +static ssize_t mem_used_total_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u64 val = 0; + struct zram *zram = dev_to_zram(dev); + struct zram_meta *meta = zram->meta; + + down_read(&zram->init_lock); + if (zram->init_done) + val = zs_get_total_size_bytes(meta->mem_pool); + up_read(&zram->init_lock); + + return sprintf(buf, "%llu\n", val); +} + static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { @@ -60,6 +158,97 @@ static void zram_clear_flag(struct zram_meta *meta, u32 index, meta->table[index].flags &= ~BIT(flag); } +static inline int is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} + +/* + * Check if request is within bounds and aligned on zram logical blocks. + */ +static inline int valid_io_request(struct zram *zram, struct bio *bio) +{ + u64 start, end, bound; + + /* unaligned request */ + if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) + return 0; + if (unlikely(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) + return 0; + + start = bio->bi_sector; + end = start + (bio->bi_size >> SECTOR_SHIFT); + bound = zram->disksize >> SECTOR_SHIFT; + /* out of range range */ + if (unlikely(start >= bound || end >= bound || start > end)) + return 0; + + /* I/O request is valid */ + return 1; +} + +static void zram_meta_free(struct zram_meta *meta) +{ + zs_destroy_pool(meta->mem_pool); + kfree(meta->compress_workmem); + free_pages((unsigned long)meta->compress_buffer, 1); + vfree(meta->table); + kfree(meta); +} + +static struct zram_meta *zram_meta_alloc(u64 disksize) +{ + size_t num_pages; + struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if (!meta) + goto out; + + meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + if (!meta->compress_workmem) + goto free_meta; + + meta->compress_buffer = + (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!meta->compress_buffer) { + pr_err("Error allocating compressor buffer space\n"); + goto free_workmem; + } + + num_pages = disksize >> PAGE_SHIFT; + meta->table = vzalloc(num_pages * sizeof(*meta->table)); + if (!meta->table) { + pr_err("Error allocating zram address table\n"); + goto free_buffer; + } + + meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM); + if (!meta->mem_pool) { + pr_err("Error creating memory pool\n"); + goto free_table; + } + + return meta; + +free_table: + vfree(meta->table); +free_buffer: + free_pages((unsigned long)meta->compress_buffer, 1); +free_workmem: + kfree(meta->compress_workmem); +free_meta: + kfree(meta); + meta = NULL; +out: + return meta; +} + +static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +{ + if (*offset + bvec->bv_len >= PAGE_SIZE) + (*index)++; + *offset = (*offset + bvec->bv_len) % PAGE_SIZE; +} + static int page_zero_filled(void *ptr) { unsigned int pos; @@ -75,6 +264,21 @@ static int page_zero_filled(void *ptr) return 1; } +static void handle_zero_page(struct bio_vec *bvec) +{ + struct page *page = bvec->bv_page; + void *user_mem; + + user_mem = kmap_atomic(page); + if (is_partial_io(bvec)) + memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); + else + clear_page(user_mem); + kunmap_atomic(user_mem); + + flush_dcache_page(page); +} + static void zram_free_page(struct zram *zram, size_t index) { struct zram_meta *meta = zram->meta; @@ -108,26 +312,6 @@ static void zram_free_page(struct zram *zram, size_t index) meta->table[index].size = 0; } -static inline int is_partial_io(struct bio_vec *bvec) -{ - return bvec->bv_len != PAGE_SIZE; -} - -static void handle_zero_page(struct bio_vec *bvec) -{ - struct page *page = bvec->bv_page; - void *user_mem; - - user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) - memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); - else - clear_page(user_mem); - kunmap_atomic(user_mem); - - flush_dcache_page(page); -} - static int zram_decompress_page(struct zram *zram, char *mem, u32 index) { int ret = LZO_E_OK; @@ -338,11 +522,117 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, return ret; } -static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +static void zram_reset_device(struct zram *zram) { - if (*offset + bvec->bv_len >= PAGE_SIZE) - (*index)++; - *offset = (*offset + bvec->bv_len) % PAGE_SIZE; + size_t index; + struct zram_meta *meta; + + if (!zram->init_done) + return; + + meta = zram->meta; + zram->init_done = 0; + + /* Free all pages that are still in this zram device */ + for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { + unsigned long handle = meta->table[index].handle; + if (!handle) + continue; + + zs_free(meta->mem_pool, handle); + } + + zram_meta_free(zram->meta); + zram->meta = NULL; + /* Reset stats */ + memset(&zram->stats, 0, sizeof(zram->stats)); + + zram->disksize = 0; + set_capacity(zram->disk, 0); +} + +static void zram_init_device(struct zram *zram, struct zram_meta *meta) +{ + if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { + pr_info( + "There is little point creating a zram of greater than " + "twice the size of memory since we expect a 2:1 compression " + "ratio. Note that zram uses about 0.1%% of the size of " + "the disk when not in use so a huge zram is " + "wasteful.\n" + "\tMemory Size: %lu kB\n" + "\tSize you selected: %llu kB\n" + "Continuing anyway ...\n", + (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 + ); + } + + /* zram devices sort of resembles non-rotational disks */ + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); + + zram->meta = meta; + zram->init_done = 1; + + pr_debug("Initialization done!\n"); +} + +static ssize_t disksize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 disksize; + struct zram_meta *meta; + struct zram *zram = dev_to_zram(dev); + + disksize = memparse(buf, NULL); + if (!disksize) + return -EINVAL; + + disksize = PAGE_ALIGN(disksize); + meta = zram_meta_alloc(disksize); + down_write(&zram->init_lock); + if (zram->init_done) { + up_write(&zram->init_lock); + zram_meta_free(meta); + pr_info("Cannot change disksize for initialized device\n"); + return -EBUSY; + } + + zram->disksize = disksize; + set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + zram_init_device(zram, meta); + up_write(&zram->init_lock); + + return len; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned short do_reset; + struct zram *zram; + struct block_device *bdev; + + zram = dev_to_zram(dev); + bdev = bdget_disk(zram->disk, 0); + + /* Do not reset an active device! */ + if (bdev->bd_holders) + return -EBUSY; + + ret = kstrtou16(buf, 10, &do_reset); + if (ret) + return ret; + + if (!do_reset) + return -EINVAL; + + /* Make sure all pending I/O is finished */ + if (bdev) + fsync_bdev(bdev); + + zram_reset_device(zram); + return len; } static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) @@ -401,30 +691,6 @@ out: } /* - * Check if request is within bounds and aligned on zram logical blocks. - */ -static inline int valid_io_request(struct zram *zram, struct bio *bio) -{ - u64 start, end, bound; - - /* unaligned request */ - if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) - return 0; - if (unlikely(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) - return 0; - - start = bio->bi_sector; - end = start + (bio->bi_size >> SECTOR_SHIFT); - bound = zram->disksize >> SECTOR_SHIFT; - /* out of range range */ - if (unlikely(start >= bound || end >= bound || start > end)) - return 0; - - /* I/O request is valid */ - return 1; -} - -/* * Handler function for all zram I/O requests. */ static int zram_make_request(struct request_queue *queue, struct bio *bio) @@ -452,122 +718,6 @@ error: return 0; } -static void __zram_reset_device(struct zram *zram) -{ - size_t index; - struct zram_meta *meta; - - if (!zram->init_done) - return; - - meta = zram->meta; - zram->init_done = 0; - - /* Free all pages that are still in this zram device */ - for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { - unsigned long handle = meta->table[index].handle; - if (!handle) - continue; - - zs_free(meta->mem_pool, handle); - } - - zram_meta_free(zram->meta); - zram->meta = NULL; - /* Reset stats */ - memset(&zram->stats, 0, sizeof(zram->stats)); - - zram->disksize = 0; - set_capacity(zram->disk, 0); -} - -void zram_reset_device(struct zram *zram) -{ - down_write(&zram->init_lock); - __zram_reset_device(zram); - up_write(&zram->init_lock); -} - -void zram_meta_free(struct zram_meta *meta) -{ - zs_destroy_pool(meta->mem_pool); - kfree(meta->compress_workmem); - free_pages((unsigned long)meta->compress_buffer, 1); - vfree(meta->table); - kfree(meta); -} - -struct zram_meta *zram_meta_alloc(u64 disksize) -{ - size_t num_pages; - struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); - if (!meta) - goto out; - - meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); - if (!meta->compress_workmem) - goto free_meta; - - meta->compress_buffer = - (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!meta->compress_buffer) { - pr_err("Error allocating compressor buffer space\n"); - goto free_workmem; - } - - num_pages = disksize >> PAGE_SHIFT; - meta->table = vzalloc(num_pages * sizeof(*meta->table)); - if (!meta->table) { - pr_err("Error allocating zram address table\n"); - goto free_buffer; - } - - meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM); - if (!meta->mem_pool) { - pr_err("Error creating memory pool\n"); - goto free_table; - } - - return meta; - -free_table: - vfree(meta->table); -free_buffer: - free_pages((unsigned long)meta->compress_buffer, 1); -free_workmem: - kfree(meta->compress_workmem); -free_meta: - kfree(meta); - meta = NULL; -out: - return meta; -} - -void zram_init_device(struct zram *zram, struct zram_meta *meta) -{ - if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { - pr_info( - "There is little point creating a zram of greater than " - "twice the size of memory since we expect a 2:1 compression " - "ratio. Note that zram uses about 0.1%% of the size of " - "the disk when not in use so a huge zram is " - "wasteful.\n" - "\tMemory Size: %lu kB\n" - "\tSize you selected: %llu kB\n" - "Continuing anyway ...\n", - (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 - ); - } - - /* zram devices sort of resembles non-rotational disks */ - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); - - zram->meta = meta; - zram->init_done = 1; - - pr_debug("Initialization done!\n"); -} - static void zram_slot_free_notify(struct block_device *bdev, unsigned long index) { @@ -585,6 +735,38 @@ static const struct block_device_operations zram_devops = { .owner = THIS_MODULE }; +static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR, + disksize_show, disksize_store); +static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL); +static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); +static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL); +static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL); +static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL); +static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL); +static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL); +static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); +static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL); +static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); + +static struct attribute *zram_disk_attrs[] = { + &dev_attr_disksize.attr, + &dev_attr_initstate.attr, + &dev_attr_reset.attr, + &dev_attr_num_reads.attr, + &dev_attr_num_writes.attr, + &dev_attr_invalid_io.attr, + &dev_attr_notify_free.attr, + &dev_attr_zero_pages.attr, + &dev_attr_orig_data_size.attr, + &dev_attr_compr_data_size.attr, + &dev_attr_mem_used_total.attr, + NULL, +}; + +static struct attribute_group zram_disk_attr_group = { + .attrs = zram_disk_attrs, +}; + static int create_device(struct zram *zram, int device_id) { int ret = -ENOMEM; @@ -730,12 +912,12 @@ static void __exit zram_exit(void) pr_debug("Cleanup done!\n"); } -module_param(num_devices, uint, 0); -MODULE_PARM_DESC(num_devices, "Number of zram devices"); - module_init(zram_init); module_exit(zram_exit); +module_param(num_devices, uint, 0); +MODULE_PARM_DESC(num_devices, "Number of zram devices"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Nitin Gupta "); MODULE_DESCRIPTION("Compressed RAM Block Device"); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 11b09fc..9e57bfb 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -112,14 +112,4 @@ struct zram { struct zram_stats stats; }; - -#ifdef CONFIG_SYSFS -extern struct attribute_group zram_disk_attr_group; -#endif - -extern void zram_reset_device(struct zram *zram); -extern struct zram_meta *zram_meta_alloc(u64 disksize); -extern void zram_meta_free(struct zram_meta *meta); -extern void zram_init_device(struct zram *zram, struct zram_meta *meta); - #endif diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c deleted file mode 100644 index 7b36be1..0000000 --- a/drivers/staging/zram/zram_sysfs.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Compressed RAM block device - * - * Copyright (C) 2008, 2009, 2010 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the licence that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - * - * Project home: http://compcache.googlecode.com/ - */ - -#include -#include -#include -#include - -#include "zram_drv.h" - -static inline struct zram *dev_to_zram(struct device *dev) -{ - return (struct zram *)dev_to_disk(dev)->private_data; -} - -static ssize_t disksize_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", zram->disksize); -} - -static ssize_t disksize_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - u64 disksize; - struct zram_meta *meta; - struct zram *zram = dev_to_zram(dev); - - disksize = memparse(buf, NULL); - if (!disksize) - return -EINVAL; - - disksize = PAGE_ALIGN(disksize); - meta = zram_meta_alloc(disksize); - down_write(&zram->init_lock); - if (zram->init_done) { - up_write(&zram->init_lock); - zram_meta_free(meta); - pr_info("Cannot change disksize for initialized device\n"); - return -EBUSY; - } - - zram->disksize = disksize; - set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); - zram_init_device(zram, meta); - up_write(&zram->init_lock); - - return len; -} - -static ssize_t initstate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%u\n", zram->init_done); -} - -static inline ssize_t initstate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - return 0; -} - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - int ret; - unsigned short do_reset; - struct zram *zram; - struct block_device *bdev; - - zram = dev_to_zram(dev); - bdev = bdget_disk(zram->disk, 0); - - /* Do not reset an active device! */ - if (bdev->bd_holders) - return -EBUSY; - - ret = kstrtou16(buf, 10, &do_reset); - if (ret) - return ret; - - if (!do_reset) - return -EINVAL; - - /* Make sure all pending I/O is finished */ - if (bdev) - fsync_bdev(bdev); - - zram_reset_device(zram); - return len; -} - -static ssize_t num_reads_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_reads)); -} - -static ssize_t num_writes_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_writes)); -} - -static ssize_t invalid_io_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.invalid_io)); -} - -static ssize_t notify_free_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.notify_free)); -} - -static ssize_t zero_pages_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%u\n", zram->stats.pages_zero); -} - -static ssize_t orig_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)(zram->stats.pages_stored) << PAGE_SHIFT); -} - -static ssize_t compr_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.compr_size)); -} - -static ssize_t mem_used_total_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u64 val = 0; - struct zram *zram = dev_to_zram(dev); - struct zram_meta *meta = zram->meta; - - down_read(&zram->init_lock); - if (zram->init_done) - val = zs_get_total_size_bytes(meta->mem_pool); - up_read(&zram->init_lock); - - return sprintf(buf, "%llu\n", val); -} - -static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR, - disksize_show, disksize_store); -static DEVICE_ATTR(initstate, S_IRUGO | S_IWUSR, initstate_show, initstate_store); -static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); -static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL); -static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL); -static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL); -static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL); -static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL); -static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); -static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL); -static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); - -static struct attribute *zram_disk_attrs[] = { - &dev_attr_disksize.attr, - &dev_attr_initstate.attr, - &dev_attr_reset.attr, - &dev_attr_num_reads.attr, - &dev_attr_num_writes.attr, - &dev_attr_invalid_io.attr, - &dev_attr_notify_free.attr, - &dev_attr_zero_pages.attr, - &dev_attr_orig_data_size.attr, - &dev_attr_compr_data_size.attr, - &dev_attr_mem_used_total.attr, - NULL, -}; - -struct attribute_group zram_disk_attr_group = { - .attrs = zram_disk_attrs, -}; -- cgit v1.1 From 843651d740ac98689c1a0055df4f77a271c3d75c Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Sat, 22 Jun 2013 17:21:00 +0300 Subject: zram: allow request end to coincide with disksize Pass valid_io_request() checks if request end coincides with disksize (end equals bound), only fail if we attempt to read beyond the bound. mkfs.ext2 produces numerous errors: [ 2164.632747] quiet_error: 1 callbacks suppressed [ 2164.633260] Buffer I/O error on device zram0, logical block 153599 [ 2164.633265] lost page write due to I/O error on zram0 Change-Id: I71f9f52ec11897d0462d3ff54a853040faf36dcd Signed-off-by: Sergey Senozhatsky Signed-off-by: Greg Kroah-Hartman Git-commit: 75c7caf5a052ffd8db3312fa7864ee2d142890c4 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 658c8fe..5a795ca 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -180,7 +180,7 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) end = start + (bio->bi_size >> SECTOR_SHIFT); bound = zram->disksize >> SECTOR_SHIFT; /* out of range range */ - if (unlikely(start >= bound || end >= bound || start > end)) + if (unlikely(start >= bound || end > bound || start > end)) return 0; /* I/O request is valid */ -- cgit v1.1 From a13825f39ca024eb785b6fcbc0a9f806f89f55ec Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Wed, 26 Jun 2013 15:28:39 +0300 Subject: staging: zram: protect zram_reset_device() call Commit 9b3bb7abcdf2df0f1b2657e6cbc9d06bc2b3b36f (remove zram_sysfs file (v2)) accidentally made zram_reset_device() racy. Protect zram_reset_device() call with zram->lock. Change-Id: I93ce19f9b262584f4ef805dce5ed4de9b3968a30 Signed-off-by: Sergey Senozhatsky Acked-by: Jerome Marchand Signed-off-by: Greg Kroah-Hartman Git-commit: 644d478793c6594277f8ae76954da4ace7ac6f96 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 5a795ca..d4f66e3 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -527,8 +527,11 @@ static void zram_reset_device(struct zram *zram) size_t index; struct zram_meta *meta; - if (!zram->init_done) + down_write(&zram->init_lock); + if (!zram->init_done) { + up_write(&zram->init_lock); return; + } meta = zram->meta; zram->init_done = 0; @@ -549,6 +552,7 @@ static void zram_reset_device(struct zram *zram) zram->disksize = 0; set_capacity(zram->disk, 0); + up_write(&zram->init_lock); } static void zram_init_device(struct zram *zram, struct zram_meta *meta) -- cgit v1.1 From cb42ec818eff1844d66b891fdeb784f3a9b7b25b Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 12 Jul 2013 14:20:52 -0400 Subject: staging: zram: Add auto loading of module if user opens /dev/zram. Greg spotted that said driver is not subscribing to the automagic mechanism of auto-loading if a user tries to open /dev/zram. This fixes it. Change-Id: Ib169a11a1a2717967350015d58d77f431ab5b973 CC: Minchan Kim Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman Git-commit: c70bda992c12e593e411c02a52e4bd6985407539 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index d4f66e3..5b76365 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -925,3 +925,4 @@ MODULE_PARM_DESC(num_devices, "Number of zram devices"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Nitin Gupta "); MODULE_DESCRIPTION("Compressed RAM Block Device"); +MODULE_ALIAS("devname:zram"); -- cgit v1.1 From a0b95fe74c5e5fc02f3749cf3a31ab787014a5d7 Mon Sep 17 00:00:00 2001 From: Sunghan Suh Date: Wed, 3 Jul 2013 20:10:05 +0900 Subject: zram: prevent data loss in error cases of function zram_bvec_write() In function zram_bvec_write(), previous data at the index is already freed by function zram_free_page(). When failed to compress or zs_malloc, there is no way to restore old data. Therefore, free previous data when it's about to update. Also, no need to check whether table is not empty outside of function zram_free_page(), because the function properly checks inside. Change-Id: I8cb3daf146a99d3b5999c7a42e5e2a260f4c3a48 Signed-off-by: Sunghan Suh Signed-off-by: Greg Kroah-Hartman Git-commit: f40ac2ae1b506484dd9261a24bbf3e86b2206ff8 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 5b76365..e74425a 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -418,14 +418,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } - /* - * System overwrites unused sectors. Free memory associated - * with this sector now. - */ - if (meta->table[index].handle || - zram_test_flag(meta, index, ZRAM_ZERO)) - zram_free_page(zram, index); - user_mem = kmap_atomic(page); if (is_partial_io(bvec)) { @@ -439,6 +431,9 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (page_zero_filled(uncmem)) { kunmap_atomic(user_mem); + /* Free memory associated with this sector now. */ + zram_free_page(zram, index); + zram->stats.pages_zero++; zram_set_flag(meta, index, ZRAM_ZERO); ret = 0; @@ -486,6 +481,12 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, zs_unmap_object(meta->mem_pool, handle); + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_free_page(zram, index); + meta->table[index].handle = handle; meta->table[index].size = clen; -- cgit v1.1 From e60cdab903cca0f6ebace03e01a278b6764c9767 Mon Sep 17 00:00:00 2001 From: Kumar Gaurav Date: Thu, 8 Aug 2013 23:53:24 +0530 Subject: Staging: zram: zram_drv.c: Fixed Error of trailing whitespace Fixed by removing trailing whitespace Change-Id: If43b8acf92d4c504c443c8e4c7995de3e410aca0 Signed-off-by: Kumar Gaurav Signed-off-by: Greg Kroah-Hartman Git-commit: a539c72a195c081d950475c2945cb82d80be9b66 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index e74425a..607ab18 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -169,7 +169,7 @@ static inline int is_partial_io(struct bio_vec *bvec) static inline int valid_io_request(struct zram *zram, struct bio *bio) { u64 start, end, bound; - + /* unaligned request */ if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) return 0; -- cgit v1.1 From 36ba49314893622f518852ffd5e9bc0f3b674428 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Mon, 12 Aug 2013 15:13:55 +0900 Subject: zram: fix invalid memory access [1] tried to fix invalid memory access on zram->disk but it didn't fix properly because get_disk failed during module exit path. Actually, we don't need to reset zram->disk's capacity to zero in module exit path so that this patch introduces new argument "reset_capacity" on zram_reset_divice and it only reset it when reset_store is called. [1] 6030ea9b, zram: avoid invalid memory access in zram_exit() Change-Id: I6616ac8f82aab6d03a47f3cfb91d28a825e6e3a6 Cc: Nitin Gupta Cc: Jiang Liu Cc: stable@vger.kernel.org Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman Git-commit: 2b86ab9cc29fcd435cde9378c3b9ffe8b5c76128 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 607ab18..37fc592 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -523,7 +523,7 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, return ret; } -static void zram_reset_device(struct zram *zram) +static void zram_reset_device(struct zram *zram, bool reset_capacity) { size_t index; struct zram_meta *meta; @@ -552,7 +552,8 @@ static void zram_reset_device(struct zram *zram) memset(&zram->stats, 0, sizeof(zram->stats)); zram->disksize = 0; - set_capacity(zram->disk, 0); + if (reset_capacity) + set_capacity(zram->disk, 0); up_write(&zram->init_lock); } @@ -636,7 +637,7 @@ static ssize_t reset_store(struct device *dev, if (bdev) fsync_bdev(bdev); - zram_reset_device(zram); + zram_reset_device(zram, true); return len; } @@ -905,10 +906,12 @@ static void __exit zram_exit(void) for (i = 0; i < num_devices; i++) { zram = &zram_devices[i]; - get_disk(zram->disk); destroy_device(zram); - zram_reset_device(zram); - put_disk(zram->disk); + /* + * Shouldn't access zram->disk after destroy_device + * because destroy_device already released zram->disk. + */ + zram_reset_device(zram, false); } unregister_blkdev(zram_major, "zram"); -- cgit v1.1 From 84fe4a2b991d92deb774c399514612c589ae4eb1 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Mon, 12 Aug 2013 15:13:56 +0900 Subject: zram: don't grab mutex in zram_slot_free_noity [1] introduced down_write in zram_slot_free_notify to prevent race between zram_slot_free_notify and zram_bvec_[read|write]. The race could happen if somebody who has right permission to open swap device is reading swap device while it is used by swap in parallel. However, zram_slot_free_notify is called with holding spin_lock of swap layer so we shouldn't avoid holing mutex. Otherwise, lockdep warns it. This patch adds new list to handle free slot and workqueue so zram_slot_free_notify just registers slot index to be freed and registers the request to workqueue. If workqueue is expired, it holds mutex_lock so there is no problem any more. If any I/O is issued, zram handles pending slot-free request caused by zram_slot_free_notify right before handling issued request because workqueue wouldn't be expired yet so zram I/O request handling function can miss it. Lastly, when zram is reset, flush_work could handle all of pending free request so we shouldn't have memory leak. NOTE: If zram_slot_free_notify's kmalloc with GFP_ATOMIC would be failed, the slot will be freed when next write I/O write the slot. [1] [57ab0485, zram: use zram->lock to protect zram_free_page() in swap free notify path] * from v2 * refactoring * from v1 * totally redesign Change-Id: Ic69dce098c89bb7cb5563566b802375320b90a76 Cc: Nitin Gupta Cc: Jiang Liu Cc: stable@vger.kernel.org Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman Git-commit: a0c516cbfc7452c8cbd564525fef66d9f20b46d1 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 60 ++++++++++++++++++++++++++++++++++++++--- drivers/staging/zram/zram_drv.h | 10 +++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 37fc592..0cf058a 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -440,6 +440,14 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } + /* + * zram_slot_free_notify could miss free so that let's + * double check. + */ + if (unlikely(meta->table[index].handle || + zram_test_flag(meta, index, ZRAM_ZERO))) + zram_free_page(zram, index); + ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, meta->compress_workmem); @@ -505,6 +513,20 @@ out: return ret; } +static void handle_pending_slot_free(struct zram *zram) +{ + struct zram_slot_free *free_rq; + + spin_lock(&zram->slot_free_lock); + while (zram->slot_free_rq) { + free_rq = zram->slot_free_rq; + zram->slot_free_rq = free_rq->next; + zram_free_page(zram, free_rq->index); + kfree(free_rq); + } + spin_unlock(&zram->slot_free_lock); +} + static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, int offset, struct bio *bio, int rw) { @@ -512,10 +534,12 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, if (rw == READ) { down_read(&zram->lock); + handle_pending_slot_free(zram); ret = zram_bvec_read(zram, bvec, index, offset, bio); up_read(&zram->lock); } else { down_write(&zram->lock); + handle_pending_slot_free(zram); ret = zram_bvec_write(zram, bvec, index, offset); up_write(&zram->lock); } @@ -528,6 +552,8 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) size_t index; struct zram_meta *meta; + flush_work(&zram->free_work); + down_write(&zram->init_lock); if (!zram->init_done) { up_write(&zram->init_lock); @@ -724,16 +750,40 @@ error: return 0; } +static void zram_slot_free(struct work_struct *work) +{ + struct zram *zram; + + zram = container_of(work, struct zram, free_work); + down_write(&zram->lock); + handle_pending_slot_free(zram); + up_write(&zram->lock); +} + +static void add_slot_free(struct zram *zram, struct zram_slot_free *free_rq) +{ + spin_lock(&zram->slot_free_lock); + free_rq->next = zram->slot_free_rq; + zram->slot_free_rq = free_rq; + spin_unlock(&zram->slot_free_lock); +} + static void zram_slot_free_notify(struct block_device *bdev, unsigned long index) { struct zram *zram; + struct zram_slot_free *free_rq; zram = bdev->bd_disk->private_data; - down_write(&zram->lock); - zram_free_page(zram, index); - up_write(&zram->lock); atomic64_inc(&zram->stats.notify_free); + + free_rq = kmalloc(sizeof(struct zram_slot_free), GFP_ATOMIC); + if (!free_rq) + return; + + free_rq->index = index; + add_slot_free(zram, free_rq); + schedule_work(&zram->free_work); } static const struct block_device_operations zram_devops = { @@ -780,6 +830,10 @@ static int create_device(struct zram *zram, int device_id) init_rwsem(&zram->lock); init_rwsem(&zram->init_lock); + INIT_WORK(&zram->free_work, zram_slot_free); + spin_lock_init(&zram->slot_free_lock); + zram->slot_free_rq = NULL; + zram->queue = blk_alloc_queue(GFP_KERNEL); if (!zram->queue) { pr_err("Error allocating disk queue for device %d\n", diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 9e57bfb..97a3acf 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -94,11 +94,20 @@ struct zram_meta { struct zs_pool *mem_pool; }; +struct zram_slot_free { + unsigned long index; + struct zram_slot_free *next; +}; + struct zram { struct zram_meta *meta; struct rw_semaphore lock; /* protect compression buffers, table, * 32bit stat counters against concurrent * notifications, reads and writes */ + + struct work_struct free_work; /* handle pending free request */ + struct zram_slot_free *slot_free_rq; /* list head of free request */ + struct request_queue *queue; struct gendisk *disk; int init_done; @@ -109,6 +118,7 @@ struct zram { * we can store in a disk. */ u64 disksize; /* bytes */ + spinlock_t slot_free_lock; struct zram_stats stats; }; -- cgit v1.1 From 875fae5428c584fefab66accdf1ddf388b9ebd4f Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Fri, 1 Nov 2013 17:08:26 -0700 Subject: zram: Disable allocation failure logging Disable the logging of errors when allocations fail for zram pages. This avoid excessive logging when system is very low on memory. Change-Id: Ifabcf8f1b9c3e3717599d6a0a924f7b2061f00ea Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 0cf058a..72fbf05 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -221,7 +221,8 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) goto free_buffer; } - meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM); + meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM | + __GFP_NOWARN); if (!meta->mem_pool) { pr_err("Error creating memory pool\n"); goto free_table; -- cgit v1.1 From c32d11d7bfc35f379da6983537bc221c27bd7b26 Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Thu, 5 Sep 2013 09:17:58 -0700 Subject: zram: Change ratio to 90% for bad compress Change the ratio for determining whether or not we should store pages as uncompressed in zram. This will allow zram to fit more data since more of the pages will be stored as compressed. Change-Id: I37170cafff7e8a4cc44f1622fe52a6cbff85f218 Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 97a3acf..508a19f 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -32,7 +32,7 @@ static const unsigned max_num_devices = 32; * Pages that compress to size greater than this are stored * uncompressed in memory. */ -static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; +static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; /* * NOTE: max_zpage_size must be less than or equal to: -- cgit v1.1 From b8a2bd16924eebba1f5cf10a54c20eac981fa3cd Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Thu, 31 Oct 2013 17:53:41 -0700 Subject: staging: zram: Rate limit memory allocation errors If an error occurs allocating memory for zram we will not be able to fullfill the request to store the page in zram. The swap subsystem still continues to try to swap out pages to zram even when this error occurs since there is currently no facility to stop the swap subsystem from swapping out during such errors. This can cause the system to be overflowed with logging errors. Reduce the amount of logging to prevent the kernel log from being filled with these error messages. Change-Id: I54b920337749ece59d9ca78fa8b29345ec7b976b Signed-off-by: Olav Haugan --- drivers/staging/zram/zram_drv.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 72fbf05..6063c11 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "zram_drv.h" @@ -39,6 +40,12 @@ static int zram_major; static struct zram *zram_devices; +/* + * We don't need to see memory allocation errors more than once every 1 + * second to know that a problem is occurring. + */ +#define ALLOC_ERROR_LOG_RATE_MS 1000 + /* Module params (documentation at end) */ static unsigned int num_devices = 1; @@ -400,6 +407,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, struct page *page; unsigned char *user_mem, *cmem, *src, *uncmem = NULL; struct zram_meta *meta = zram->meta; + static unsigned long zram_rs_time; page = bvec->bv_page; src = meta->compress_buffer; @@ -473,8 +481,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, handle = zs_malloc(meta->mem_pool, clen); if (!handle) { - pr_info("Error allocating memory for compressed page: %u, size=%zu\n", - index, clen); + if (printk_timed_ratelimit(&zram_rs_time, + ALLOC_ERROR_LOG_RATE_MS)) + pr_info("Error allocating memory for compressed page: %u, size=%zu\n", + index, clen); ret = -ENOMEM; goto out; } -- cgit v1.1 From 6507ab80f9bffeec6d122d3101584b0e77035e01 Mon Sep 17 00:00:00 2001 From: Marlies Ruck Date: Wed, 15 May 2013 16:56:49 -0400 Subject: Staging: Fixes string split across lines in zsmalloc zsmalloc-main Fixes the following checkpatch warning: WARNING: quoted string split across lines Change-Id: Ia192b2d0213de838d61f77db233169c802a4419f Signed-off-by: Marlies Ruck Signed-off-by: Greg Kroah-Hartman Git-commit: 93ad5ab50476aa7e2b33aac31f41d0efc9f729d7 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zsmalloc/zsmalloc-main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 1d5c6ba..84ef48f9 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -844,8 +844,7 @@ void zs_destroy_pool(struct zs_pool *pool) for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { if (class->fullness_list[fg]) { - pr_info("Freeing non-empty class with size " - "%db, fullness group %d\n", + pr_info("Freeing non-empty class with size %db, fullness group %d\n", class->size, fg); } } -- cgit v1.1 From 210b418775c845cd6b8116c7b144b7b36f9c90fb Mon Sep 17 00:00:00 2001 From: Sara Bird Date: Mon, 20 May 2013 15:18:14 -0400 Subject: staging/zsmalloc: Fixed up incorrect formatted comments The existing comments are using an odd style. Fixed them up to adhere to the StyleGuide. No code changes. Change-Id: I24a720787c00a79883cb268ebf1257b525655f7d Signed-off-by: Sara Bird Signed-off-by: Greg Kroah-Hartman Git-commit: 396b7fd6f9668c04f20ee6daca3054f5c5ec1056 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zsmalloc/zsmalloc-main.c | 4 ++-- drivers/staging/zsmalloc/zsmalloc.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 84ef48f9..e5bdb26 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -224,7 +224,7 @@ struct zs_pool { * performs VM mapping faster than copying, then it should be added here * so that USE_PGTABLE_MAPPING is defined. This causes zsmalloc to use * page table mapping rather than copying for object mapping. -*/ + */ #if defined(CONFIG_ARM) && !defined(MODULE) #define USE_PGTABLE_MAPPING #endif @@ -967,7 +967,7 @@ EXPORT_SYMBOL_GPL(zs_free); * against nested mappings. * * This function returns with preemption and page faults disabled. -*/ + */ void *zs_map_object(struct zs_pool *pool, unsigned long handle, enum zs_mapmode mm) { diff --git a/drivers/staging/zsmalloc/zsmalloc.h b/drivers/staging/zsmalloc/zsmalloc.h index 46dbd05..fbe6bec 100644 --- a/drivers/staging/zsmalloc/zsmalloc.h +++ b/drivers/staging/zsmalloc/zsmalloc.h @@ -19,7 +19,7 @@ * zsmalloc mapping modes * * NOTE: These only make a difference when a mapped object spans pages -*/ + */ enum zs_mapmode { ZS_MM_RW, /* normal read-write mapping */ ZS_MM_RO, /* read-only (no copy-out at unmap time) */ -- cgit v1.1 From 195db76c72c596469fc6a194f968ccec33f1de09 Mon Sep 17 00:00:00 2001 From: Sunghan Suh Date: Fri, 12 Jul 2013 16:08:13 +0900 Subject: staging: zsmalloc: access page->private by using page_private macro Change-Id: Ia09720c7787bc03c69d01874437bdc619cae8c7e Signed-off-by: Sunghan Suh Signed-off-by: Greg Kroah-Hartman Git-commit: e842b976a88a39b447fc34bd0fcb3c0be0a1d9d9 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Olav Haugan --- drivers/staging/zsmalloc/zsmalloc-main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index e5bdb26..523b937 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -423,7 +423,7 @@ static struct page *get_next_page(struct page *page) if (is_last_page(page)) next = NULL; else if (is_first_page(page)) - next = (struct page *)page->private; + next = (struct page *)page_private(page); else next = list_entry(page->lru.next, struct page, lru); @@ -581,7 +581,7 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags) first_page->inuse = 0; } if (i == 1) - first_page->private = (unsigned long)page; + set_page_private(first_page, (unsigned long)page); if (i >= 1) page->first_page = first_page; if (i >= 2) -- cgit v1.1 From 2ee074b5fc854501be8d25fe4cae23402e5d1812 Mon Sep 17 00:00:00 2001 From: Olav Haugan Date: Mon, 4 Nov 2013 15:51:51 -0800 Subject: staging: zsmalloc: Ensure handle is never 0 on success zsmalloc encodes a handle using the pfn and an object index. On hardware platforms with physical memory starting at 0x0 the pfn can be 0. This causes the encoded handle to be 0 and is incorrectly interpreted as an allocation failure. This issue affects all current and future SoCs with physical memory starting at 0x0. All MSM8974 SoCs which includes Google Nexus 5 devices are affected. To prevent this false error we ensure that the encoded handle will not be 0 when allocation succeeds. Change-Id: I5ad31712be4dd5105ebee81fa95927039c0f6935 Signed-off-by: Olav Haugan --- drivers/staging/zsmalloc/zsmalloc-main.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 523b937..41a6803 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -430,7 +430,12 @@ static struct page *get_next_page(struct page *page) return next; } -/* Encode as a single handle value */ +/* + * Encode as a single handle value. + * On hardware platforms with physical memory starting at 0x0 the pfn + * could be 0 so we ensure that the handle will never be 0 by adjusting the + * encoded obj_idx value before encoding. + */ static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) { unsigned long handle; @@ -441,17 +446,21 @@ static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) } handle = page_to_pfn(page) << OBJ_INDEX_BITS; - handle |= (obj_idx & OBJ_INDEX_MASK); + handle |= ((obj_idx + 1) & OBJ_INDEX_MASK); return (void *)handle; } -/* Decode pair from the given object handle */ +/* + * Decode pair from the given object handle. We adjust the + * decoded obj_idx back to its original value since it was adjusted in + * obj_location_to_handle(). + */ static void obj_handle_to_location(unsigned long handle, struct page **page, unsigned long *obj_idx) { *page = pfn_to_page(handle >> OBJ_INDEX_BITS); - *obj_idx = handle & OBJ_INDEX_MASK; + *obj_idx = (handle & OBJ_INDEX_MASK) - 1; } static unsigned long obj_idx_to_offset(struct page *page, -- cgit v1.1 From 7dd2395665340ac6d0c9632ffa58a98685ed50e7 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 11 Dec 2013 11:04:36 +0900 Subject: zsmalloc: add Kconfig for enabling page table method Zsmalloc has two methods 1) copy-based and 2) pte based to access objects that span two pages. You can see history why we supported two approach from [1]. But it was bad choice that adding hard coding to select arch which want to use pte based method because there are lots of SoC in an architecure and they can have different cache size, CPU speed and so on so it would be better to expose it to user as selectable Kconfig option like Andrew Morton suggested. [1] https://lkml.org/lkml/2012/7/11/58 Acked-by: Nitin Gupta Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/Kconfig | 13 +++++++++++++ drivers/staging/zsmalloc/zsmalloc-main.c | 19 ++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig index 7fab032..e75611a 100644 --- a/drivers/staging/zsmalloc/Kconfig +++ b/drivers/staging/zsmalloc/Kconfig @@ -8,3 +8,16 @@ config ZSMALLOC non-standard allocator interface where a handle, not a pointer, is returned by an alloc(). This handle must be mapped in order to access the allocated space. + +config PGTABLE_MAPPING + bool "Use page table mapping to access object in zsmalloc" + depends on ZSMALLOC + help + By default, zsmalloc uses a copy-based object mapping method to + access allocations that span two pages. However, if a particular + architecture (ex, ARM) performs VM mapping faster than copying, + then you should select this. This causes zsmalloc to use page table + mapping rather than copying for object mapping. + + You can check speed with zsmalloc benchmark[1]. + [1] https://github.com/spartacus06/zsmalloc diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 41a6803..8e8af0a 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -218,19 +218,8 @@ struct zs_pool { #define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) #define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) -/* - * By default, zsmalloc uses a copy-based object mapping method to access - * allocations that span two pages. However, if a particular architecture - * performs VM mapping faster than copying, then it should be added here - * so that USE_PGTABLE_MAPPING is defined. This causes zsmalloc to use - * page table mapping rather than copying for object mapping. - */ -#if defined(CONFIG_ARM) && !defined(MODULE) -#define USE_PGTABLE_MAPPING -#endif - struct mapping_area { -#ifdef USE_PGTABLE_MAPPING +#ifdef CONFIG_PGTABLE_MAPPING struct vm_struct *vm; /* vm area for mapping object that span pages */ #else char *vm_buf; /* copy buffer for objects that span pages */ @@ -631,7 +620,7 @@ static struct page *find_get_zspage(struct size_class *class) return page; } -#ifdef USE_PGTABLE_MAPPING +#ifdef CONFIG_PGTABLE_MAPPING static inline int __zs_cpu_up(struct mapping_area *area) { /* @@ -669,7 +658,7 @@ static inline void __zs_unmap_object(struct mapping_area *area, unmap_kernel_range(addr, PAGE_SIZE * 2); } -#else /* USE_PGTABLE_MAPPING */ +#else /* CONFIG_PGTABLE_MAPPING */ static inline int __zs_cpu_up(struct mapping_area *area) { @@ -747,7 +736,7 @@ out: pagefault_enable(); } -#endif /* USE_PGTABLE_MAPPING */ +#endif /* CONFIG_PGTABLE_MAPPING */ static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, void *pcpu) -- cgit v1.1 From 4b85df5990d1cd6cf04ca48e54a711774081c657 Mon Sep 17 00:00:00 2001 From: Nitin Cupta Date: Wed, 11 Dec 2013 11:04:37 +0900 Subject: zsmalloc: add more comment This patch adds lots of comments and it will help others to review and enhance. Signed-off-by: Seth Jennings Signed-off-by: Nitin Gupta Signed-off-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zsmalloc/zsmalloc-main.c | 66 +++++++++++++++++++++++++++----- drivers/staging/zsmalloc/zsmalloc.h | 9 ++++- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c index 8e8af0a..07186ab 100644 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ b/drivers/staging/zsmalloc/zsmalloc-main.c @@ -10,16 +10,14 @@ * Released under the terms of GNU General Public License Version 2.0 */ - /* - * This allocator is designed for use with zcache and zram. Thus, the - * allocator is supposed to work well under low memory conditions. In - * particular, it never attempts higher order page allocation which is - * very likely to fail under memory pressure. On the other hand, if we - * just use single (0-order) pages, it would suffer from very high - * fragmentation -- any object of size PAGE_SIZE/2 or larger would occupy - * an entire page. This was one of the major issues with its predecessor - * (xvmalloc). + * This allocator is designed for use with zram. Thus, the allocator is + * supposed to work well under low memory conditions. In particular, it + * never attempts higher order page allocation which is very likely to + * fail under memory pressure. On the other hand, if we just use single + * (0-order) pages, it would suffer from very high fragmentation -- + * any object of size PAGE_SIZE/2 or larger would occupy an entire page. + * This was one of the major issues with its predecessor (xvmalloc). * * To overcome these issues, zsmalloc allocates a bunch of 0-order pages * and links them together using various 'struct page' fields. These linked @@ -27,6 +25,21 @@ * page boundaries. The code refers to these linked pages as a single entity * called zspage. * + * For simplicity, zsmalloc can only allocate objects of size up to PAGE_SIZE + * since this satisfies the requirements of all its current users (in the + * worst case, page is incompressible and is thus stored "as-is" i.e. in + * uncompressed form). For allocation requests larger than this size, failure + * is returned (see zs_malloc). + * + * Additionally, zs_malloc() does not return a dereferenceable pointer. + * Instead, it returns an opaque handle (unsigned long) which encodes actual + * location of the allocated object. The reason for this indirection is that + * zsmalloc does not keep zspages permanently mapped since that would cause + * issues on 32-bit systems where the VA region for kernel space mappings + * is very small. So, before using the allocating memory, the object has to + * be mapped using zs_map_object() to get a usable pointer and subsequently + * unmapped using zs_unmap_object(). + * * Following is how we use various fields and flags of underlying * struct page(s) to form a zspage. * @@ -98,7 +111,7 @@ /* * Object location (, ) is encoded as - * as single (void *) handle value. + * as single (unsigned long) handle value. * * Note that object index is relative to system * page it is stored in, so for each sub-page belonging @@ -264,6 +277,13 @@ static void set_zspage_mapping(struct page *page, unsigned int class_idx, page->mapping = (struct address_space *)m; } +/* + * zsmalloc divides the pool into various size classes where each + * class maintains a list of zspages where each zspage is divided + * into equal sized chunks. Each allocation falls into one of these + * classes depending on its size. This function returns index of the + * size class which has chunk size big enough to hold the give size. + */ static int get_size_class_index(int size) { int idx = 0; @@ -275,6 +295,13 @@ static int get_size_class_index(int size) return idx; } +/* + * For each size class, zspages are divided into different groups + * depending on how "full" they are. This was done so that we could + * easily find empty or nearly empty zspages when we try to shrink + * the pool (not yet implemented). This function returns fullness + * status of the given page. + */ static enum fullness_group get_fullness_group(struct page *page) { int inuse, max_objects; @@ -296,6 +323,12 @@ static enum fullness_group get_fullness_group(struct page *page) return fg; } +/* + * Each size class maintains various freelists and zspages are assigned + * to one of these freelists based on the number of live objects they + * have. This functions inserts the given zspage into the freelist + * identified by . + */ static void insert_zspage(struct page *page, struct size_class *class, enum fullness_group fullness) { @@ -313,6 +346,10 @@ static void insert_zspage(struct page *page, struct size_class *class, *head = page; } +/* + * This function removes the given zspage from the freelist identified + * by . + */ static void remove_zspage(struct page *page, struct size_class *class, enum fullness_group fullness) { @@ -334,6 +371,15 @@ static void remove_zspage(struct page *page, struct size_class *class, list_del_init(&page->lru); } +/* + * Each size class maintains zspages in different fullness groups depending + * on the number of live objects they contain. When allocating or freeing + * objects, the fullness status of the page can change, say, from ALMOST_FULL + * to ALMOST_EMPTY when freeing an object. This function checks if such + * a status change has occurred for the given page and accordingly moves the + * page from the freelist of the old fullness group to that of the new + * fullness group. + */ static enum fullness_group fix_fullness_group(struct zs_pool *pool, struct page *page) { diff --git a/drivers/staging/zsmalloc/zsmalloc.h b/drivers/staging/zsmalloc/zsmalloc.h index fbe6bec..c2eb174 100644 --- a/drivers/staging/zsmalloc/zsmalloc.h +++ b/drivers/staging/zsmalloc/zsmalloc.h @@ -18,12 +18,19 @@ /* * zsmalloc mapping modes * - * NOTE: These only make a difference when a mapped object spans pages + * NOTE: These only make a difference when a mapped object spans pages. + * They also have no effect when PGTABLE_MAPPING is selected. */ enum zs_mapmode { ZS_MM_RW, /* normal read-write mapping */ ZS_MM_RO, /* read-only (no copy-out at unmap time) */ ZS_MM_WO /* write-only (no copy-in at map time) */ + /* + * NOTE: ZS_MM_WO should only be used for initializing new + * (uninitialized) allocations. Partial writes to already + * initialized allocations should use ZS_MM_RW to preserve the + * existing data. + */ }; struct zs_pool; -- cgit v1.1 From a491dd7878acecaf69e94131f2834afd72e64f19 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 12 Sep 2013 15:41:31 -0700 Subject: Revert "staging: zram: Add auto loading of module if user opens /dev/zram." This reverts commit c70bda992c12e593e411c02a52e4bd6985407539. It's incorrect, Kay writes: Please just remove it. "devname" is meant to be used for single-instance devices with a static dev_t, never for things like zramX. It will not do anything useful here, it does nothing really without a statically assigned dev_t, and it should not be used for devices of this kind anyway. Reported-by: Tom Gundersen Reported-by: Kay Sievers Cc: Minchan Kim Cc: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 6063c11..91fe3a1 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -994,4 +994,3 @@ MODULE_PARM_DESC(num_devices, "Number of zram devices"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Nitin Gupta "); MODULE_DESCRIPTION("Compressed RAM Block Device"); -MODULE_ALIAS("devname:zram"); -- cgit v1.1 From bae6d9d2927a7bc7f0531c5d55f411cabc5f57cc Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Wed, 30 Oct 2013 18:43:32 +0530 Subject: Staging: zram: Fix variable dereferenced before check This patch fixes the following Smatch warning in zram_drv.c- drivers/staging/zram/zram_drv.c:899 destroy_device() warn: variable dereferenced before check 'zram->disk' (see line 896) Acked-by: Minchan Kim Acked-by: Jerome Marchand Signed-off-by: Rashika Kheria Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 91fe3a1..06f217e 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -909,13 +909,10 @@ static void destroy_device(struct zram *zram) sysfs_remove_group(&disk_to_dev(zram->disk)->kobj, &zram_disk_attr_group); - if (zram->disk) { - del_gendisk(zram->disk); - put_disk(zram->disk); - } + del_gendisk(zram->disk); + put_disk(zram->disk); - if (zram->queue) - blk_cleanup_queue(zram->queue); + blk_cleanup_queue(zram->queue); } static int __init zram_init(void) -- cgit v1.1 From 895ec41cfb21cb9983209689da392e41d1390efb Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Wed, 30 Oct 2013 18:36:32 +0530 Subject: Staging: zram: Fix access of NULL pointer This patch fixes the bug in reset_store caused by accessing NULL pointer. The bdev gets its value from bdget_disk() which could fail when memory pressure is severe and hence can return NULL because allocation of inode in bdget could fail. Hence, this patch introduces a check for bdev to prevent reference to a NULL pointer in the later part of the code. It also removes unnecessary check of bdev for fsync_bdev(). Cc: stable Acked-by: Jerome Marchand Signed-off-by: Rashika Kheria Acked-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 06f217e..5c56b38 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -659,6 +659,9 @@ static ssize_t reset_store(struct device *dev, zram = dev_to_zram(dev); bdev = bdget_disk(zram->disk, 0); + if (!bdev) + return -ENOMEM; + /* Do not reset an active device! */ if (bdev->bd_holders) return -EBUSY; @@ -671,8 +674,7 @@ static ssize_t reset_store(struct device *dev, return -EINVAL; /* Make sure all pending I/O is finished */ - if (bdev) - fsync_bdev(bdev); + fsync_bdev(bdev); zram_reset_device(zram, true); return len; -- cgit v1.1 From 888aed97ac127d477527dc06b5ce9259cf509e13 Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Sun, 10 Nov 2013 22:13:53 +0530 Subject: Staging: zram: Fix memory leak by refcount mismatch As suggested by Minchan Kim and Jerome Marchand "The code in reset_store get the block device (bdget_disk()) but it does not put it (bdput()) when it's done using it. The usage count is therefore incremented but never decremented." This patch also puts bdput() for all error cases. Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: stable@vger.kernel.org Signed-off-by: Rashika Kheria Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 5c56b38..d22e5ad 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -663,21 +663,30 @@ static ssize_t reset_store(struct device *dev, return -ENOMEM; /* Do not reset an active device! */ - if (bdev->bd_holders) - return -EBUSY; + if (bdev->bd_holders) { + ret = -EBUSY; + goto out; + } ret = kstrtou16(buf, 10, &do_reset); if (ret) - return ret; + goto out; - if (!do_reset) - return -EINVAL; + if (!do_reset) { + ret = -EINVAL; + goto out; + } /* Make sure all pending I/O is finished */ fsync_bdev(bdev); + bdput(bdev); zram_reset_device(zram, true); return len; + +out: + bdput(bdev); + return ret; } static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) -- cgit v1.1 From 286588f748a4ecfe52cd671af74bf7c0285293f0 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:45:50 -0800 Subject: zsmalloc: move it under mm This patch moves zsmalloc under mm directory. Before that, description will explain why we have needed custom allocator. Zsmalloc is a new slab-based memory allocator for storing compressed pages. It is designed for low fragmentation and high allocation success rate on large object, but <= PAGE_SIZE allocations. zsmalloc differs from the kernel slab allocator in two primary ways to achieve these design goals. zsmalloc never requires high order page allocations to back slabs, or "size classes" in zsmalloc terms. Instead it allows multiple single-order pages to be stitched together into a "zspage" which backs the slab. This allows for higher allocation success rate under memory pressure. Also, zsmalloc allows objects to span page boundaries within the zspage. This allows for lower fragmentation than could be had with the kernel slab allocator for objects between PAGE_SIZE/2 and PAGE_SIZE. With the kernel slab allocator, if a page compresses to 60% of it original size, the memory savings gained through compression is lost in fragmentation because another object of the same size can't be stored in the leftover space. This ability to span pages results in zsmalloc allocations not being directly addressable by the user. The user is given an non-dereferencable handle in response to an allocation request. That handle must be mapped, using zs_map_object(), which returns a pointer to the mapped region that can be used. The mapping is necessary since the object data may reside in two different noncontigious pages. The zsmalloc fulfills the allocation needs for zram perfectly [sjenning@linux.vnet.ibm.com: borrow Seth's quote] Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Reviewed-by: Konrad Rzeszutek Wilk Cc: Bob Liu Cc: Greg Kroah-Hartman Cc: Hugh Dickins Cc: Jens Axboe Cc: Luigi Semenzato Cc: Mel Gorman Cc: Pekka Enberg Cc: Rik van Riel Cc: Seth Jennings Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Change-Id: Ib026c17143131089494dc394c4a35e230220ec83 Conflicts: drivers/staging/Kconfig drivers/staging/Makefile Conflicts: mm/Kconfig mm/Makefile --- drivers/staging/zram/zram_drv.h | 3 +- drivers/staging/zsmalloc/Kconfig | 23 - drivers/staging/zsmalloc/Makefile | 3 - drivers/staging/zsmalloc/zsmalloc-main.c | 1107 ------------------------------ drivers/staging/zsmalloc/zsmalloc.h | 50 -- include/linux/zsmalloc.h | 50 ++ mm/Kconfig | 25 + mm/Makefile | 2 + mm/zsmalloc.c | 1106 +++++++++++++++++++++++++++++ 9 files changed, 1184 insertions(+), 1185 deletions(-) delete mode 100644 drivers/staging/zsmalloc/Kconfig delete mode 100644 drivers/staging/zsmalloc/Makefile delete mode 100644 drivers/staging/zsmalloc/zsmalloc-main.c delete mode 100644 drivers/staging/zsmalloc/zsmalloc.h create mode 100644 include/linux/zsmalloc.h create mode 100644 mm/zsmalloc.c diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index 508a19f..1c39fed 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -17,8 +17,7 @@ #include #include - -#include "../zsmalloc/zsmalloc.h" +#include /* * Some arbitrary value. This is just to catch diff --git a/drivers/staging/zsmalloc/Kconfig b/drivers/staging/zsmalloc/Kconfig deleted file mode 100644 index e75611a..0000000 --- a/drivers/staging/zsmalloc/Kconfig +++ /dev/null @@ -1,23 +0,0 @@ -config ZSMALLOC - bool "Memory allocator for compressed pages" - default n - help - zsmalloc is a slab-based memory allocator designed to store - compressed RAM pages. zsmalloc uses virtual memory mapping - in order to reduce fragmentation. However, this results in a - non-standard allocator interface where a handle, not a pointer, is - returned by an alloc(). This handle must be mapped in order to - access the allocated space. - -config PGTABLE_MAPPING - bool "Use page table mapping to access object in zsmalloc" - depends on ZSMALLOC - help - By default, zsmalloc uses a copy-based object mapping method to - access allocations that span two pages. However, if a particular - architecture (ex, ARM) performs VM mapping faster than copying, - then you should select this. This causes zsmalloc to use page table - mapping rather than copying for object mapping. - - You can check speed with zsmalloc benchmark[1]. - [1] https://github.com/spartacus06/zsmalloc diff --git a/drivers/staging/zsmalloc/Makefile b/drivers/staging/zsmalloc/Makefile deleted file mode 100644 index b134848..0000000 --- a/drivers/staging/zsmalloc/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -zsmalloc-y := zsmalloc-main.o - -obj-$(CONFIG_ZSMALLOC) += zsmalloc.o diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c deleted file mode 100644 index 07186ab..0000000 --- a/drivers/staging/zsmalloc/zsmalloc-main.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * zsmalloc memory allocator - * - * Copyright (C) 2011 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the license that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - */ - -/* - * This allocator is designed for use with zram. Thus, the allocator is - * supposed to work well under low memory conditions. In particular, it - * never attempts higher order page allocation which is very likely to - * fail under memory pressure. On the other hand, if we just use single - * (0-order) pages, it would suffer from very high fragmentation -- - * any object of size PAGE_SIZE/2 or larger would occupy an entire page. - * This was one of the major issues with its predecessor (xvmalloc). - * - * To overcome these issues, zsmalloc allocates a bunch of 0-order pages - * and links them together using various 'struct page' fields. These linked - * pages act as a single higher-order page i.e. an object can span 0-order - * page boundaries. The code refers to these linked pages as a single entity - * called zspage. - * - * For simplicity, zsmalloc can only allocate objects of size up to PAGE_SIZE - * since this satisfies the requirements of all its current users (in the - * worst case, page is incompressible and is thus stored "as-is" i.e. in - * uncompressed form). For allocation requests larger than this size, failure - * is returned (see zs_malloc). - * - * Additionally, zs_malloc() does not return a dereferenceable pointer. - * Instead, it returns an opaque handle (unsigned long) which encodes actual - * location of the allocated object. The reason for this indirection is that - * zsmalloc does not keep zspages permanently mapped since that would cause - * issues on 32-bit systems where the VA region for kernel space mappings - * is very small. So, before using the allocating memory, the object has to - * be mapped using zs_map_object() to get a usable pointer and subsequently - * unmapped using zs_unmap_object(). - * - * Following is how we use various fields and flags of underlying - * struct page(s) to form a zspage. - * - * Usage of struct page fields: - * page->first_page: points to the first component (0-order) page - * page->index (union with page->freelist): offset of the first object - * starting in this page. For the first page, this is - * always 0, so we use this field (aka freelist) to point - * to the first free object in zspage. - * page->lru: links together all component pages (except the first page) - * of a zspage - * - * For _first_ page only: - * - * page->private (union with page->first_page): refers to the - * component page after the first page - * page->freelist: points to the first free object in zspage. - * Free objects are linked together using in-place - * metadata. - * page->objects: maximum number of objects we can store in this - * zspage (class->zspage_order * PAGE_SIZE / class->size) - * page->lru: links together first pages of various zspages. - * Basically forming list of zspages in a fullness group. - * page->mapping: class index and fullness group of the zspage - * - * Usage of struct page flags: - * PG_private: identifies the first component page - * PG_private2: identifies the last component page - * - */ - -#ifdef CONFIG_ZSMALLOC_DEBUG -#define DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zsmalloc.h" - -/* - * This must be power of 2 and greater than of equal to sizeof(link_free). - * These two conditions ensure that any 'struct link_free' itself doesn't - * span more than 1 page which avoids complex case of mapping 2 pages simply - * to restore link_free pointer values. - */ -#define ZS_ALIGN 8 - -/* - * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single) - * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N. - */ -#define ZS_MAX_ZSPAGE_ORDER 2 -#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) - -/* - * Object location (, ) is encoded as - * as single (unsigned long) handle value. - * - * Note that object index is relative to system - * page it is stored in, so for each sub-page belonging - * to a zspage, obj_idx starts with 0. - * - * This is made more complicated by various memory models and PAE. - */ - -#ifndef MAX_PHYSMEM_BITS -#ifdef CONFIG_HIGHMEM64G -#define MAX_PHYSMEM_BITS 36 -#else /* !CONFIG_HIGHMEM64G */ -/* - * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just - * be PAGE_SHIFT - */ -#define MAX_PHYSMEM_BITS BITS_PER_LONG -#endif -#endif -#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) -#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) -#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) - -#define MAX(a, b) ((a) >= (b) ? (a) : (b)) -/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ -#define ZS_MIN_ALLOC_SIZE \ - MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) -#define ZS_MAX_ALLOC_SIZE PAGE_SIZE - -/* - * On systems with 4K page size, this gives 254 size classes! There is a - * trader-off here: - * - Large number of size classes is potentially wasteful as free page are - * spread across these classes - * - Small number of size classes causes large internal fragmentation - * - Probably its better to use specific size classes (empirically - * determined). NOTE: all those class sizes must be set as multiple of - * ZS_ALIGN to make sure link_free itself never has to span 2 pages. - * - * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN - * (reason above) - */ -#define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> 8) -#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \ - ZS_SIZE_CLASS_DELTA + 1) - -/* - * We do not maintain any list for completely empty or full pages - */ -enum fullness_group { - ZS_ALMOST_FULL, - ZS_ALMOST_EMPTY, - _ZS_NR_FULLNESS_GROUPS, - - ZS_EMPTY, - ZS_FULL -}; - -/* - * We assign a page to ZS_ALMOST_EMPTY fullness group when: - * n <= N / f, where - * n = number of allocated objects - * N = total number of objects zspage can store - * f = 1/fullness_threshold_frac - * - * Similarly, we assign zspage to: - * ZS_ALMOST_FULL when n > N / f - * ZS_EMPTY when n == 0 - * ZS_FULL when n == N - * - * (see: fix_fullness_group()) - */ -static const int fullness_threshold_frac = 4; - -struct size_class { - /* - * Size of objects stored in this class. Must be multiple - * of ZS_ALIGN. - */ - int size; - unsigned int index; - - /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ - int pages_per_zspage; - - spinlock_t lock; - - /* stats */ - u64 pages_allocated; - - struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS]; -}; - -/* - * Placed within free objects to form a singly linked list. - * For every zspage, first_page->freelist gives head of this list. - * - * This must be power of 2 and less than or equal to ZS_ALIGN - */ -struct link_free { - /* Handle of next free chunk (encodes ) */ - void *next; -}; - -struct zs_pool { - struct size_class size_class[ZS_SIZE_CLASSES]; - - gfp_t flags; /* allocation flags used when growing pool */ -}; - -/* - * A zspage's class index and fullness group - * are encoded in its (first)page->mapping - */ -#define CLASS_IDX_BITS 28 -#define FULLNESS_BITS 4 -#define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) -#define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) - -struct mapping_area { -#ifdef CONFIG_PGTABLE_MAPPING - struct vm_struct *vm; /* vm area for mapping object that span pages */ -#else - char *vm_buf; /* copy buffer for objects that span pages */ -#endif - char *vm_addr; /* address of kmap_atomic()'ed pages */ - enum zs_mapmode vm_mm; /* mapping mode */ -}; - - -/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ -static DEFINE_PER_CPU(struct mapping_area, zs_map_area); - -static int is_first_page(struct page *page) -{ - return PagePrivate(page); -} - -static int is_last_page(struct page *page) -{ - return PagePrivate2(page); -} - -static void get_zspage_mapping(struct page *page, unsigned int *class_idx, - enum fullness_group *fullness) -{ - unsigned long m; - BUG_ON(!is_first_page(page)); - - m = (unsigned long)page->mapping; - *fullness = m & FULLNESS_MASK; - *class_idx = (m >> FULLNESS_BITS) & CLASS_IDX_MASK; -} - -static void set_zspage_mapping(struct page *page, unsigned int class_idx, - enum fullness_group fullness) -{ - unsigned long m; - BUG_ON(!is_first_page(page)); - - m = ((class_idx & CLASS_IDX_MASK) << FULLNESS_BITS) | - (fullness & FULLNESS_MASK); - page->mapping = (struct address_space *)m; -} - -/* - * zsmalloc divides the pool into various size classes where each - * class maintains a list of zspages where each zspage is divided - * into equal sized chunks. Each allocation falls into one of these - * classes depending on its size. This function returns index of the - * size class which has chunk size big enough to hold the give size. - */ -static int get_size_class_index(int size) -{ - int idx = 0; - - if (likely(size > ZS_MIN_ALLOC_SIZE)) - idx = DIV_ROUND_UP(size - ZS_MIN_ALLOC_SIZE, - ZS_SIZE_CLASS_DELTA); - - return idx; -} - -/* - * For each size class, zspages are divided into different groups - * depending on how "full" they are. This was done so that we could - * easily find empty or nearly empty zspages when we try to shrink - * the pool (not yet implemented). This function returns fullness - * status of the given page. - */ -static enum fullness_group get_fullness_group(struct page *page) -{ - int inuse, max_objects; - enum fullness_group fg; - BUG_ON(!is_first_page(page)); - - inuse = page->inuse; - max_objects = page->objects; - - if (inuse == 0) - fg = ZS_EMPTY; - else if (inuse == max_objects) - fg = ZS_FULL; - else if (inuse <= max_objects / fullness_threshold_frac) - fg = ZS_ALMOST_EMPTY; - else - fg = ZS_ALMOST_FULL; - - return fg; -} - -/* - * Each size class maintains various freelists and zspages are assigned - * to one of these freelists based on the number of live objects they - * have. This functions inserts the given zspage into the freelist - * identified by . - */ -static void insert_zspage(struct page *page, struct size_class *class, - enum fullness_group fullness) -{ - struct page **head; - - BUG_ON(!is_first_page(page)); - - if (fullness >= _ZS_NR_FULLNESS_GROUPS) - return; - - head = &class->fullness_list[fullness]; - if (*head) - list_add_tail(&page->lru, &(*head)->lru); - - *head = page; -} - -/* - * This function removes the given zspage from the freelist identified - * by . - */ -static void remove_zspage(struct page *page, struct size_class *class, - enum fullness_group fullness) -{ - struct page **head; - - BUG_ON(!is_first_page(page)); - - if (fullness >= _ZS_NR_FULLNESS_GROUPS) - return; - - head = &class->fullness_list[fullness]; - BUG_ON(!*head); - if (list_empty(&(*head)->lru)) - *head = NULL; - else if (*head == page) - *head = (struct page *)list_entry((*head)->lru.next, - struct page, lru); - - list_del_init(&page->lru); -} - -/* - * Each size class maintains zspages in different fullness groups depending - * on the number of live objects they contain. When allocating or freeing - * objects, the fullness status of the page can change, say, from ALMOST_FULL - * to ALMOST_EMPTY when freeing an object. This function checks if such - * a status change has occurred for the given page and accordingly moves the - * page from the freelist of the old fullness group to that of the new - * fullness group. - */ -static enum fullness_group fix_fullness_group(struct zs_pool *pool, - struct page *page) -{ - int class_idx; - struct size_class *class; - enum fullness_group currfg, newfg; - - BUG_ON(!is_first_page(page)); - - get_zspage_mapping(page, &class_idx, &currfg); - newfg = get_fullness_group(page); - if (newfg == currfg) - goto out; - - class = &pool->size_class[class_idx]; - remove_zspage(page, class, currfg); - insert_zspage(page, class, newfg); - set_zspage_mapping(page, class_idx, newfg); - -out: - return newfg; -} - -/* - * We have to decide on how many pages to link together - * to form a zspage for each size class. This is important - * to reduce wastage due to unusable space left at end of - * each zspage which is given as: - * wastage = Zp - Zp % size_class - * where Zp = zspage size = k * PAGE_SIZE where k = 1, 2, ... - * - * For example, for size class of 3/8 * PAGE_SIZE, we should - * link together 3 PAGE_SIZE sized pages to form a zspage - * since then we can perfectly fit in 8 such objects. - */ -static int get_pages_per_zspage(int class_size) -{ - int i, max_usedpc = 0; - /* zspage order which gives maximum used size per KB */ - int max_usedpc_order = 1; - - for (i = 1; i <= ZS_MAX_PAGES_PER_ZSPAGE; i++) { - int zspage_size; - int waste, usedpc; - - zspage_size = i * PAGE_SIZE; - waste = zspage_size % class_size; - usedpc = (zspage_size - waste) * 100 / zspage_size; - - if (usedpc > max_usedpc) { - max_usedpc = usedpc; - max_usedpc_order = i; - } - } - - return max_usedpc_order; -} - -/* - * A single 'zspage' is composed of many system pages which are - * linked together using fields in struct page. This function finds - * the first/head page, given any component page of a zspage. - */ -static struct page *get_first_page(struct page *page) -{ - if (is_first_page(page)) - return page; - else - return page->first_page; -} - -static struct page *get_next_page(struct page *page) -{ - struct page *next; - - if (is_last_page(page)) - next = NULL; - else if (is_first_page(page)) - next = (struct page *)page_private(page); - else - next = list_entry(page->lru.next, struct page, lru); - - return next; -} - -/* - * Encode as a single handle value. - * On hardware platforms with physical memory starting at 0x0 the pfn - * could be 0 so we ensure that the handle will never be 0 by adjusting the - * encoded obj_idx value before encoding. - */ -static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) -{ - unsigned long handle; - - if (!page) { - BUG_ON(obj_idx); - return NULL; - } - - handle = page_to_pfn(page) << OBJ_INDEX_BITS; - handle |= ((obj_idx + 1) & OBJ_INDEX_MASK); - - return (void *)handle; -} - -/* - * Decode pair from the given object handle. We adjust the - * decoded obj_idx back to its original value since it was adjusted in - * obj_location_to_handle(). - */ -static void obj_handle_to_location(unsigned long handle, struct page **page, - unsigned long *obj_idx) -{ - *page = pfn_to_page(handle >> OBJ_INDEX_BITS); - *obj_idx = (handle & OBJ_INDEX_MASK) - 1; -} - -static unsigned long obj_idx_to_offset(struct page *page, - unsigned long obj_idx, int class_size) -{ - unsigned long off = 0; - - if (!is_first_page(page)) - off = page->index; - - return off + obj_idx * class_size; -} - -static void reset_page(struct page *page) -{ - clear_bit(PG_private, &page->flags); - clear_bit(PG_private_2, &page->flags); - set_page_private(page, 0); - page->mapping = NULL; - page->freelist = NULL; - reset_page_mapcount(page); -} - -static void free_zspage(struct page *first_page) -{ - struct page *nextp, *tmp, *head_extra; - - BUG_ON(!is_first_page(first_page)); - BUG_ON(first_page->inuse); - - head_extra = (struct page *)page_private(first_page); - - reset_page(first_page); - __free_page(first_page); - - /* zspage with only 1 system page */ - if (!head_extra) - return; - - list_for_each_entry_safe(nextp, tmp, &head_extra->lru, lru) { - list_del(&nextp->lru); - reset_page(nextp); - __free_page(nextp); - } - reset_page(head_extra); - __free_page(head_extra); -} - -/* Initialize a newly allocated zspage */ -static void init_zspage(struct page *first_page, struct size_class *class) -{ - unsigned long off = 0; - struct page *page = first_page; - - BUG_ON(!is_first_page(first_page)); - while (page) { - struct page *next_page; - struct link_free *link; - unsigned int i, objs_on_page; - - /* - * page->index stores offset of first object starting - * in the page. For the first page, this is always 0, - * so we use first_page->index (aka ->freelist) to store - * head of corresponding zspage's freelist. - */ - if (page != first_page) - page->index = off; - - link = (struct link_free *)kmap_atomic(page) + - off / sizeof(*link); - objs_on_page = (PAGE_SIZE - off) / class->size; - - for (i = 1; i <= objs_on_page; i++) { - off += class->size; - if (off < PAGE_SIZE) { - link->next = obj_location_to_handle(page, i); - link += class->size / sizeof(*link); - } - } - - /* - * We now come to the last (full or partial) object on this - * page, which must point to the first object on the next - * page (if present) - */ - next_page = get_next_page(page); - link->next = obj_location_to_handle(next_page, 0); - kunmap_atomic(link); - page = next_page; - off = (off + class->size) % PAGE_SIZE; - } -} - -/* - * Allocate a zspage for the given size class - */ -static struct page *alloc_zspage(struct size_class *class, gfp_t flags) -{ - int i, error; - struct page *first_page = NULL, *uninitialized_var(prev_page); - - /* - * Allocate individual pages and link them together as: - * 1. first page->private = first sub-page - * 2. all sub-pages are linked together using page->lru - * 3. each sub-page is linked to the first page using page->first_page - * - * For each size class, First/Head pages are linked together using - * page->lru. Also, we set PG_private to identify the first page - * (i.e. no other sub-page has this flag set) and PG_private_2 to - * identify the last page. - */ - error = -ENOMEM; - for (i = 0; i < class->pages_per_zspage; i++) { - struct page *page; - - page = alloc_page(flags); - if (!page) - goto cleanup; - - INIT_LIST_HEAD(&page->lru); - if (i == 0) { /* first page */ - SetPagePrivate(page); - set_page_private(page, 0); - first_page = page; - first_page->inuse = 0; - } - if (i == 1) - set_page_private(first_page, (unsigned long)page); - if (i >= 1) - page->first_page = first_page; - if (i >= 2) - list_add(&page->lru, &prev_page->lru); - if (i == class->pages_per_zspage - 1) /* last page */ - SetPagePrivate2(page); - prev_page = page; - } - - init_zspage(first_page, class); - - first_page->freelist = obj_location_to_handle(first_page, 0); - /* Maximum number of objects we can store in this zspage */ - first_page->objects = class->pages_per_zspage * PAGE_SIZE / class->size; - - error = 0; /* Success */ - -cleanup: - if (unlikely(error) && first_page) { - free_zspage(first_page); - first_page = NULL; - } - - return first_page; -} - -static struct page *find_get_zspage(struct size_class *class) -{ - int i; - struct page *page; - - for (i = 0; i < _ZS_NR_FULLNESS_GROUPS; i++) { - page = class->fullness_list[i]; - if (page) - break; - } - - return page; -} - -#ifdef CONFIG_PGTABLE_MAPPING -static inline int __zs_cpu_up(struct mapping_area *area) -{ - /* - * Make sure we don't leak memory if a cpu UP notification - * and zs_init() race and both call zs_cpu_up() on the same cpu - */ - if (area->vm) - return 0; - area->vm = alloc_vm_area(PAGE_SIZE * 2, NULL); - if (!area->vm) - return -ENOMEM; - return 0; -} - -static inline void __zs_cpu_down(struct mapping_area *area) -{ - if (area->vm) - free_vm_area(area->vm); - area->vm = NULL; -} - -static inline void *__zs_map_object(struct mapping_area *area, - struct page *pages[2], int off, int size) -{ - BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, &pages)); - area->vm_addr = area->vm->addr; - return area->vm_addr + off; -} - -static inline void __zs_unmap_object(struct mapping_area *area, - struct page *pages[2], int off, int size) -{ - unsigned long addr = (unsigned long)area->vm_addr; - - unmap_kernel_range(addr, PAGE_SIZE * 2); -} - -#else /* CONFIG_PGTABLE_MAPPING */ - -static inline int __zs_cpu_up(struct mapping_area *area) -{ - /* - * Make sure we don't leak memory if a cpu UP notification - * and zs_init() race and both call zs_cpu_up() on the same cpu - */ - if (area->vm_buf) - return 0; - area->vm_buf = (char *)__get_free_page(GFP_KERNEL); - if (!area->vm_buf) - return -ENOMEM; - return 0; -} - -static inline void __zs_cpu_down(struct mapping_area *area) -{ - if (area->vm_buf) - free_page((unsigned long)area->vm_buf); - area->vm_buf = NULL; -} - -static void *__zs_map_object(struct mapping_area *area, - struct page *pages[2], int off, int size) -{ - int sizes[2]; - void *addr; - char *buf = area->vm_buf; - - /* disable page faults to match kmap_atomic() return conditions */ - pagefault_disable(); - - /* no read fastpath */ - if (area->vm_mm == ZS_MM_WO) - goto out; - - sizes[0] = PAGE_SIZE - off; - sizes[1] = size - sizes[0]; - - /* copy object to per-cpu buffer */ - addr = kmap_atomic(pages[0]); - memcpy(buf, addr + off, sizes[0]); - kunmap_atomic(addr); - addr = kmap_atomic(pages[1]); - memcpy(buf + sizes[0], addr, sizes[1]); - kunmap_atomic(addr); -out: - return area->vm_buf; -} - -static void __zs_unmap_object(struct mapping_area *area, - struct page *pages[2], int off, int size) -{ - int sizes[2]; - void *addr; - char *buf = area->vm_buf; - - /* no write fastpath */ - if (area->vm_mm == ZS_MM_RO) - goto out; - - sizes[0] = PAGE_SIZE - off; - sizes[1] = size - sizes[0]; - - /* copy per-cpu buffer to object */ - addr = kmap_atomic(pages[0]); - memcpy(addr + off, buf, sizes[0]); - kunmap_atomic(addr); - addr = kmap_atomic(pages[1]); - memcpy(addr, buf + sizes[0], sizes[1]); - kunmap_atomic(addr); - -out: - /* enable page faults to match kunmap_atomic() return conditions */ - pagefault_enable(); -} - -#endif /* CONFIG_PGTABLE_MAPPING */ - -static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, - void *pcpu) -{ - int ret, cpu = (long)pcpu; - struct mapping_area *area; - - switch (action) { - case CPU_UP_PREPARE: - area = &per_cpu(zs_map_area, cpu); - ret = __zs_cpu_up(area); - if (ret) - return notifier_from_errno(ret); - break; - case CPU_DEAD: - case CPU_UP_CANCELED: - area = &per_cpu(zs_map_area, cpu); - __zs_cpu_down(area); - break; - } - - return NOTIFY_OK; -} - -static struct notifier_block zs_cpu_nb = { - .notifier_call = zs_cpu_notifier -}; - -static void zs_exit(void) -{ - int cpu; - - for_each_online_cpu(cpu) - zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu); - unregister_cpu_notifier(&zs_cpu_nb); -} - -static int zs_init(void) -{ - int cpu, ret; - - register_cpu_notifier(&zs_cpu_nb); - for_each_online_cpu(cpu) { - ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu); - if (notifier_to_errno(ret)) - goto fail; - } - return 0; -fail: - zs_exit(); - return notifier_to_errno(ret); -} - -/** - * zs_create_pool - Creates an allocation pool to work from. - * @flags: allocation flags used to allocate pool metadata - * - * This function must be called before anything when using - * the zsmalloc allocator. - * - * On success, a pointer to the newly created pool is returned, - * otherwise NULL. - */ -struct zs_pool *zs_create_pool(gfp_t flags) -{ - int i, ovhd_size; - struct zs_pool *pool; - - ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); - pool = kzalloc(ovhd_size, GFP_KERNEL); - if (!pool) - return NULL; - - for (i = 0; i < ZS_SIZE_CLASSES; i++) { - int size; - struct size_class *class; - - size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; - if (size > ZS_MAX_ALLOC_SIZE) - size = ZS_MAX_ALLOC_SIZE; - - class = &pool->size_class[i]; - class->size = size; - class->index = i; - spin_lock_init(&class->lock); - class->pages_per_zspage = get_pages_per_zspage(size); - - } - - pool->flags = flags; - - return pool; -} -EXPORT_SYMBOL_GPL(zs_create_pool); - -void zs_destroy_pool(struct zs_pool *pool) -{ - int i; - - for (i = 0; i < ZS_SIZE_CLASSES; i++) { - int fg; - struct size_class *class = &pool->size_class[i]; - - for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { - if (class->fullness_list[fg]) { - pr_info("Freeing non-empty class with size %db, fullness group %d\n", - class->size, fg); - } - } - } - kfree(pool); -} -EXPORT_SYMBOL_GPL(zs_destroy_pool); - -/** - * zs_malloc - Allocate block of given size from pool. - * @pool: pool to allocate from - * @size: size of block to allocate - * - * On success, handle to the allocated object is returned, - * otherwise 0. - * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail. - */ -unsigned long zs_malloc(struct zs_pool *pool, size_t size) -{ - unsigned long obj; - struct link_free *link; - int class_idx; - struct size_class *class; - - struct page *first_page, *m_page; - unsigned long m_objidx, m_offset; - - if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) - return 0; - - class_idx = get_size_class_index(size); - class = &pool->size_class[class_idx]; - BUG_ON(class_idx != class->index); - - spin_lock(&class->lock); - first_page = find_get_zspage(class); - - if (!first_page) { - spin_unlock(&class->lock); - first_page = alloc_zspage(class, pool->flags); - if (unlikely(!first_page)) - return 0; - - set_zspage_mapping(first_page, class->index, ZS_EMPTY); - spin_lock(&class->lock); - class->pages_allocated += class->pages_per_zspage; - } - - obj = (unsigned long)first_page->freelist; - obj_handle_to_location(obj, &m_page, &m_objidx); - m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); - - link = (struct link_free *)kmap_atomic(m_page) + - m_offset / sizeof(*link); - first_page->freelist = link->next; - memset(link, POISON_INUSE, sizeof(*link)); - kunmap_atomic(link); - - first_page->inuse++; - /* Now move the zspage to another fullness group, if required */ - fix_fullness_group(pool, first_page); - spin_unlock(&class->lock); - - return obj; -} -EXPORT_SYMBOL_GPL(zs_malloc); - -void zs_free(struct zs_pool *pool, unsigned long obj) -{ - struct link_free *link; - struct page *first_page, *f_page; - unsigned long f_objidx, f_offset; - - int class_idx; - struct size_class *class; - enum fullness_group fullness; - - if (unlikely(!obj)) - return; - - obj_handle_to_location(obj, &f_page, &f_objidx); - first_page = get_first_page(f_page); - - get_zspage_mapping(first_page, &class_idx, &fullness); - class = &pool->size_class[class_idx]; - f_offset = obj_idx_to_offset(f_page, f_objidx, class->size); - - spin_lock(&class->lock); - - /* Insert this object in containing zspage's freelist */ - link = (struct link_free *)((unsigned char *)kmap_atomic(f_page) - + f_offset); - link->next = first_page->freelist; - kunmap_atomic(link); - first_page->freelist = (void *)obj; - - first_page->inuse--; - fullness = fix_fullness_group(pool, first_page); - - if (fullness == ZS_EMPTY) - class->pages_allocated -= class->pages_per_zspage; - - spin_unlock(&class->lock); - - if (fullness == ZS_EMPTY) - free_zspage(first_page); -} -EXPORT_SYMBOL_GPL(zs_free); - -/** - * zs_map_object - get address of allocated object from handle. - * @pool: pool from which the object was allocated - * @handle: handle returned from zs_malloc - * - * Before using an object allocated from zs_malloc, it must be mapped using - * this function. When done with the object, it must be unmapped using - * zs_unmap_object. - * - * Only one object can be mapped per cpu at a time. There is no protection - * against nested mappings. - * - * This function returns with preemption and page faults disabled. - */ -void *zs_map_object(struct zs_pool *pool, unsigned long handle, - enum zs_mapmode mm) -{ - struct page *page; - unsigned long obj_idx, off; - - unsigned int class_idx; - enum fullness_group fg; - struct size_class *class; - struct mapping_area *area; - struct page *pages[2]; - - BUG_ON(!handle); - - /* - * Because we use per-cpu mapping areas shared among the - * pools/users, we can't allow mapping in interrupt context - * because it can corrupt another users mappings. - */ - BUG_ON(in_interrupt()); - - obj_handle_to_location(handle, &page, &obj_idx); - get_zspage_mapping(get_first_page(page), &class_idx, &fg); - class = &pool->size_class[class_idx]; - off = obj_idx_to_offset(page, obj_idx, class->size); - - area = &get_cpu_var(zs_map_area); - area->vm_mm = mm; - if (off + class->size <= PAGE_SIZE) { - /* this object is contained entirely within a page */ - area->vm_addr = kmap_atomic(page); - return area->vm_addr + off; - } - - /* this object spans two pages */ - pages[0] = page; - pages[1] = get_next_page(page); - BUG_ON(!pages[1]); - - return __zs_map_object(area, pages, off, class->size); -} -EXPORT_SYMBOL_GPL(zs_map_object); - -void zs_unmap_object(struct zs_pool *pool, unsigned long handle) -{ - struct page *page; - unsigned long obj_idx, off; - - unsigned int class_idx; - enum fullness_group fg; - struct size_class *class; - struct mapping_area *area; - - BUG_ON(!handle); - - obj_handle_to_location(handle, &page, &obj_idx); - get_zspage_mapping(get_first_page(page), &class_idx, &fg); - class = &pool->size_class[class_idx]; - off = obj_idx_to_offset(page, obj_idx, class->size); - - area = &__get_cpu_var(zs_map_area); - if (off + class->size <= PAGE_SIZE) - kunmap_atomic(area->vm_addr); - else { - struct page *pages[2]; - - pages[0] = page; - pages[1] = get_next_page(page); - BUG_ON(!pages[1]); - - __zs_unmap_object(area, pages, off, class->size); - } - put_cpu_var(zs_map_area); -} -EXPORT_SYMBOL_GPL(zs_unmap_object); - -u64 zs_get_total_size_bytes(struct zs_pool *pool) -{ - int i; - u64 npages = 0; - - for (i = 0; i < ZS_SIZE_CLASSES; i++) - npages += pool->size_class[i].pages_allocated; - - return npages << PAGE_SHIFT; -} -EXPORT_SYMBOL_GPL(zs_get_total_size_bytes); - -module_init(zs_init); -module_exit(zs_exit); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_AUTHOR("Nitin Gupta "); diff --git a/drivers/staging/zsmalloc/zsmalloc.h b/drivers/staging/zsmalloc/zsmalloc.h deleted file mode 100644 index c2eb174..0000000 --- a/drivers/staging/zsmalloc/zsmalloc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * zsmalloc memory allocator - * - * Copyright (C) 2011 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the license that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - */ - -#ifndef _ZS_MALLOC_H_ -#define _ZS_MALLOC_H_ - -#include - -/* - * zsmalloc mapping modes - * - * NOTE: These only make a difference when a mapped object spans pages. - * They also have no effect when PGTABLE_MAPPING is selected. - */ -enum zs_mapmode { - ZS_MM_RW, /* normal read-write mapping */ - ZS_MM_RO, /* read-only (no copy-out at unmap time) */ - ZS_MM_WO /* write-only (no copy-in at map time) */ - /* - * NOTE: ZS_MM_WO should only be used for initializing new - * (uninitialized) allocations. Partial writes to already - * initialized allocations should use ZS_MM_RW to preserve the - * existing data. - */ -}; - -struct zs_pool; - -struct zs_pool *zs_create_pool(gfp_t flags); -void zs_destroy_pool(struct zs_pool *pool); - -unsigned long zs_malloc(struct zs_pool *pool, size_t size); -void zs_free(struct zs_pool *pool, unsigned long obj); - -void *zs_map_object(struct zs_pool *pool, unsigned long handle, - enum zs_mapmode mm); -void zs_unmap_object(struct zs_pool *pool, unsigned long handle); - -u64 zs_get_total_size_bytes(struct zs_pool *pool); - -#endif diff --git a/include/linux/zsmalloc.h b/include/linux/zsmalloc.h new file mode 100644 index 0000000..c2eb174 --- /dev/null +++ b/include/linux/zsmalloc.h @@ -0,0 +1,50 @@ +/* + * zsmalloc memory allocator + * + * Copyright (C) 2011 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the license that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + */ + +#ifndef _ZS_MALLOC_H_ +#define _ZS_MALLOC_H_ + +#include + +/* + * zsmalloc mapping modes + * + * NOTE: These only make a difference when a mapped object spans pages. + * They also have no effect when PGTABLE_MAPPING is selected. + */ +enum zs_mapmode { + ZS_MM_RW, /* normal read-write mapping */ + ZS_MM_RO, /* read-only (no copy-out at unmap time) */ + ZS_MM_WO /* write-only (no copy-in at map time) */ + /* + * NOTE: ZS_MM_WO should only be used for initializing new + * (uninitialized) allocations. Partial writes to already + * initialized allocations should use ZS_MM_RW to preserve the + * existing data. + */ +}; + +struct zs_pool; + +struct zs_pool *zs_create_pool(gfp_t flags); +void zs_destroy_pool(struct zs_pool *pool); + +unsigned long zs_malloc(struct zs_pool *pool, size_t size); +void zs_free(struct zs_pool *pool, unsigned long obj); + +void *zs_map_object(struct zs_pool *pool, unsigned long handle, + enum zs_mapmode mm); +void zs_unmap_object(struct zs_pool *pool, unsigned long handle); + +u64 zs_get_total_size_bytes(struct zs_pool *pool); + +#endif diff --git a/mm/Kconfig b/mm/Kconfig index dcecf12..6c733a7 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -371,6 +371,31 @@ config CLEANCACHE If unsure, say Y to enable cleancache +config ZSMALLOC + bool "Memory allocator for compressed pages" + depends on MMU + default n + help + zsmalloc is a slab-based memory allocator designed to store + compressed RAM pages. zsmalloc uses virtual memory mapping + in order to reduce fragmentation. However, this results in a + non-standard allocator interface where a handle, not a pointer, is + returned by an alloc(). This handle must be mapped in order to + access the allocated space. + +config PGTABLE_MAPPING + bool "Use page table mapping to access object in zsmalloc" + depends on ZSMALLOC + help + By default, zsmalloc uses a copy-based object mapping method to + access allocations that span two pages. However, if a particular + architecture (ex, ARM) performs VM mapping faster than copying, + then you should select this. This causes zsmalloc to use page table + mapping rather than copying for object mapping. + + You can check speed with zsmalloc benchmark[1]. + [1] https://github.com/spartacus06/zsmalloc + config CMA bool "Contiguous Memory Allocator framework" # Currently there is only one allocator so force it on diff --git a/mm/Makefile b/mm/Makefile index 9b994ce..47ebf93 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -78,5 +78,7 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o +obj-$(CONFIG_ZSMALLOC) += zsmalloc.o + obj-$(CONFIG_CMA) += cma.o obj-$(CONFIG_CMA_BEST_FIT) += cma-best-fit.o diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c new file mode 100644 index 0000000..6c60592 --- /dev/null +++ b/mm/zsmalloc.c @@ -0,0 +1,1106 @@ +/* + * zsmalloc memory allocator + * + * Copyright (C) 2011 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the license that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + */ + +/* + * This allocator is designed for use with zram. Thus, the allocator is + * supposed to work well under low memory conditions. In particular, it + * never attempts higher order page allocation which is very likely to + * fail under memory pressure. On the other hand, if we just use single + * (0-order) pages, it would suffer from very high fragmentation -- + * any object of size PAGE_SIZE/2 or larger would occupy an entire page. + * This was one of the major issues with its predecessor (xvmalloc). + * + * To overcome these issues, zsmalloc allocates a bunch of 0-order pages + * and links them together using various 'struct page' fields. These linked + * pages act as a single higher-order page i.e. an object can span 0-order + * page boundaries. The code refers to these linked pages as a single entity + * called zspage. + * + * For simplicity, zsmalloc can only allocate objects of size up to PAGE_SIZE + * since this satisfies the requirements of all its current users (in the + * worst case, page is incompressible and is thus stored "as-is" i.e. in + * uncompressed form). For allocation requests larger than this size, failure + * is returned (see zs_malloc). + * + * Additionally, zs_malloc() does not return a dereferenceable pointer. + * Instead, it returns an opaque handle (unsigned long) which encodes actual + * location of the allocated object. The reason for this indirection is that + * zsmalloc does not keep zspages permanently mapped since that would cause + * issues on 32-bit systems where the VA region for kernel space mappings + * is very small. So, before using the allocating memory, the object has to + * be mapped using zs_map_object() to get a usable pointer and subsequently + * unmapped using zs_unmap_object(). + * + * Following is how we use various fields and flags of underlying + * struct page(s) to form a zspage. + * + * Usage of struct page fields: + * page->first_page: points to the first component (0-order) page + * page->index (union with page->freelist): offset of the first object + * starting in this page. For the first page, this is + * always 0, so we use this field (aka freelist) to point + * to the first free object in zspage. + * page->lru: links together all component pages (except the first page) + * of a zspage + * + * For _first_ page only: + * + * page->private (union with page->first_page): refers to the + * component page after the first page + * page->freelist: points to the first free object in zspage. + * Free objects are linked together using in-place + * metadata. + * page->objects: maximum number of objects we can store in this + * zspage (class->zspage_order * PAGE_SIZE / class->size) + * page->lru: links together first pages of various zspages. + * Basically forming list of zspages in a fullness group. + * page->mapping: class index and fullness group of the zspage + * + * Usage of struct page flags: + * PG_private: identifies the first component page + * PG_private2: identifies the last component page + * + */ + +#ifdef CONFIG_ZSMALLOC_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This must be power of 2 and greater than of equal to sizeof(link_free). + * These two conditions ensure that any 'struct link_free' itself doesn't + * span more than 1 page which avoids complex case of mapping 2 pages simply + * to restore link_free pointer values. + */ +#define ZS_ALIGN 8 + +/* + * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single) + * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N. + */ +#define ZS_MAX_ZSPAGE_ORDER 2 +#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) + +/* + * Object location (, ) is encoded as + * as single (unsigned long) handle value. + * + * Note that object index is relative to system + * page it is stored in, so for each sub-page belonging + * to a zspage, obj_idx starts with 0. + * + * This is made more complicated by various memory models and PAE. + */ + +#ifndef MAX_PHYSMEM_BITS +#ifdef CONFIG_HIGHMEM64G +#define MAX_PHYSMEM_BITS 36 +#else /* !CONFIG_HIGHMEM64G */ +/* + * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just + * be PAGE_SHIFT + */ +#define MAX_PHYSMEM_BITS BITS_PER_LONG +#endif +#endif +#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT) +#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS) +#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1) + +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) +/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ +#define ZS_MIN_ALLOC_SIZE \ + MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) +#define ZS_MAX_ALLOC_SIZE PAGE_SIZE + +/* + * On systems with 4K page size, this gives 254 size classes! There is a + * trader-off here: + * - Large number of size classes is potentially wasteful as free page are + * spread across these classes + * - Small number of size classes causes large internal fragmentation + * - Probably its better to use specific size classes (empirically + * determined). NOTE: all those class sizes must be set as multiple of + * ZS_ALIGN to make sure link_free itself never has to span 2 pages. + * + * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN + * (reason above) + */ +#define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> 8) +#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \ + ZS_SIZE_CLASS_DELTA + 1) + +/* + * We do not maintain any list for completely empty or full pages + */ +enum fullness_group { + ZS_ALMOST_FULL, + ZS_ALMOST_EMPTY, + _ZS_NR_FULLNESS_GROUPS, + + ZS_EMPTY, + ZS_FULL +}; + +/* + * We assign a page to ZS_ALMOST_EMPTY fullness group when: + * n <= N / f, where + * n = number of allocated objects + * N = total number of objects zspage can store + * f = 1/fullness_threshold_frac + * + * Similarly, we assign zspage to: + * ZS_ALMOST_FULL when n > N / f + * ZS_EMPTY when n == 0 + * ZS_FULL when n == N + * + * (see: fix_fullness_group()) + */ +static const int fullness_threshold_frac = 4; + +struct size_class { + /* + * Size of objects stored in this class. Must be multiple + * of ZS_ALIGN. + */ + int size; + unsigned int index; + + /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ + int pages_per_zspage; + + spinlock_t lock; + + /* stats */ + u64 pages_allocated; + + struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS]; +}; + +/* + * Placed within free objects to form a singly linked list. + * For every zspage, first_page->freelist gives head of this list. + * + * This must be power of 2 and less than or equal to ZS_ALIGN + */ +struct link_free { + /* Handle of next free chunk (encodes ) */ + void *next; +}; + +struct zs_pool { + struct size_class size_class[ZS_SIZE_CLASSES]; + + gfp_t flags; /* allocation flags used when growing pool */ +}; + +/* + * A zspage's class index and fullness group + * are encoded in its (first)page->mapping + */ +#define CLASS_IDX_BITS 28 +#define FULLNESS_BITS 4 +#define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1) +#define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1) + +struct mapping_area { +#ifdef CONFIG_PGTABLE_MAPPING + struct vm_struct *vm; /* vm area for mapping object that span pages */ +#else + char *vm_buf; /* copy buffer for objects that span pages */ +#endif + char *vm_addr; /* address of kmap_atomic()'ed pages */ + enum zs_mapmode vm_mm; /* mapping mode */ +}; + + +/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ +static DEFINE_PER_CPU(struct mapping_area, zs_map_area); + +static int is_first_page(struct page *page) +{ + return PagePrivate(page); +} + +static int is_last_page(struct page *page) +{ + return PagePrivate2(page); +} + +static void get_zspage_mapping(struct page *page, unsigned int *class_idx, + enum fullness_group *fullness) +{ + unsigned long m; + BUG_ON(!is_first_page(page)); + + m = (unsigned long)page->mapping; + *fullness = m & FULLNESS_MASK; + *class_idx = (m >> FULLNESS_BITS) & CLASS_IDX_MASK; +} + +static void set_zspage_mapping(struct page *page, unsigned int class_idx, + enum fullness_group fullness) +{ + unsigned long m; + BUG_ON(!is_first_page(page)); + + m = ((class_idx & CLASS_IDX_MASK) << FULLNESS_BITS) | + (fullness & FULLNESS_MASK); + page->mapping = (struct address_space *)m; +} + +/* + * zsmalloc divides the pool into various size classes where each + * class maintains a list of zspages where each zspage is divided + * into equal sized chunks. Each allocation falls into one of these + * classes depending on its size. This function returns index of the + * size class which has chunk size big enough to hold the give size. + */ +static int get_size_class_index(int size) +{ + int idx = 0; + + if (likely(size > ZS_MIN_ALLOC_SIZE)) + idx = DIV_ROUND_UP(size - ZS_MIN_ALLOC_SIZE, + ZS_SIZE_CLASS_DELTA); + + return idx; +} + +/* + * For each size class, zspages are divided into different groups + * depending on how "full" they are. This was done so that we could + * easily find empty or nearly empty zspages when we try to shrink + * the pool (not yet implemented). This function returns fullness + * status of the given page. + */ +static enum fullness_group get_fullness_group(struct page *page) +{ + int inuse, max_objects; + enum fullness_group fg; + BUG_ON(!is_first_page(page)); + + inuse = page->inuse; + max_objects = page->objects; + + if (inuse == 0) + fg = ZS_EMPTY; + else if (inuse == max_objects) + fg = ZS_FULL; + else if (inuse <= max_objects / fullness_threshold_frac) + fg = ZS_ALMOST_EMPTY; + else + fg = ZS_ALMOST_FULL; + + return fg; +} + +/* + * Each size class maintains various freelists and zspages are assigned + * to one of these freelists based on the number of live objects they + * have. This functions inserts the given zspage into the freelist + * identified by . + */ +static void insert_zspage(struct page *page, struct size_class *class, + enum fullness_group fullness) +{ + struct page **head; + + BUG_ON(!is_first_page(page)); + + if (fullness >= _ZS_NR_FULLNESS_GROUPS) + return; + + head = &class->fullness_list[fullness]; + if (*head) + list_add_tail(&page->lru, &(*head)->lru); + + *head = page; +} + +/* + * This function removes the given zspage from the freelist identified + * by . + */ +static void remove_zspage(struct page *page, struct size_class *class, + enum fullness_group fullness) +{ + struct page **head; + + BUG_ON(!is_first_page(page)); + + if (fullness >= _ZS_NR_FULLNESS_GROUPS) + return; + + head = &class->fullness_list[fullness]; + BUG_ON(!*head); + if (list_empty(&(*head)->lru)) + *head = NULL; + else if (*head == page) + *head = (struct page *)list_entry((*head)->lru.next, + struct page, lru); + + list_del_init(&page->lru); +} + +/* + * Each size class maintains zspages in different fullness groups depending + * on the number of live objects they contain. When allocating or freeing + * objects, the fullness status of the page can change, say, from ALMOST_FULL + * to ALMOST_EMPTY when freeing an object. This function checks if such + * a status change has occurred for the given page and accordingly moves the + * page from the freelist of the old fullness group to that of the new + * fullness group. + */ +static enum fullness_group fix_fullness_group(struct zs_pool *pool, + struct page *page) +{ + int class_idx; + struct size_class *class; + enum fullness_group currfg, newfg; + + BUG_ON(!is_first_page(page)); + + get_zspage_mapping(page, &class_idx, &currfg); + newfg = get_fullness_group(page); + if (newfg == currfg) + goto out; + + class = &pool->size_class[class_idx]; + remove_zspage(page, class, currfg); + insert_zspage(page, class, newfg); + set_zspage_mapping(page, class_idx, newfg); + +out: + return newfg; +} + +/* + * We have to decide on how many pages to link together + * to form a zspage for each size class. This is important + * to reduce wastage due to unusable space left at end of + * each zspage which is given as: + * wastage = Zp - Zp % size_class + * where Zp = zspage size = k * PAGE_SIZE where k = 1, 2, ... + * + * For example, for size class of 3/8 * PAGE_SIZE, we should + * link together 3 PAGE_SIZE sized pages to form a zspage + * since then we can perfectly fit in 8 such objects. + */ +static int get_pages_per_zspage(int class_size) +{ + int i, max_usedpc = 0; + /* zspage order which gives maximum used size per KB */ + int max_usedpc_order = 1; + + for (i = 1; i <= ZS_MAX_PAGES_PER_ZSPAGE; i++) { + int zspage_size; + int waste, usedpc; + + zspage_size = i * PAGE_SIZE; + waste = zspage_size % class_size; + usedpc = (zspage_size - waste) * 100 / zspage_size; + + if (usedpc > max_usedpc) { + max_usedpc = usedpc; + max_usedpc_order = i; + } + } + + return max_usedpc_order; +} + +/* + * A single 'zspage' is composed of many system pages which are + * linked together using fields in struct page. This function finds + * the first/head page, given any component page of a zspage. + */ +static struct page *get_first_page(struct page *page) +{ + if (is_first_page(page)) + return page; + else + return page->first_page; +} + +static struct page *get_next_page(struct page *page) +{ + struct page *next; + + if (is_last_page(page)) + next = NULL; + else if (is_first_page(page)) + next = (struct page *)page_private(page); + else + next = list_entry(page->lru.next, struct page, lru); + + return next; +} + +/* + * Encode as a single handle value. + * On hardware platforms with physical memory starting at 0x0 the pfn + * could be 0 so we ensure that the handle will never be 0 by adjusting the + * encoded obj_idx value before encoding. + */ +static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) +{ + unsigned long handle; + + if (!page) { + BUG_ON(obj_idx); + return NULL; + } + + handle = page_to_pfn(page) << OBJ_INDEX_BITS; + handle |= ((obj_idx + 1) & OBJ_INDEX_MASK); + + return (void *)handle; +} + +/* + * Decode pair from the given object handle. We adjust the + * decoded obj_idx back to its original value since it was adjusted in + * obj_location_to_handle(). + */ +static void obj_handle_to_location(unsigned long handle, struct page **page, + unsigned long *obj_idx) +{ + *page = pfn_to_page(handle >> OBJ_INDEX_BITS); + *obj_idx = (handle & OBJ_INDEX_MASK) - 1; +} + +static unsigned long obj_idx_to_offset(struct page *page, + unsigned long obj_idx, int class_size) +{ + unsigned long off = 0; + + if (!is_first_page(page)) + off = page->index; + + return off + obj_idx * class_size; +} + +static void reset_page(struct page *page) +{ + clear_bit(PG_private, &page->flags); + clear_bit(PG_private_2, &page->flags); + set_page_private(page, 0); + page->mapping = NULL; + page->freelist = NULL; + reset_page_mapcount(page); +} + +static void free_zspage(struct page *first_page) +{ + struct page *nextp, *tmp, *head_extra; + + BUG_ON(!is_first_page(first_page)); + BUG_ON(first_page->inuse); + + head_extra = (struct page *)page_private(first_page); + + reset_page(first_page); + __free_page(first_page); + + /* zspage with only 1 system page */ + if (!head_extra) + return; + + list_for_each_entry_safe(nextp, tmp, &head_extra->lru, lru) { + list_del(&nextp->lru); + reset_page(nextp); + __free_page(nextp); + } + reset_page(head_extra); + __free_page(head_extra); +} + +/* Initialize a newly allocated zspage */ +static void init_zspage(struct page *first_page, struct size_class *class) +{ + unsigned long off = 0; + struct page *page = first_page; + + BUG_ON(!is_first_page(first_page)); + while (page) { + struct page *next_page; + struct link_free *link; + unsigned int i, objs_on_page; + + /* + * page->index stores offset of first object starting + * in the page. For the first page, this is always 0, + * so we use first_page->index (aka ->freelist) to store + * head of corresponding zspage's freelist. + */ + if (page != first_page) + page->index = off; + + link = (struct link_free *)kmap_atomic(page) + + off / sizeof(*link); + objs_on_page = (PAGE_SIZE - off) / class->size; + + for (i = 1; i <= objs_on_page; i++) { + off += class->size; + if (off < PAGE_SIZE) { + link->next = obj_location_to_handle(page, i); + link += class->size / sizeof(*link); + } + } + + /* + * We now come to the last (full or partial) object on this + * page, which must point to the first object on the next + * page (if present) + */ + next_page = get_next_page(page); + link->next = obj_location_to_handle(next_page, 0); + kunmap_atomic(link); + page = next_page; + off = (off + class->size) % PAGE_SIZE; + } +} + +/* + * Allocate a zspage for the given size class + */ +static struct page *alloc_zspage(struct size_class *class, gfp_t flags) +{ + int i, error; + struct page *first_page = NULL, *uninitialized_var(prev_page); + + /* + * Allocate individual pages and link them together as: + * 1. first page->private = first sub-page + * 2. all sub-pages are linked together using page->lru + * 3. each sub-page is linked to the first page using page->first_page + * + * For each size class, First/Head pages are linked together using + * page->lru. Also, we set PG_private to identify the first page + * (i.e. no other sub-page has this flag set) and PG_private_2 to + * identify the last page. + */ + error = -ENOMEM; + for (i = 0; i < class->pages_per_zspage; i++) { + struct page *page; + + page = alloc_page(flags); + if (!page) + goto cleanup; + + INIT_LIST_HEAD(&page->lru); + if (i == 0) { /* first page */ + SetPagePrivate(page); + set_page_private(page, 0); + first_page = page; + first_page->inuse = 0; + } + if (i == 1) + set_page_private(first_page, (unsigned long)page); + if (i >= 1) + page->first_page = first_page; + if (i >= 2) + list_add(&page->lru, &prev_page->lru); + if (i == class->pages_per_zspage - 1) /* last page */ + SetPagePrivate2(page); + prev_page = page; + } + + init_zspage(first_page, class); + + first_page->freelist = obj_location_to_handle(first_page, 0); + /* Maximum number of objects we can store in this zspage */ + first_page->objects = class->pages_per_zspage * PAGE_SIZE / class->size; + + error = 0; /* Success */ + +cleanup: + if (unlikely(error) && first_page) { + free_zspage(first_page); + first_page = NULL; + } + + return first_page; +} + +static struct page *find_get_zspage(struct size_class *class) +{ + int i; + struct page *page; + + for (i = 0; i < _ZS_NR_FULLNESS_GROUPS; i++) { + page = class->fullness_list[i]; + if (page) + break; + } + + return page; +} + +#ifdef CONFIG_PGTABLE_MAPPING +static inline int __zs_cpu_up(struct mapping_area *area) +{ + /* + * Make sure we don't leak memory if a cpu UP notification + * and zs_init() race and both call zs_cpu_up() on the same cpu + */ + if (area->vm) + return 0; + area->vm = alloc_vm_area(PAGE_SIZE * 2, NULL); + if (!area->vm) + return -ENOMEM; + return 0; +} + +static inline void __zs_cpu_down(struct mapping_area *area) +{ + if (area->vm) + free_vm_area(area->vm); + area->vm = NULL; +} + +static inline void *__zs_map_object(struct mapping_area *area, + struct page *pages[2], int off, int size) +{ + BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, &pages)); + area->vm_addr = area->vm->addr; + return area->vm_addr + off; +} + +static inline void __zs_unmap_object(struct mapping_area *area, + struct page *pages[2], int off, int size) +{ + unsigned long addr = (unsigned long)area->vm_addr; + + unmap_kernel_range(addr, PAGE_SIZE * 2); +} + +#else /* CONFIG_PGTABLE_MAPPING */ + +static inline int __zs_cpu_up(struct mapping_area *area) +{ + /* + * Make sure we don't leak memory if a cpu UP notification + * and zs_init() race and both call zs_cpu_up() on the same cpu + */ + if (area->vm_buf) + return 0; + area->vm_buf = (char *)__get_free_page(GFP_KERNEL); + if (!area->vm_buf) + return -ENOMEM; + return 0; +} + +static inline void __zs_cpu_down(struct mapping_area *area) +{ + if (area->vm_buf) + free_page((unsigned long)area->vm_buf); + area->vm_buf = NULL; +} + +static void *__zs_map_object(struct mapping_area *area, + struct page *pages[2], int off, int size) +{ + int sizes[2]; + void *addr; + char *buf = area->vm_buf; + + /* disable page faults to match kmap_atomic() return conditions */ + pagefault_disable(); + + /* no read fastpath */ + if (area->vm_mm == ZS_MM_WO) + goto out; + + sizes[0] = PAGE_SIZE - off; + sizes[1] = size - sizes[0]; + + /* copy object to per-cpu buffer */ + addr = kmap_atomic(pages[0]); + memcpy(buf, addr + off, sizes[0]); + kunmap_atomic(addr); + addr = kmap_atomic(pages[1]); + memcpy(buf + sizes[0], addr, sizes[1]); + kunmap_atomic(addr); +out: + return area->vm_buf; +} + +static void __zs_unmap_object(struct mapping_area *area, + struct page *pages[2], int off, int size) +{ + int sizes[2]; + void *addr; + char *buf = area->vm_buf; + + /* no write fastpath */ + if (area->vm_mm == ZS_MM_RO) + goto out; + + sizes[0] = PAGE_SIZE - off; + sizes[1] = size - sizes[0]; + + /* copy per-cpu buffer to object */ + addr = kmap_atomic(pages[0]); + memcpy(addr + off, buf, sizes[0]); + kunmap_atomic(addr); + addr = kmap_atomic(pages[1]); + memcpy(addr, buf + sizes[0], sizes[1]); + kunmap_atomic(addr); + +out: + /* enable page faults to match kunmap_atomic() return conditions */ + pagefault_enable(); +} + +#endif /* CONFIG_PGTABLE_MAPPING */ + +static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action, + void *pcpu) +{ + int ret, cpu = (long)pcpu; + struct mapping_area *area; + + switch (action) { + case CPU_UP_PREPARE: + area = &per_cpu(zs_map_area, cpu); + ret = __zs_cpu_up(area); + if (ret) + return notifier_from_errno(ret); + break; + case CPU_DEAD: + case CPU_UP_CANCELED: + area = &per_cpu(zs_map_area, cpu); + __zs_cpu_down(area); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block zs_cpu_nb = { + .notifier_call = zs_cpu_notifier +}; + +static void zs_exit(void) +{ + int cpu; + + for_each_online_cpu(cpu) + zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu); + unregister_cpu_notifier(&zs_cpu_nb); +} + +static int zs_init(void) +{ + int cpu, ret; + + register_cpu_notifier(&zs_cpu_nb); + for_each_online_cpu(cpu) { + ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu); + if (notifier_to_errno(ret)) + goto fail; + } + return 0; +fail: + zs_exit(); + return notifier_to_errno(ret); +} + +/** + * zs_create_pool - Creates an allocation pool to work from. + * @flags: allocation flags used to allocate pool metadata + * + * This function must be called before anything when using + * the zsmalloc allocator. + * + * On success, a pointer to the newly created pool is returned, + * otherwise NULL. + */ +struct zs_pool *zs_create_pool(gfp_t flags) +{ + int i, ovhd_size; + struct zs_pool *pool; + + ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); + pool = kzalloc(ovhd_size, GFP_KERNEL); + if (!pool) + return NULL; + + for (i = 0; i < ZS_SIZE_CLASSES; i++) { + int size; + struct size_class *class; + + size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; + if (size > ZS_MAX_ALLOC_SIZE) + size = ZS_MAX_ALLOC_SIZE; + + class = &pool->size_class[i]; + class->size = size; + class->index = i; + spin_lock_init(&class->lock); + class->pages_per_zspage = get_pages_per_zspage(size); + + } + + pool->flags = flags; + + return pool; +} +EXPORT_SYMBOL_GPL(zs_create_pool); + +void zs_destroy_pool(struct zs_pool *pool) +{ + int i; + + for (i = 0; i < ZS_SIZE_CLASSES; i++) { + int fg; + struct size_class *class = &pool->size_class[i]; + + for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { + if (class->fullness_list[fg]) { + pr_info("Freeing non-empty class with size %db, fullness group %d\n", + class->size, fg); + } + } + } + kfree(pool); +} +EXPORT_SYMBOL_GPL(zs_destroy_pool); + +/** + * zs_malloc - Allocate block of given size from pool. + * @pool: pool to allocate from + * @size: size of block to allocate + * + * On success, handle to the allocated object is returned, + * otherwise 0. + * Allocation requests with size > ZS_MAX_ALLOC_SIZE will fail. + */ +unsigned long zs_malloc(struct zs_pool *pool, size_t size) +{ + unsigned long obj; + struct link_free *link; + int class_idx; + struct size_class *class; + + struct page *first_page, *m_page; + unsigned long m_objidx, m_offset; + + if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) + return 0; + + class_idx = get_size_class_index(size); + class = &pool->size_class[class_idx]; + BUG_ON(class_idx != class->index); + + spin_lock(&class->lock); + first_page = find_get_zspage(class); + + if (!first_page) { + spin_unlock(&class->lock); + first_page = alloc_zspage(class, pool->flags); + if (unlikely(!first_page)) + return 0; + + set_zspage_mapping(first_page, class->index, ZS_EMPTY); + spin_lock(&class->lock); + class->pages_allocated += class->pages_per_zspage; + } + + obj = (unsigned long)first_page->freelist; + obj_handle_to_location(obj, &m_page, &m_objidx); + m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); + + link = (struct link_free *)kmap_atomic(m_page) + + m_offset / sizeof(*link); + first_page->freelist = link->next; + memset(link, POISON_INUSE, sizeof(*link)); + kunmap_atomic(link); + + first_page->inuse++; + /* Now move the zspage to another fullness group, if required */ + fix_fullness_group(pool, first_page); + spin_unlock(&class->lock); + + return obj; +} +EXPORT_SYMBOL_GPL(zs_malloc); + +void zs_free(struct zs_pool *pool, unsigned long obj) +{ + struct link_free *link; + struct page *first_page, *f_page; + unsigned long f_objidx, f_offset; + + int class_idx; + struct size_class *class; + enum fullness_group fullness; + + if (unlikely(!obj)) + return; + + obj_handle_to_location(obj, &f_page, &f_objidx); + first_page = get_first_page(f_page); + + get_zspage_mapping(first_page, &class_idx, &fullness); + class = &pool->size_class[class_idx]; + f_offset = obj_idx_to_offset(f_page, f_objidx, class->size); + + spin_lock(&class->lock); + + /* Insert this object in containing zspage's freelist */ + link = (struct link_free *)((unsigned char *)kmap_atomic(f_page) + + f_offset); + link->next = first_page->freelist; + kunmap_atomic(link); + first_page->freelist = (void *)obj; + + first_page->inuse--; + fullness = fix_fullness_group(pool, first_page); + + if (fullness == ZS_EMPTY) + class->pages_allocated -= class->pages_per_zspage; + + spin_unlock(&class->lock); + + if (fullness == ZS_EMPTY) + free_zspage(first_page); +} +EXPORT_SYMBOL_GPL(zs_free); + +/** + * zs_map_object - get address of allocated object from handle. + * @pool: pool from which the object was allocated + * @handle: handle returned from zs_malloc + * + * Before using an object allocated from zs_malloc, it must be mapped using + * this function. When done with the object, it must be unmapped using + * zs_unmap_object. + * + * Only one object can be mapped per cpu at a time. There is no protection + * against nested mappings. + * + * This function returns with preemption and page faults disabled. + */ +void *zs_map_object(struct zs_pool *pool, unsigned long handle, + enum zs_mapmode mm) +{ + struct page *page; + unsigned long obj_idx, off; + + unsigned int class_idx; + enum fullness_group fg; + struct size_class *class; + struct mapping_area *area; + struct page *pages[2]; + + BUG_ON(!handle); + + /* + * Because we use per-cpu mapping areas shared among the + * pools/users, we can't allow mapping in interrupt context + * because it can corrupt another users mappings. + */ + BUG_ON(in_interrupt()); + + obj_handle_to_location(handle, &page, &obj_idx); + get_zspage_mapping(get_first_page(page), &class_idx, &fg); + class = &pool->size_class[class_idx]; + off = obj_idx_to_offset(page, obj_idx, class->size); + + area = &get_cpu_var(zs_map_area); + area->vm_mm = mm; + if (off + class->size <= PAGE_SIZE) { + /* this object is contained entirely within a page */ + area->vm_addr = kmap_atomic(page); + return area->vm_addr + off; + } + + /* this object spans two pages */ + pages[0] = page; + pages[1] = get_next_page(page); + BUG_ON(!pages[1]); + + return __zs_map_object(area, pages, off, class->size); +} +EXPORT_SYMBOL_GPL(zs_map_object); + +void zs_unmap_object(struct zs_pool *pool, unsigned long handle) +{ + struct page *page; + unsigned long obj_idx, off; + + unsigned int class_idx; + enum fullness_group fg; + struct size_class *class; + struct mapping_area *area; + + BUG_ON(!handle); + + obj_handle_to_location(handle, &page, &obj_idx); + get_zspage_mapping(get_first_page(page), &class_idx, &fg); + class = &pool->size_class[class_idx]; + off = obj_idx_to_offset(page, obj_idx, class->size); + + area = &__get_cpu_var(zs_map_area); + if (off + class->size <= PAGE_SIZE) + kunmap_atomic(area->vm_addr); + else { + struct page *pages[2]; + + pages[0] = page; + pages[1] = get_next_page(page); + BUG_ON(!pages[1]); + + __zs_unmap_object(area, pages, off, class->size); + } + put_cpu_var(zs_map_area); +} +EXPORT_SYMBOL_GPL(zs_unmap_object); + +u64 zs_get_total_size_bytes(struct zs_pool *pool) +{ + int i; + u64 npages = 0; + + for (i = 0; i < ZS_SIZE_CLASSES; i++) + npages += pool->size_class[i].pages_allocated; + + return npages << PAGE_SHIFT; +} +EXPORT_SYMBOL_GPL(zs_get_total_size_bytes); + +module_init(zs_init); +module_exit(zs_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Nitin Gupta "); -- cgit v1.1 From 5636a0d05a0441c9eb503f9cf56e6a961e979a7f Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:45:55 -0800 Subject: zsmalloc: add copyright Add my copyright to the zsmalloc source code which I maintain. Signed-off-by: Minchan Kim Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/zsmalloc.h | 1 + mm/zsmalloc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/linux/zsmalloc.h b/include/linux/zsmalloc.h index c2eb174..e44d634 100644 --- a/include/linux/zsmalloc.h +++ b/include/linux/zsmalloc.h @@ -2,6 +2,7 @@ * zsmalloc memory allocator * * Copyright (C) 2011 Nitin Gupta + * Copyright (C) 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the license that better fits your requirements. diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 6c60592..4d44c1e 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -2,6 +2,7 @@ * zsmalloc memory allocator * * Copyright (C) 2011 Nitin Gupta + * Copyright (C) 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the license that better fits your requirements. -- cgit v1.1 From 47176d5791a5cb11550172171f4f6c4ed4ec5a92 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:45:52 -0800 Subject: zram: promote zram from staging Zram has lived in staging for a LONG LONG time and have been fixed/improved by many contributors so code is clean and stable now. Of course, there are lots of product using zram in real practice. The major TV companys have used zram as swap since two years ago and recently our production team released android smart phone with zram which is used as swap, too and recently Android Kitkat start to use zram for small memory smart phone. And there was a report Google released their ChromeOS with zram, too and cyanogenmod have been used zram long time ago. And I heard some disto have used zram block device for tmpfs. In addition, I saw many report from many other peoples. For example, Lubuntu start to use it. The benefit of zram is very clear. With my experience, one of the benefit was to remove jitter of video application with backgroud memory pressure. It would be effect of efficient memory usage by compression but more issue is whether swap is there or not in the system. Recent mobile platforms have used JAVA so there are many anonymous pages. But embedded system normally are reluctant to use eMMC or SDCard as swap because there is wear-leveling and latency issues so if we do not use swap, it means we can't reclaim anoymous pages and at last, we could encounter OOM kill. :( Although we have real storage as swap, it was a problem, too. Because it sometime ends up making system very unresponsible caused by slow swap storage performance. Quote from Luigi on Google "Since Chrome OS was mentioned: the main reason why we don't use swap to a disk (rotating or SSD) is because it doesn't degrade gracefully and leads to a bad interactive experience. Generally we prefer to manage RAM at a higher level, by transparently killing and restarting processes. But we noticed that zram is fast enough to be competitive with the latter, and it lets us make more efficient use of the available RAM. " and he announced. http://www.spinics.net/lists/linux-mm/msg57717.html Other uses case is to use zram for block device. Zram is block device so anyone can format the block device and mount on it so some guys on the internet start zram as /var/tmp. http://forums.gentoo.org/viewtopic-t-838198-start-0.html Let's promote zram and enhance/maintain it instead of removing. Change-Id: Ie8f4e47eb9b74f4269da921eb6c709964fb6753e Signed-off-by: Minchan Kim Reviewed-by: Konrad Rzeszutek Wilk Acked-by: Nitin Gupta Acked-by: Pekka Enberg Cc: Bob Liu Cc: Greg Kroah-Hartman Cc: Hugh Dickins Cc: Jens Axboe Cc: Luigi Semenzato Cc: Mel Gorman Cc: Rik van Riel Cc: Seth Jennings Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: drivers/block/Kconfig drivers/staging/Kconfig drivers/staging/Makefile --- Documentation/blockdev/zram.txt | 77 +++ drivers/block/Kconfig | 2 + drivers/block/Makefile | 1 + drivers/block/zram/Kconfig | 25 + drivers/block/zram/Makefile | 3 + drivers/block/zram/zram_drv.c | 1004 +++++++++++++++++++++++++++++++++++++++ drivers/block/zram/zram_drv.h | 124 +++++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/zram/Kconfig | 25 - drivers/staging/zram/Makefile | 3 - drivers/staging/zram/zram.txt | 77 --- drivers/staging/zram/zram_drv.c | 1004 --------------------------------------- drivers/staging/zram/zram_drv.h | 124 ----- 14 files changed, 1236 insertions(+), 1236 deletions(-) create mode 100644 Documentation/blockdev/zram.txt create mode 100644 drivers/block/zram/Kconfig create mode 100644 drivers/block/zram/Makefile create mode 100644 drivers/block/zram/zram_drv.c create mode 100644 drivers/block/zram/zram_drv.h delete mode 100644 drivers/staging/zram/Kconfig delete mode 100644 drivers/staging/zram/Makefile delete mode 100644 drivers/staging/zram/zram.txt delete mode 100644 drivers/staging/zram/zram_drv.c delete mode 100644 drivers/staging/zram/zram_drv.h diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt new file mode 100644 index 0000000..765d790 --- /dev/null +++ b/Documentation/blockdev/zram.txt @@ -0,0 +1,77 @@ +zram: Compressed RAM based block devices +---------------------------------------- + +Project home: http://compcache.googlecode.com/ + +* Introduction + +The zram module creates RAM based block devices named /dev/zram +( = 0, 1, ...). Pages written to these disks are compressed and stored +in memory itself. These disks allow very fast I/O and compression provides +good amounts of memory savings. Some of the usecases include /tmp storage, +use as swap disks, various caches under /var and maybe many more :) + +Statistics for individual zram devices are exported through sysfs nodes at +/sys/block/zram/ + +* Usage + +Following shows a typical sequence of steps for using zram. + +1) Load Module: + modprobe zram num_devices=4 + This creates 4 devices: /dev/zram{0,1,2,3} + (num_devices parameter is optional. Default: 1) + +2) Set Disksize + Set disk size by writing the value to sysfs node 'disksize'. + The value can be either in bytes or you can use mem suffixes. + Examples: + # Initialize /dev/zram0 with 50MB disksize + echo $((50*1024*1024)) > /sys/block/zram0/disksize + + # Using mem suffixes + echo 256K > /sys/block/zram0/disksize + echo 512M > /sys/block/zram0/disksize + echo 1G > /sys/block/zram0/disksize + +3) Activate: + mkswap /dev/zram0 + swapon /dev/zram0 + + mkfs.ext4 /dev/zram1 + mount /dev/zram1 /tmp + +4) Stats: + Per-device statistics are exported as various nodes under + /sys/block/zram/ + disksize + num_reads + num_writes + invalid_io + notify_free + discard + zero_pages + orig_data_size + compr_data_size + mem_used_total + +5) Deactivate: + swapoff /dev/zram0 + umount /dev/zram1 + +6) Reset: + Write any positive value to 'reset' sysfs node + echo 1 > /sys/block/zram0/reset + echo 1 > /sys/block/zram1/reset + + This frees all the memory allocated for the given device and + resets the disksize to zero. You must set the disksize again + before reusing the device. + +Please report any problems at: + - Mailing list: linux-mm-cc at laptop dot org + - Issue tracker: http://code.google.com/p/compcache/issues/list + +Nitin Gupta +ngupta@vflare.org diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 717d6e4..b7ac932 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -116,6 +116,8 @@ config PARIDE source "drivers/block/paride/Kconfig" +source "drivers/block/zram/Kconfig" + config BLK_CPQ_DA tristate "Compaq SMART2 support" depends on PCI && VIRT_TO_BUS diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 76646e9..edc9543 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -40,4 +40,5 @@ obj-$(CONFIG_XEN_BLKDEV_BACKEND) += xen-blkback/ obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ obj-$(CONFIG_BLK_DEV_RBD) += rbd.o +obj-$(CONFIG_ZRAM) += zram/ swim_mod-y := swim.o swim_asm.o diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig new file mode 100644 index 0000000..983314c --- /dev/null +++ b/drivers/block/zram/Kconfig @@ -0,0 +1,25 @@ +config ZRAM + tristate "Compressed RAM block device support" + depends on BLOCK && SYSFS && ZSMALLOC + select LZO_COMPRESS + select LZO_DECOMPRESS + default n + help + Creates virtual block devices called /dev/zramX (X = 0, 1, ...). + Pages written to these disks are compressed and stored in memory + itself. These disks allow very fast I/O and compression provides + good amounts of memory savings. + + It has several use cases, for example: /tmp storage, use as swap + disks and maybe many more. + + See zram.txt for more information. + Project home: + +config ZRAM_DEBUG + bool "Compressed RAM block device debug support" + depends on ZRAM + default n + help + This option adds additional debugging code to the compressed + RAM block device driver. diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile new file mode 100644 index 0000000..cb0f9ce --- /dev/null +++ b/drivers/block/zram/Makefile @@ -0,0 +1,3 @@ +zram-y := zram_drv.o + +obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c new file mode 100644 index 0000000..d22e5ad --- /dev/null +++ b/drivers/block/zram/zram_drv.c @@ -0,0 +1,1004 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + * Project home: http://compcache.googlecode.com + */ + +#define KMSG_COMPONENT "zram" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#ifdef CONFIG_ZRAM_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zram_drv.h" + +/* Globals */ +static int zram_major; +static struct zram *zram_devices; + +/* + * We don't need to see memory allocation errors more than once every 1 + * second to know that a problem is occurring. + */ +#define ALLOC_ERROR_LOG_RATE_MS 1000 + +/* Module params (documentation at end) */ +static unsigned int num_devices = 1; + +static inline struct zram *dev_to_zram(struct device *dev) +{ + return (struct zram *)dev_to_disk(dev)->private_data; +} + +static ssize_t disksize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", zram->disksize); +} + +static ssize_t initstate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%u\n", zram->init_done); +} + +static ssize_t num_reads_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.num_reads)); +} + +static ssize_t num_writes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.num_writes)); +} + +static ssize_t invalid_io_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.invalid_io)); +} + +static ssize_t notify_free_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.notify_free)); +} + +static ssize_t zero_pages_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%u\n", zram->stats.pages_zero); +} + +static ssize_t orig_data_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)(zram->stats.pages_stored) << PAGE_SHIFT); +} + +static ssize_t compr_data_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return sprintf(buf, "%llu\n", + (u64)atomic64_read(&zram->stats.compr_size)); +} + +static ssize_t mem_used_total_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u64 val = 0; + struct zram *zram = dev_to_zram(dev); + struct zram_meta *meta = zram->meta; + + down_read(&zram->init_lock); + if (zram->init_done) + val = zs_get_total_size_bytes(meta->mem_pool); + up_read(&zram->init_lock); + + return sprintf(buf, "%llu\n", val); +} + +static int zram_test_flag(struct zram_meta *meta, u32 index, + enum zram_pageflags flag) +{ + return meta->table[index].flags & BIT(flag); +} + +static void zram_set_flag(struct zram_meta *meta, u32 index, + enum zram_pageflags flag) +{ + meta->table[index].flags |= BIT(flag); +} + +static void zram_clear_flag(struct zram_meta *meta, u32 index, + enum zram_pageflags flag) +{ + meta->table[index].flags &= ~BIT(flag); +} + +static inline int is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} + +/* + * Check if request is within bounds and aligned on zram logical blocks. + */ +static inline int valid_io_request(struct zram *zram, struct bio *bio) +{ + u64 start, end, bound; + + /* unaligned request */ + if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) + return 0; + if (unlikely(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) + return 0; + + start = bio->bi_sector; + end = start + (bio->bi_size >> SECTOR_SHIFT); + bound = zram->disksize >> SECTOR_SHIFT; + /* out of range range */ + if (unlikely(start >= bound || end > bound || start > end)) + return 0; + + /* I/O request is valid */ + return 1; +} + +static void zram_meta_free(struct zram_meta *meta) +{ + zs_destroy_pool(meta->mem_pool); + kfree(meta->compress_workmem); + free_pages((unsigned long)meta->compress_buffer, 1); + vfree(meta->table); + kfree(meta); +} + +static struct zram_meta *zram_meta_alloc(u64 disksize) +{ + size_t num_pages; + struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if (!meta) + goto out; + + meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + if (!meta->compress_workmem) + goto free_meta; + + meta->compress_buffer = + (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!meta->compress_buffer) { + pr_err("Error allocating compressor buffer space\n"); + goto free_workmem; + } + + num_pages = disksize >> PAGE_SHIFT; + meta->table = vzalloc(num_pages * sizeof(*meta->table)); + if (!meta->table) { + pr_err("Error allocating zram address table\n"); + goto free_buffer; + } + + meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM | + __GFP_NOWARN); + if (!meta->mem_pool) { + pr_err("Error creating memory pool\n"); + goto free_table; + } + + return meta; + +free_table: + vfree(meta->table); +free_buffer: + free_pages((unsigned long)meta->compress_buffer, 1); +free_workmem: + kfree(meta->compress_workmem); +free_meta: + kfree(meta); + meta = NULL; +out: + return meta; +} + +static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +{ + if (*offset + bvec->bv_len >= PAGE_SIZE) + (*index)++; + *offset = (*offset + bvec->bv_len) % PAGE_SIZE; +} + +static int page_zero_filled(void *ptr) +{ + unsigned int pos; + unsigned long *page; + + page = (unsigned long *)ptr; + + for (pos = 0; pos != PAGE_SIZE / sizeof(*page); pos++) { + if (page[pos]) + return 0; + } + + return 1; +} + +static void handle_zero_page(struct bio_vec *bvec) +{ + struct page *page = bvec->bv_page; + void *user_mem; + + user_mem = kmap_atomic(page); + if (is_partial_io(bvec)) + memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); + else + clear_page(user_mem); + kunmap_atomic(user_mem); + + flush_dcache_page(page); +} + +static void zram_free_page(struct zram *zram, size_t index) +{ + struct zram_meta *meta = zram->meta; + unsigned long handle = meta->table[index].handle; + u16 size = meta->table[index].size; + + if (unlikely(!handle)) { + /* + * No memory is allocated for zero filled pages. + * Simply clear zero page flag. + */ + if (zram_test_flag(meta, index, ZRAM_ZERO)) { + zram_clear_flag(meta, index, ZRAM_ZERO); + zram->stats.pages_zero--; + } + return; + } + + if (unlikely(size > max_zpage_size)) + zram->stats.bad_compress--; + + zs_free(meta->mem_pool, handle); + + if (size <= PAGE_SIZE / 2) + zram->stats.good_compress--; + + atomic64_sub(meta->table[index].size, &zram->stats.compr_size); + zram->stats.pages_stored--; + + meta->table[index].handle = 0; + meta->table[index].size = 0; +} + +static int zram_decompress_page(struct zram *zram, char *mem, u32 index) +{ + int ret = LZO_E_OK; + size_t clen = PAGE_SIZE; + unsigned char *cmem; + struct zram_meta *meta = zram->meta; + unsigned long handle = meta->table[index].handle; + + if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { + clear_page(mem); + return 0; + } + + cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); + if (meta->table[index].size == PAGE_SIZE) + copy_page(mem, cmem); + else + ret = lzo1x_decompress_safe(cmem, meta->table[index].size, + mem, &clen); + zs_unmap_object(meta->mem_pool, handle); + + /* Should NEVER happen. Return bio error if it does. */ + if (unlikely(ret != LZO_E_OK)) { + pr_err("Decompression failed! err=%d, page=%u\n", ret, index); + atomic64_inc(&zram->stats.failed_reads); + return ret; + } + + return 0; +} + +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page; + unsigned char *user_mem, *uncmem = NULL; + struct zram_meta *meta = zram->meta; + page = bvec->bv_page; + + if (unlikely(!meta->table[index].handle) || + zram_test_flag(meta, index, ZRAM_ZERO)) { + handle_zero_page(bvec); + return 0; + } + + if (is_partial_io(bvec)) + /* Use a temporary buffer to decompress the page */ + uncmem = kmalloc(PAGE_SIZE, GFP_NOIO); + + user_mem = kmap_atomic(page); + if (!is_partial_io(bvec)) + uncmem = user_mem; + + if (!uncmem) { + pr_info("Unable to allocate temp memory\n"); + ret = -ENOMEM; + goto out_cleanup; + } + + ret = zram_decompress_page(zram, uncmem, index); + /* Should NEVER happen. Return bio error if it does. */ + if (unlikely(ret != LZO_E_OK)) + goto out_cleanup; + + if (is_partial_io(bvec)) + memcpy(user_mem + bvec->bv_offset, uncmem + offset, + bvec->bv_len); + + flush_dcache_page(page); + ret = 0; +out_cleanup: + kunmap_atomic(user_mem); + if (is_partial_io(bvec)) + kfree(uncmem); + return ret; +} + +static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset) +{ + int ret = 0; + size_t clen; + unsigned long handle; + struct page *page; + unsigned char *user_mem, *cmem, *src, *uncmem = NULL; + struct zram_meta *meta = zram->meta; + static unsigned long zram_rs_time; + + page = bvec->bv_page; + src = meta->compress_buffer; + + if (is_partial_io(bvec)) { + /* + * This is a partial IO. We need to read the full page + * before to write the changes. + */ + uncmem = kmalloc(PAGE_SIZE, GFP_NOIO); + if (!uncmem) { + ret = -ENOMEM; + goto out; + } + ret = zram_decompress_page(zram, uncmem, index); + if (ret) + goto out; + } + + user_mem = kmap_atomic(page); + + if (is_partial_io(bvec)) { + memcpy(uncmem + offset, user_mem + bvec->bv_offset, + bvec->bv_len); + kunmap_atomic(user_mem); + user_mem = NULL; + } else { + uncmem = user_mem; + } + + if (page_zero_filled(uncmem)) { + kunmap_atomic(user_mem); + /* Free memory associated with this sector now. */ + zram_free_page(zram, index); + + zram->stats.pages_zero++; + zram_set_flag(meta, index, ZRAM_ZERO); + ret = 0; + goto out; + } + + /* + * zram_slot_free_notify could miss free so that let's + * double check. + */ + if (unlikely(meta->table[index].handle || + zram_test_flag(meta, index, ZRAM_ZERO))) + zram_free_page(zram, index); + + ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, + meta->compress_workmem); + + if (!is_partial_io(bvec)) { + kunmap_atomic(user_mem); + user_mem = NULL; + uncmem = NULL; + } + + if (unlikely(ret != LZO_E_OK)) { + pr_err("Compression failed! err=%d\n", ret); + goto out; + } + + if (unlikely(clen > max_zpage_size)) { + zram->stats.bad_compress++; + clen = PAGE_SIZE; + src = NULL; + if (is_partial_io(bvec)) + src = uncmem; + } + + handle = zs_malloc(meta->mem_pool, clen); + if (!handle) { + if (printk_timed_ratelimit(&zram_rs_time, + ALLOC_ERROR_LOG_RATE_MS)) + pr_info("Error allocating memory for compressed page: %u, size=%zu\n", + index, clen); + ret = -ENOMEM; + goto out; + } + cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO); + + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) { + src = kmap_atomic(page); + copy_page(cmem, src); + kunmap_atomic(src); + } else { + memcpy(cmem, src, clen); + } + + zs_unmap_object(meta->mem_pool, handle); + + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_free_page(zram, index); + + meta->table[index].handle = handle; + meta->table[index].size = clen; + + /* Update stats */ + atomic64_add(clen, &zram->stats.compr_size); + zram->stats.pages_stored++; + if (clen <= PAGE_SIZE / 2) + zram->stats.good_compress++; + +out: + if (is_partial_io(bvec)) + kfree(uncmem); + + if (ret) + atomic64_inc(&zram->stats.failed_writes); + return ret; +} + +static void handle_pending_slot_free(struct zram *zram) +{ + struct zram_slot_free *free_rq; + + spin_lock(&zram->slot_free_lock); + while (zram->slot_free_rq) { + free_rq = zram->slot_free_rq; + zram->slot_free_rq = free_rq->next; + zram_free_page(zram, free_rq->index); + kfree(free_rq); + } + spin_unlock(&zram->slot_free_lock); +} + +static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset, struct bio *bio, int rw) +{ + int ret; + + if (rw == READ) { + down_read(&zram->lock); + handle_pending_slot_free(zram); + ret = zram_bvec_read(zram, bvec, index, offset, bio); + up_read(&zram->lock); + } else { + down_write(&zram->lock); + handle_pending_slot_free(zram); + ret = zram_bvec_write(zram, bvec, index, offset); + up_write(&zram->lock); + } + + return ret; +} + +static void zram_reset_device(struct zram *zram, bool reset_capacity) +{ + size_t index; + struct zram_meta *meta; + + flush_work(&zram->free_work); + + down_write(&zram->init_lock); + if (!zram->init_done) { + up_write(&zram->init_lock); + return; + } + + meta = zram->meta; + zram->init_done = 0; + + /* Free all pages that are still in this zram device */ + for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { + unsigned long handle = meta->table[index].handle; + if (!handle) + continue; + + zs_free(meta->mem_pool, handle); + } + + zram_meta_free(zram->meta); + zram->meta = NULL; + /* Reset stats */ + memset(&zram->stats, 0, sizeof(zram->stats)); + + zram->disksize = 0; + if (reset_capacity) + set_capacity(zram->disk, 0); + up_write(&zram->init_lock); +} + +static void zram_init_device(struct zram *zram, struct zram_meta *meta) +{ + if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { + pr_info( + "There is little point creating a zram of greater than " + "twice the size of memory since we expect a 2:1 compression " + "ratio. Note that zram uses about 0.1%% of the size of " + "the disk when not in use so a huge zram is " + "wasteful.\n" + "\tMemory Size: %lu kB\n" + "\tSize you selected: %llu kB\n" + "Continuing anyway ...\n", + (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 + ); + } + + /* zram devices sort of resembles non-rotational disks */ + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); + + zram->meta = meta; + zram->init_done = 1; + + pr_debug("Initialization done!\n"); +} + +static ssize_t disksize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 disksize; + struct zram_meta *meta; + struct zram *zram = dev_to_zram(dev); + + disksize = memparse(buf, NULL); + if (!disksize) + return -EINVAL; + + disksize = PAGE_ALIGN(disksize); + meta = zram_meta_alloc(disksize); + down_write(&zram->init_lock); + if (zram->init_done) { + up_write(&zram->init_lock); + zram_meta_free(meta); + pr_info("Cannot change disksize for initialized device\n"); + return -EBUSY; + } + + zram->disksize = disksize; + set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + zram_init_device(zram, meta); + up_write(&zram->init_lock); + + return len; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned short do_reset; + struct zram *zram; + struct block_device *bdev; + + zram = dev_to_zram(dev); + bdev = bdget_disk(zram->disk, 0); + + if (!bdev) + return -ENOMEM; + + /* Do not reset an active device! */ + if (bdev->bd_holders) { + ret = -EBUSY; + goto out; + } + + ret = kstrtou16(buf, 10, &do_reset); + if (ret) + goto out; + + if (!do_reset) { + ret = -EINVAL; + goto out; + } + + /* Make sure all pending I/O is finished */ + fsync_bdev(bdev); + bdput(bdev); + + zram_reset_device(zram, true); + return len; + +out: + bdput(bdev); + return ret; +} + +static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) +{ + int i, offset; + u32 index; + struct bio_vec *bvec; + + switch (rw) { + case READ: + atomic64_inc(&zram->stats.num_reads); + break; + case WRITE: + atomic64_inc(&zram->stats.num_writes); + break; + } + + index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + bio_for_each_segment(bvec, bio, i) { + int max_transfer_size = PAGE_SIZE - offset; + + if (bvec->bv_len > max_transfer_size) { + /* + * zram_bvec_rw() can only make operation on a single + * zram page. Split the bio vector. + */ + struct bio_vec bv; + + bv.bv_page = bvec->bv_page; + bv.bv_len = max_transfer_size; + bv.bv_offset = bvec->bv_offset; + + if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0) + goto out; + + bv.bv_len = bvec->bv_len - max_transfer_size; + bv.bv_offset += max_transfer_size; + if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0) + goto out; + } else + if (zram_bvec_rw(zram, bvec, index, offset, bio, rw) + < 0) + goto out; + + update_position(&index, &offset, bvec); + } + + set_bit(BIO_UPTODATE, &bio->bi_flags); + bio_endio(bio, 0); + return; + +out: + bio_io_error(bio); +} + +/* + * Handler function for all zram I/O requests. + */ +static int zram_make_request(struct request_queue *queue, struct bio *bio) +{ + struct zram *zram = queue->queuedata; + + down_read(&zram->init_lock); + if (unlikely(!zram->init_done)) + goto error; + + if (!valid_io_request(zram, bio)) { + atomic64_inc(&zram->stats.invalid_io); + goto error; + } + + __zram_make_request(zram, bio, bio_data_dir(bio)); + up_read(&zram->init_lock); + + return 0; + +error: + up_read(&zram->init_lock); + bio_io_error(bio); + + return 0; +} + +static void zram_slot_free(struct work_struct *work) +{ + struct zram *zram; + + zram = container_of(work, struct zram, free_work); + down_write(&zram->lock); + handle_pending_slot_free(zram); + up_write(&zram->lock); +} + +static void add_slot_free(struct zram *zram, struct zram_slot_free *free_rq) +{ + spin_lock(&zram->slot_free_lock); + free_rq->next = zram->slot_free_rq; + zram->slot_free_rq = free_rq; + spin_unlock(&zram->slot_free_lock); +} + +static void zram_slot_free_notify(struct block_device *bdev, + unsigned long index) +{ + struct zram *zram; + struct zram_slot_free *free_rq; + + zram = bdev->bd_disk->private_data; + atomic64_inc(&zram->stats.notify_free); + + free_rq = kmalloc(sizeof(struct zram_slot_free), GFP_ATOMIC); + if (!free_rq) + return; + + free_rq->index = index; + add_slot_free(zram, free_rq); + schedule_work(&zram->free_work); +} + +static const struct block_device_operations zram_devops = { + .swap_slot_free_notify = zram_slot_free_notify, + .owner = THIS_MODULE +}; + +static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR, + disksize_show, disksize_store); +static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL); +static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); +static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL); +static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL); +static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL); +static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL); +static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL); +static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); +static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL); +static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); + +static struct attribute *zram_disk_attrs[] = { + &dev_attr_disksize.attr, + &dev_attr_initstate.attr, + &dev_attr_reset.attr, + &dev_attr_num_reads.attr, + &dev_attr_num_writes.attr, + &dev_attr_invalid_io.attr, + &dev_attr_notify_free.attr, + &dev_attr_zero_pages.attr, + &dev_attr_orig_data_size.attr, + &dev_attr_compr_data_size.attr, + &dev_attr_mem_used_total.attr, + NULL, +}; + +static struct attribute_group zram_disk_attr_group = { + .attrs = zram_disk_attrs, +}; + +static int create_device(struct zram *zram, int device_id) +{ + int ret = -ENOMEM; + + init_rwsem(&zram->lock); + init_rwsem(&zram->init_lock); + + INIT_WORK(&zram->free_work, zram_slot_free); + spin_lock_init(&zram->slot_free_lock); + zram->slot_free_rq = NULL; + + zram->queue = blk_alloc_queue(GFP_KERNEL); + if (!zram->queue) { + pr_err("Error allocating disk queue for device %d\n", + device_id); + goto out; + } + + blk_queue_make_request(zram->queue, zram_make_request); + zram->queue->queuedata = zram; + + /* gendisk structure */ + zram->disk = alloc_disk(1); + if (!zram->disk) { + pr_warn("Error allocating disk structure for device %d\n", + device_id); + goto out_free_queue; + } + + zram->disk->major = zram_major; + zram->disk->first_minor = device_id; + zram->disk->fops = &zram_devops; + zram->disk->queue = zram->queue; + zram->disk->private_data = zram; + snprintf(zram->disk->disk_name, 16, "zram%d", device_id); + + /* Actual capacity set using syfs (/sys/block/zram/disksize */ + set_capacity(zram->disk, 0); + + /* + * To ensure that we always get PAGE_SIZE aligned + * and n*PAGE_SIZED sized I/O requests. + */ + blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE); + blk_queue_logical_block_size(zram->disk->queue, + ZRAM_LOGICAL_BLOCK_SIZE); + blk_queue_io_min(zram->disk->queue, PAGE_SIZE); + blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); + + add_disk(zram->disk); + + ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj, + &zram_disk_attr_group); + if (ret < 0) { + pr_warn("Error creating sysfs group"); + goto out_free_disk; + } + + zram->init_done = 0; + return 0; + +out_free_disk: + del_gendisk(zram->disk); + put_disk(zram->disk); +out_free_queue: + blk_cleanup_queue(zram->queue); +out: + return ret; +} + +static void destroy_device(struct zram *zram) +{ + sysfs_remove_group(&disk_to_dev(zram->disk)->kobj, + &zram_disk_attr_group); + + del_gendisk(zram->disk); + put_disk(zram->disk); + + blk_cleanup_queue(zram->queue); +} + +static int __init zram_init(void) +{ + int ret, dev_id; + + if (num_devices > max_num_devices) { + pr_warn("Invalid value for num_devices: %u\n", + num_devices); + ret = -EINVAL; + goto out; + } + + zram_major = register_blkdev(0, "zram"); + if (zram_major <= 0) { + pr_warn("Unable to get major number\n"); + ret = -EBUSY; + goto out; + } + + /* Allocate the device array and initialize each one */ + zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); + if (!zram_devices) { + ret = -ENOMEM; + goto unregister; + } + + for (dev_id = 0; dev_id < num_devices; dev_id++) { + ret = create_device(&zram_devices[dev_id], dev_id); + if (ret) + goto free_devices; + } + + pr_info("Created %u device(s) ...\n", num_devices); + + return 0; + +free_devices: + while (dev_id) + destroy_device(&zram_devices[--dev_id]); + kfree(zram_devices); +unregister: + unregister_blkdev(zram_major, "zram"); +out: + return ret; +} + +static void __exit zram_exit(void) +{ + int i; + struct zram *zram; + + for (i = 0; i < num_devices; i++) { + zram = &zram_devices[i]; + + destroy_device(zram); + /* + * Shouldn't access zram->disk after destroy_device + * because destroy_device already released zram->disk. + */ + zram_reset_device(zram, false); + } + + unregister_blkdev(zram_major, "zram"); + + kfree(zram_devices); + pr_debug("Cleanup done!\n"); +} + +module_init(zram_init); +module_exit(zram_exit); + +module_param(num_devices, uint, 0); +MODULE_PARM_DESC(num_devices, "Number of zram devices"); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Nitin Gupta "); +MODULE_DESCRIPTION("Compressed RAM Block Device"); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h new file mode 100644 index 0000000..1c39fed --- /dev/null +++ b/drivers/block/zram/zram_drv.h @@ -0,0 +1,124 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + * Project home: http://compcache.googlecode.com + */ + +#ifndef _ZRAM_DRV_H_ +#define _ZRAM_DRV_H_ + +#include +#include +#include + +/* + * Some arbitrary value. This is just to catch + * invalid value for num_devices module parameter. + */ +static const unsigned max_num_devices = 32; + +/*-- Configurable parameters */ + +/* + * Pages that compress to size greater than this are stored + * uncompressed in memory. + */ +static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; + +/* + * NOTE: max_zpage_size must be less than or equal to: + * ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would + * always return failure. + */ + +/*-- End of configurable params */ + +#define SECTOR_SHIFT 9 +#define SECTOR_SIZE (1 << SECTOR_SHIFT) +#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) +#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) +#define ZRAM_LOGICAL_BLOCK_SHIFT 12 +#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT) +#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ + (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) + +/* Flags for zram pages (table[page_no].flags) */ +enum zram_pageflags { + /* Page consists entirely of zeros */ + ZRAM_ZERO, + + __NR_ZRAM_PAGEFLAGS, +}; + +/*-- Data structures */ + +/* Allocated for each disk page */ +struct table { + unsigned long handle; + u16 size; /* object size (excluding header) */ + u8 count; /* object ref count (not yet used) */ + u8 flags; +} __aligned(4); + +/* + * All 64bit fields should only be manipulated by 64bit atomic accessors. + * All modifications to 32bit counter should be protected by zram->lock. + */ +struct zram_stats { + atomic64_t compr_size; /* compressed size of pages stored */ + atomic64_t num_reads; /* failed + successful */ + atomic64_t num_writes; /* --do-- */ + atomic64_t failed_reads; /* should NEVER! happen */ + atomic64_t failed_writes; /* can happen when memory is too low */ + atomic64_t invalid_io; /* non-page-aligned I/O requests */ + atomic64_t notify_free; /* no. of swap slot free notifications */ + u32 pages_zero; /* no. of zero filled pages */ + u32 pages_stored; /* no. of pages currently stored */ + u32 good_compress; /* % of pages with compression ratio<=50% */ + u32 bad_compress; /* % of pages with compression ratio>=75% */ +}; + +struct zram_meta { + void *compress_workmem; + void *compress_buffer; + struct table *table; + struct zs_pool *mem_pool; +}; + +struct zram_slot_free { + unsigned long index; + struct zram_slot_free *next; +}; + +struct zram { + struct zram_meta *meta; + struct rw_semaphore lock; /* protect compression buffers, table, + * 32bit stat counters against concurrent + * notifications, reads and writes */ + + struct work_struct free_work; /* handle pending free request */ + struct zram_slot_free *slot_free_rq; /* list head of free request */ + + struct request_queue *queue; + struct gendisk *disk; + int init_done; + /* Prevent concurrent execution of device init, reset and R/W request */ + struct rw_semaphore init_lock; + /* + * This is the limit on amount of *uncompressed* worth of data + * we can store in a disk. + */ + u64 disksize; /* bytes */ + spinlock_t slot_free_lock; + + struct zram_stats stats; +}; +#endif diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 11a4b5b..d0f6718 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -104,8 +104,6 @@ source "drivers/staging/iio/Kconfig" source "drivers/staging/cs5535_gpio/Kconfig" -source "drivers/staging/zram/Kconfig" - source "drivers/staging/zcache/Kconfig" source "drivers/staging/wlags49_h2/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index c7d5f89..9400eb7 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -43,7 +43,6 @@ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio/ -obj-$(CONFIG_ZRAM) += zram/ obj-$(CONFIG_ZCACHE) += zcache/ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig deleted file mode 100644 index 983314c..0000000 --- a/drivers/staging/zram/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -config ZRAM - tristate "Compressed RAM block device support" - depends on BLOCK && SYSFS && ZSMALLOC - select LZO_COMPRESS - select LZO_DECOMPRESS - default n - help - Creates virtual block devices called /dev/zramX (X = 0, 1, ...). - Pages written to these disks are compressed and stored in memory - itself. These disks allow very fast I/O and compression provides - good amounts of memory savings. - - It has several use cases, for example: /tmp storage, use as swap - disks and maybe many more. - - See zram.txt for more information. - Project home: - -config ZRAM_DEBUG - bool "Compressed RAM block device debug support" - depends on ZRAM - default n - help - This option adds additional debugging code to the compressed - RAM block device driver. diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile deleted file mode 100644 index cb0f9ce..0000000 --- a/drivers/staging/zram/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -zram-y := zram_drv.o - -obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/staging/zram/zram.txt b/drivers/staging/zram/zram.txt deleted file mode 100644 index 765d790..0000000 --- a/drivers/staging/zram/zram.txt +++ /dev/null @@ -1,77 +0,0 @@ -zram: Compressed RAM based block devices ----------------------------------------- - -Project home: http://compcache.googlecode.com/ - -* Introduction - -The zram module creates RAM based block devices named /dev/zram -( = 0, 1, ...). Pages written to these disks are compressed and stored -in memory itself. These disks allow very fast I/O and compression provides -good amounts of memory savings. Some of the usecases include /tmp storage, -use as swap disks, various caches under /var and maybe many more :) - -Statistics for individual zram devices are exported through sysfs nodes at -/sys/block/zram/ - -* Usage - -Following shows a typical sequence of steps for using zram. - -1) Load Module: - modprobe zram num_devices=4 - This creates 4 devices: /dev/zram{0,1,2,3} - (num_devices parameter is optional. Default: 1) - -2) Set Disksize - Set disk size by writing the value to sysfs node 'disksize'. - The value can be either in bytes or you can use mem suffixes. - Examples: - # Initialize /dev/zram0 with 50MB disksize - echo $((50*1024*1024)) > /sys/block/zram0/disksize - - # Using mem suffixes - echo 256K > /sys/block/zram0/disksize - echo 512M > /sys/block/zram0/disksize - echo 1G > /sys/block/zram0/disksize - -3) Activate: - mkswap /dev/zram0 - swapon /dev/zram0 - - mkfs.ext4 /dev/zram1 - mount /dev/zram1 /tmp - -4) Stats: - Per-device statistics are exported as various nodes under - /sys/block/zram/ - disksize - num_reads - num_writes - invalid_io - notify_free - discard - zero_pages - orig_data_size - compr_data_size - mem_used_total - -5) Deactivate: - swapoff /dev/zram0 - umount /dev/zram1 - -6) Reset: - Write any positive value to 'reset' sysfs node - echo 1 > /sys/block/zram0/reset - echo 1 > /sys/block/zram1/reset - - This frees all the memory allocated for the given device and - resets the disksize to zero. You must set the disksize again - before reusing the device. - -Please report any problems at: - - Mailing list: linux-mm-cc at laptop dot org - - Issue tracker: http://code.google.com/p/compcache/issues/list - -Nitin Gupta -ngupta@vflare.org diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c deleted file mode 100644 index d22e5ad..0000000 --- a/drivers/staging/zram/zram_drv.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Compressed RAM block device - * - * Copyright (C) 2008, 2009, 2010 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the licence that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - * - * Project home: http://compcache.googlecode.com - */ - -#define KMSG_COMPONENT "zram" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - -#ifdef CONFIG_ZRAM_DEBUG -#define DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zram_drv.h" - -/* Globals */ -static int zram_major; -static struct zram *zram_devices; - -/* - * We don't need to see memory allocation errors more than once every 1 - * second to know that a problem is occurring. - */ -#define ALLOC_ERROR_LOG_RATE_MS 1000 - -/* Module params (documentation at end) */ -static unsigned int num_devices = 1; - -static inline struct zram *dev_to_zram(struct device *dev) -{ - return (struct zram *)dev_to_disk(dev)->private_data; -} - -static ssize_t disksize_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", zram->disksize); -} - -static ssize_t initstate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%u\n", zram->init_done); -} - -static ssize_t num_reads_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_reads)); -} - -static ssize_t num_writes_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_writes)); -} - -static ssize_t invalid_io_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.invalid_io)); -} - -static ssize_t notify_free_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.notify_free)); -} - -static ssize_t zero_pages_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%u\n", zram->stats.pages_zero); -} - -static ssize_t orig_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)(zram->stats.pages_stored) << PAGE_SHIFT); -} - -static ssize_t compr_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.compr_size)); -} - -static ssize_t mem_used_total_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u64 val = 0; - struct zram *zram = dev_to_zram(dev); - struct zram_meta *meta = zram->meta; - - down_read(&zram->init_lock); - if (zram->init_done) - val = zs_get_total_size_bytes(meta->mem_pool); - up_read(&zram->init_lock); - - return sprintf(buf, "%llu\n", val); -} - -static int zram_test_flag(struct zram_meta *meta, u32 index, - enum zram_pageflags flag) -{ - return meta->table[index].flags & BIT(flag); -} - -static void zram_set_flag(struct zram_meta *meta, u32 index, - enum zram_pageflags flag) -{ - meta->table[index].flags |= BIT(flag); -} - -static void zram_clear_flag(struct zram_meta *meta, u32 index, - enum zram_pageflags flag) -{ - meta->table[index].flags &= ~BIT(flag); -} - -static inline int is_partial_io(struct bio_vec *bvec) -{ - return bvec->bv_len != PAGE_SIZE; -} - -/* - * Check if request is within bounds and aligned on zram logical blocks. - */ -static inline int valid_io_request(struct zram *zram, struct bio *bio) -{ - u64 start, end, bound; - - /* unaligned request */ - if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) - return 0; - if (unlikely(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) - return 0; - - start = bio->bi_sector; - end = start + (bio->bi_size >> SECTOR_SHIFT); - bound = zram->disksize >> SECTOR_SHIFT; - /* out of range range */ - if (unlikely(start >= bound || end > bound || start > end)) - return 0; - - /* I/O request is valid */ - return 1; -} - -static void zram_meta_free(struct zram_meta *meta) -{ - zs_destroy_pool(meta->mem_pool); - kfree(meta->compress_workmem); - free_pages((unsigned long)meta->compress_buffer, 1); - vfree(meta->table); - kfree(meta); -} - -static struct zram_meta *zram_meta_alloc(u64 disksize) -{ - size_t num_pages; - struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); - if (!meta) - goto out; - - meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); - if (!meta->compress_workmem) - goto free_meta; - - meta->compress_buffer = - (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!meta->compress_buffer) { - pr_err("Error allocating compressor buffer space\n"); - goto free_workmem; - } - - num_pages = disksize >> PAGE_SHIFT; - meta->table = vzalloc(num_pages * sizeof(*meta->table)); - if (!meta->table) { - pr_err("Error allocating zram address table\n"); - goto free_buffer; - } - - meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM | - __GFP_NOWARN); - if (!meta->mem_pool) { - pr_err("Error creating memory pool\n"); - goto free_table; - } - - return meta; - -free_table: - vfree(meta->table); -free_buffer: - free_pages((unsigned long)meta->compress_buffer, 1); -free_workmem: - kfree(meta->compress_workmem); -free_meta: - kfree(meta); - meta = NULL; -out: - return meta; -} - -static void update_position(u32 *index, int *offset, struct bio_vec *bvec) -{ - if (*offset + bvec->bv_len >= PAGE_SIZE) - (*index)++; - *offset = (*offset + bvec->bv_len) % PAGE_SIZE; -} - -static int page_zero_filled(void *ptr) -{ - unsigned int pos; - unsigned long *page; - - page = (unsigned long *)ptr; - - for (pos = 0; pos != PAGE_SIZE / sizeof(*page); pos++) { - if (page[pos]) - return 0; - } - - return 1; -} - -static void handle_zero_page(struct bio_vec *bvec) -{ - struct page *page = bvec->bv_page; - void *user_mem; - - user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) - memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); - else - clear_page(user_mem); - kunmap_atomic(user_mem); - - flush_dcache_page(page); -} - -static void zram_free_page(struct zram *zram, size_t index) -{ - struct zram_meta *meta = zram->meta; - unsigned long handle = meta->table[index].handle; - u16 size = meta->table[index].size; - - if (unlikely(!handle)) { - /* - * No memory is allocated for zero filled pages. - * Simply clear zero page flag. - */ - if (zram_test_flag(meta, index, ZRAM_ZERO)) { - zram_clear_flag(meta, index, ZRAM_ZERO); - zram->stats.pages_zero--; - } - return; - } - - if (unlikely(size > max_zpage_size)) - zram->stats.bad_compress--; - - zs_free(meta->mem_pool, handle); - - if (size <= PAGE_SIZE / 2) - zram->stats.good_compress--; - - atomic64_sub(meta->table[index].size, &zram->stats.compr_size); - zram->stats.pages_stored--; - - meta->table[index].handle = 0; - meta->table[index].size = 0; -} - -static int zram_decompress_page(struct zram *zram, char *mem, u32 index) -{ - int ret = LZO_E_OK; - size_t clen = PAGE_SIZE; - unsigned char *cmem; - struct zram_meta *meta = zram->meta; - unsigned long handle = meta->table[index].handle; - - if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { - clear_page(mem); - return 0; - } - - cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); - if (meta->table[index].size == PAGE_SIZE) - copy_page(mem, cmem); - else - ret = lzo1x_decompress_safe(cmem, meta->table[index].size, - mem, &clen); - zs_unmap_object(meta->mem_pool, handle); - - /* Should NEVER happen. Return bio error if it does. */ - if (unlikely(ret != LZO_E_OK)) { - pr_err("Decompression failed! err=%d, page=%u\n", ret, index); - atomic64_inc(&zram->stats.failed_reads); - return ret; - } - - return 0; -} - -static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, - u32 index, int offset, struct bio *bio) -{ - int ret; - struct page *page; - unsigned char *user_mem, *uncmem = NULL; - struct zram_meta *meta = zram->meta; - page = bvec->bv_page; - - if (unlikely(!meta->table[index].handle) || - zram_test_flag(meta, index, ZRAM_ZERO)) { - handle_zero_page(bvec); - return 0; - } - - if (is_partial_io(bvec)) - /* Use a temporary buffer to decompress the page */ - uncmem = kmalloc(PAGE_SIZE, GFP_NOIO); - - user_mem = kmap_atomic(page); - if (!is_partial_io(bvec)) - uncmem = user_mem; - - if (!uncmem) { - pr_info("Unable to allocate temp memory\n"); - ret = -ENOMEM; - goto out_cleanup; - } - - ret = zram_decompress_page(zram, uncmem, index); - /* Should NEVER happen. Return bio error if it does. */ - if (unlikely(ret != LZO_E_OK)) - goto out_cleanup; - - if (is_partial_io(bvec)) - memcpy(user_mem + bvec->bv_offset, uncmem + offset, - bvec->bv_len); - - flush_dcache_page(page); - ret = 0; -out_cleanup: - kunmap_atomic(user_mem); - if (is_partial_io(bvec)) - kfree(uncmem); - return ret; -} - -static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, - int offset) -{ - int ret = 0; - size_t clen; - unsigned long handle; - struct page *page; - unsigned char *user_mem, *cmem, *src, *uncmem = NULL; - struct zram_meta *meta = zram->meta; - static unsigned long zram_rs_time; - - page = bvec->bv_page; - src = meta->compress_buffer; - - if (is_partial_io(bvec)) { - /* - * This is a partial IO. We need to read the full page - * before to write the changes. - */ - uncmem = kmalloc(PAGE_SIZE, GFP_NOIO); - if (!uncmem) { - ret = -ENOMEM; - goto out; - } - ret = zram_decompress_page(zram, uncmem, index); - if (ret) - goto out; - } - - user_mem = kmap_atomic(page); - - if (is_partial_io(bvec)) { - memcpy(uncmem + offset, user_mem + bvec->bv_offset, - bvec->bv_len); - kunmap_atomic(user_mem); - user_mem = NULL; - } else { - uncmem = user_mem; - } - - if (page_zero_filled(uncmem)) { - kunmap_atomic(user_mem); - /* Free memory associated with this sector now. */ - zram_free_page(zram, index); - - zram->stats.pages_zero++; - zram_set_flag(meta, index, ZRAM_ZERO); - ret = 0; - goto out; - } - - /* - * zram_slot_free_notify could miss free so that let's - * double check. - */ - if (unlikely(meta->table[index].handle || - zram_test_flag(meta, index, ZRAM_ZERO))) - zram_free_page(zram, index); - - ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, - meta->compress_workmem); - - if (!is_partial_io(bvec)) { - kunmap_atomic(user_mem); - user_mem = NULL; - uncmem = NULL; - } - - if (unlikely(ret != LZO_E_OK)) { - pr_err("Compression failed! err=%d\n", ret); - goto out; - } - - if (unlikely(clen > max_zpage_size)) { - zram->stats.bad_compress++; - clen = PAGE_SIZE; - src = NULL; - if (is_partial_io(bvec)) - src = uncmem; - } - - handle = zs_malloc(meta->mem_pool, clen); - if (!handle) { - if (printk_timed_ratelimit(&zram_rs_time, - ALLOC_ERROR_LOG_RATE_MS)) - pr_info("Error allocating memory for compressed page: %u, size=%zu\n", - index, clen); - ret = -ENOMEM; - goto out; - } - cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO); - - if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) { - src = kmap_atomic(page); - copy_page(cmem, src); - kunmap_atomic(src); - } else { - memcpy(cmem, src, clen); - } - - zs_unmap_object(meta->mem_pool, handle); - - /* - * Free memory associated with this sector - * before overwriting unused sectors. - */ - zram_free_page(zram, index); - - meta->table[index].handle = handle; - meta->table[index].size = clen; - - /* Update stats */ - atomic64_add(clen, &zram->stats.compr_size); - zram->stats.pages_stored++; - if (clen <= PAGE_SIZE / 2) - zram->stats.good_compress++; - -out: - if (is_partial_io(bvec)) - kfree(uncmem); - - if (ret) - atomic64_inc(&zram->stats.failed_writes); - return ret; -} - -static void handle_pending_slot_free(struct zram *zram) -{ - struct zram_slot_free *free_rq; - - spin_lock(&zram->slot_free_lock); - while (zram->slot_free_rq) { - free_rq = zram->slot_free_rq; - zram->slot_free_rq = free_rq->next; - zram_free_page(zram, free_rq->index); - kfree(free_rq); - } - spin_unlock(&zram->slot_free_lock); -} - -static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, - int offset, struct bio *bio, int rw) -{ - int ret; - - if (rw == READ) { - down_read(&zram->lock); - handle_pending_slot_free(zram); - ret = zram_bvec_read(zram, bvec, index, offset, bio); - up_read(&zram->lock); - } else { - down_write(&zram->lock); - handle_pending_slot_free(zram); - ret = zram_bvec_write(zram, bvec, index, offset); - up_write(&zram->lock); - } - - return ret; -} - -static void zram_reset_device(struct zram *zram, bool reset_capacity) -{ - size_t index; - struct zram_meta *meta; - - flush_work(&zram->free_work); - - down_write(&zram->init_lock); - if (!zram->init_done) { - up_write(&zram->init_lock); - return; - } - - meta = zram->meta; - zram->init_done = 0; - - /* Free all pages that are still in this zram device */ - for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { - unsigned long handle = meta->table[index].handle; - if (!handle) - continue; - - zs_free(meta->mem_pool, handle); - } - - zram_meta_free(zram->meta); - zram->meta = NULL; - /* Reset stats */ - memset(&zram->stats, 0, sizeof(zram->stats)); - - zram->disksize = 0; - if (reset_capacity) - set_capacity(zram->disk, 0); - up_write(&zram->init_lock); -} - -static void zram_init_device(struct zram *zram, struct zram_meta *meta) -{ - if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { - pr_info( - "There is little point creating a zram of greater than " - "twice the size of memory since we expect a 2:1 compression " - "ratio. Note that zram uses about 0.1%% of the size of " - "the disk when not in use so a huge zram is " - "wasteful.\n" - "\tMemory Size: %lu kB\n" - "\tSize you selected: %llu kB\n" - "Continuing anyway ...\n", - (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 - ); - } - - /* zram devices sort of resembles non-rotational disks */ - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); - - zram->meta = meta; - zram->init_done = 1; - - pr_debug("Initialization done!\n"); -} - -static ssize_t disksize_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - u64 disksize; - struct zram_meta *meta; - struct zram *zram = dev_to_zram(dev); - - disksize = memparse(buf, NULL); - if (!disksize) - return -EINVAL; - - disksize = PAGE_ALIGN(disksize); - meta = zram_meta_alloc(disksize); - down_write(&zram->init_lock); - if (zram->init_done) { - up_write(&zram->init_lock); - zram_meta_free(meta); - pr_info("Cannot change disksize for initialized device\n"); - return -EBUSY; - } - - zram->disksize = disksize; - set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); - zram_init_device(zram, meta); - up_write(&zram->init_lock); - - return len; -} - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - int ret; - unsigned short do_reset; - struct zram *zram; - struct block_device *bdev; - - zram = dev_to_zram(dev); - bdev = bdget_disk(zram->disk, 0); - - if (!bdev) - return -ENOMEM; - - /* Do not reset an active device! */ - if (bdev->bd_holders) { - ret = -EBUSY; - goto out; - } - - ret = kstrtou16(buf, 10, &do_reset); - if (ret) - goto out; - - if (!do_reset) { - ret = -EINVAL; - goto out; - } - - /* Make sure all pending I/O is finished */ - fsync_bdev(bdev); - bdput(bdev); - - zram_reset_device(zram, true); - return len; - -out: - bdput(bdev); - return ret; -} - -static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) -{ - int i, offset; - u32 index; - struct bio_vec *bvec; - - switch (rw) { - case READ: - atomic64_inc(&zram->stats.num_reads); - break; - case WRITE: - atomic64_inc(&zram->stats.num_writes); - break; - } - - index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; - offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; - - bio_for_each_segment(bvec, bio, i) { - int max_transfer_size = PAGE_SIZE - offset; - - if (bvec->bv_len > max_transfer_size) { - /* - * zram_bvec_rw() can only make operation on a single - * zram page. Split the bio vector. - */ - struct bio_vec bv; - - bv.bv_page = bvec->bv_page; - bv.bv_len = max_transfer_size; - bv.bv_offset = bvec->bv_offset; - - if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0) - goto out; - - bv.bv_len = bvec->bv_len - max_transfer_size; - bv.bv_offset += max_transfer_size; - if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0) - goto out; - } else - if (zram_bvec_rw(zram, bvec, index, offset, bio, rw) - < 0) - goto out; - - update_position(&index, &offset, bvec); - } - - set_bit(BIO_UPTODATE, &bio->bi_flags); - bio_endio(bio, 0); - return; - -out: - bio_io_error(bio); -} - -/* - * Handler function for all zram I/O requests. - */ -static int zram_make_request(struct request_queue *queue, struct bio *bio) -{ - struct zram *zram = queue->queuedata; - - down_read(&zram->init_lock); - if (unlikely(!zram->init_done)) - goto error; - - if (!valid_io_request(zram, bio)) { - atomic64_inc(&zram->stats.invalid_io); - goto error; - } - - __zram_make_request(zram, bio, bio_data_dir(bio)); - up_read(&zram->init_lock); - - return 0; - -error: - up_read(&zram->init_lock); - bio_io_error(bio); - - return 0; -} - -static void zram_slot_free(struct work_struct *work) -{ - struct zram *zram; - - zram = container_of(work, struct zram, free_work); - down_write(&zram->lock); - handle_pending_slot_free(zram); - up_write(&zram->lock); -} - -static void add_slot_free(struct zram *zram, struct zram_slot_free *free_rq) -{ - spin_lock(&zram->slot_free_lock); - free_rq->next = zram->slot_free_rq; - zram->slot_free_rq = free_rq; - spin_unlock(&zram->slot_free_lock); -} - -static void zram_slot_free_notify(struct block_device *bdev, - unsigned long index) -{ - struct zram *zram; - struct zram_slot_free *free_rq; - - zram = bdev->bd_disk->private_data; - atomic64_inc(&zram->stats.notify_free); - - free_rq = kmalloc(sizeof(struct zram_slot_free), GFP_ATOMIC); - if (!free_rq) - return; - - free_rq->index = index; - add_slot_free(zram, free_rq); - schedule_work(&zram->free_work); -} - -static const struct block_device_operations zram_devops = { - .swap_slot_free_notify = zram_slot_free_notify, - .owner = THIS_MODULE -}; - -static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR, - disksize_show, disksize_store); -static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL); -static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); -static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL); -static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL); -static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL); -static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL); -static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL); -static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); -static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL); -static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); - -static struct attribute *zram_disk_attrs[] = { - &dev_attr_disksize.attr, - &dev_attr_initstate.attr, - &dev_attr_reset.attr, - &dev_attr_num_reads.attr, - &dev_attr_num_writes.attr, - &dev_attr_invalid_io.attr, - &dev_attr_notify_free.attr, - &dev_attr_zero_pages.attr, - &dev_attr_orig_data_size.attr, - &dev_attr_compr_data_size.attr, - &dev_attr_mem_used_total.attr, - NULL, -}; - -static struct attribute_group zram_disk_attr_group = { - .attrs = zram_disk_attrs, -}; - -static int create_device(struct zram *zram, int device_id) -{ - int ret = -ENOMEM; - - init_rwsem(&zram->lock); - init_rwsem(&zram->init_lock); - - INIT_WORK(&zram->free_work, zram_slot_free); - spin_lock_init(&zram->slot_free_lock); - zram->slot_free_rq = NULL; - - zram->queue = blk_alloc_queue(GFP_KERNEL); - if (!zram->queue) { - pr_err("Error allocating disk queue for device %d\n", - device_id); - goto out; - } - - blk_queue_make_request(zram->queue, zram_make_request); - zram->queue->queuedata = zram; - - /* gendisk structure */ - zram->disk = alloc_disk(1); - if (!zram->disk) { - pr_warn("Error allocating disk structure for device %d\n", - device_id); - goto out_free_queue; - } - - zram->disk->major = zram_major; - zram->disk->first_minor = device_id; - zram->disk->fops = &zram_devops; - zram->disk->queue = zram->queue; - zram->disk->private_data = zram; - snprintf(zram->disk->disk_name, 16, "zram%d", device_id); - - /* Actual capacity set using syfs (/sys/block/zram/disksize */ - set_capacity(zram->disk, 0); - - /* - * To ensure that we always get PAGE_SIZE aligned - * and n*PAGE_SIZED sized I/O requests. - */ - blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE); - blk_queue_logical_block_size(zram->disk->queue, - ZRAM_LOGICAL_BLOCK_SIZE); - blk_queue_io_min(zram->disk->queue, PAGE_SIZE); - blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); - - add_disk(zram->disk); - - ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj, - &zram_disk_attr_group); - if (ret < 0) { - pr_warn("Error creating sysfs group"); - goto out_free_disk; - } - - zram->init_done = 0; - return 0; - -out_free_disk: - del_gendisk(zram->disk); - put_disk(zram->disk); -out_free_queue: - blk_cleanup_queue(zram->queue); -out: - return ret; -} - -static void destroy_device(struct zram *zram) -{ - sysfs_remove_group(&disk_to_dev(zram->disk)->kobj, - &zram_disk_attr_group); - - del_gendisk(zram->disk); - put_disk(zram->disk); - - blk_cleanup_queue(zram->queue); -} - -static int __init zram_init(void) -{ - int ret, dev_id; - - if (num_devices > max_num_devices) { - pr_warn("Invalid value for num_devices: %u\n", - num_devices); - ret = -EINVAL; - goto out; - } - - zram_major = register_blkdev(0, "zram"); - if (zram_major <= 0) { - pr_warn("Unable to get major number\n"); - ret = -EBUSY; - goto out; - } - - /* Allocate the device array and initialize each one */ - zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL); - if (!zram_devices) { - ret = -ENOMEM; - goto unregister; - } - - for (dev_id = 0; dev_id < num_devices; dev_id++) { - ret = create_device(&zram_devices[dev_id], dev_id); - if (ret) - goto free_devices; - } - - pr_info("Created %u device(s) ...\n", num_devices); - - return 0; - -free_devices: - while (dev_id) - destroy_device(&zram_devices[--dev_id]); - kfree(zram_devices); -unregister: - unregister_blkdev(zram_major, "zram"); -out: - return ret; -} - -static void __exit zram_exit(void) -{ - int i; - struct zram *zram; - - for (i = 0; i < num_devices; i++) { - zram = &zram_devices[i]; - - destroy_device(zram); - /* - * Shouldn't access zram->disk after destroy_device - * because destroy_device already released zram->disk. - */ - zram_reset_device(zram, false); - } - - unregister_blkdev(zram_major, "zram"); - - kfree(zram_devices); - pr_debug("Cleanup done!\n"); -} - -module_init(zram_init); -module_exit(zram_exit); - -module_param(num_devices, uint, 0); -MODULE_PARM_DESC(num_devices, "Number of zram devices"); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_AUTHOR("Nitin Gupta "); -MODULE_DESCRIPTION("Compressed RAM Block Device"); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h deleted file mode 100644 index 1c39fed..0000000 --- a/drivers/staging/zram/zram_drv.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Compressed RAM block device - * - * Copyright (C) 2008, 2009, 2010 Nitin Gupta - * - * This code is released using a dual license strategy: BSD/GPL - * You can choose the licence that better fits your requirements. - * - * Released under the terms of 3-clause BSD License - * Released under the terms of GNU General Public License Version 2.0 - * - * Project home: http://compcache.googlecode.com - */ - -#ifndef _ZRAM_DRV_H_ -#define _ZRAM_DRV_H_ - -#include -#include -#include - -/* - * Some arbitrary value. This is just to catch - * invalid value for num_devices module parameter. - */ -static const unsigned max_num_devices = 32; - -/*-- Configurable parameters */ - -/* - * Pages that compress to size greater than this are stored - * uncompressed in memory. - */ -static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; - -/* - * NOTE: max_zpage_size must be less than or equal to: - * ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would - * always return failure. - */ - -/*-- End of configurable params */ - -#define SECTOR_SHIFT 9 -#define SECTOR_SIZE (1 << SECTOR_SHIFT) -#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) -#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) -#define ZRAM_LOGICAL_BLOCK_SHIFT 12 -#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT) -#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ - (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) - -/* Flags for zram pages (table[page_no].flags) */ -enum zram_pageflags { - /* Page consists entirely of zeros */ - ZRAM_ZERO, - - __NR_ZRAM_PAGEFLAGS, -}; - -/*-- Data structures */ - -/* Allocated for each disk page */ -struct table { - unsigned long handle; - u16 size; /* object size (excluding header) */ - u8 count; /* object ref count (not yet used) */ - u8 flags; -} __aligned(4); - -/* - * All 64bit fields should only be manipulated by 64bit atomic accessors. - * All modifications to 32bit counter should be protected by zram->lock. - */ -struct zram_stats { - atomic64_t compr_size; /* compressed size of pages stored */ - atomic64_t num_reads; /* failed + successful */ - atomic64_t num_writes; /* --do-- */ - atomic64_t failed_reads; /* should NEVER! happen */ - atomic64_t failed_writes; /* can happen when memory is too low */ - atomic64_t invalid_io; /* non-page-aligned I/O requests */ - atomic64_t notify_free; /* no. of swap slot free notifications */ - u32 pages_zero; /* no. of zero filled pages */ - u32 pages_stored; /* no. of pages currently stored */ - u32 good_compress; /* % of pages with compression ratio<=50% */ - u32 bad_compress; /* % of pages with compression ratio>=75% */ -}; - -struct zram_meta { - void *compress_workmem; - void *compress_buffer; - struct table *table; - struct zs_pool *mem_pool; -}; - -struct zram_slot_free { - unsigned long index; - struct zram_slot_free *next; -}; - -struct zram { - struct zram_meta *meta; - struct rw_semaphore lock; /* protect compression buffers, table, - * 32bit stat counters against concurrent - * notifications, reads and writes */ - - struct work_struct free_work; /* handle pending free request */ - struct zram_slot_free *slot_free_rq; /* list head of free request */ - - struct request_queue *queue; - struct gendisk *disk; - int init_done; - /* Prevent concurrent execution of device init, reset and R/W request */ - struct rw_semaphore init_lock; - /* - * This is the limit on amount of *uncompressed* worth of data - * we can store in a disk. - */ - u64 disksize; /* bytes */ - spinlock_t slot_free_lock; - - struct zram_stats stats; -}; -#endif -- cgit v1.1 From 7bd9d3e31b18fa94c80849c567fd64359d90754b Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:45:55 -0800 Subject: zram: add copyright Add my copyright to the zram source code which I maintain. Signed-off-by: Minchan Kim Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 1 + drivers/block/zram/zram_drv.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index d22e5ad..05c8258 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2,6 +2,7 @@ * Compressed RAM block device * * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the licence that better fits your requirements. diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 1c39fed..e9b2476 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -2,6 +2,7 @@ * Compressed RAM block device * * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim * * This code is released using a dual license strategy: BSD/GPL * You can choose the licence that better fits your requirements. -- cgit v1.1 From 5343c207e9f3941bc8fa156a884553832f49fb43 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:45:58 -0800 Subject: zram: fix race between reset and flushing pending work Dan and Sergey reported that there is a racy between reset and flushing of pending work so that it could make oops by freeing zram->meta in reset while zram_slot_free can access zram->meta if new request is adding during the race window. This patch moves flush after taking init_lock so it prevents new request so that it closes the race. Signed-off-by: Minchan Kim Reported-by: Dan Carpenter Cc: Nitin Gupta Cc: Jerome Marchand Tested-by: Sergey Senozhatsky Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 05c8258..7881508 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -564,14 +564,14 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) size_t index; struct zram_meta *meta; - flush_work(&zram->free_work); - down_write(&zram->init_lock); if (!zram->init_done) { up_write(&zram->init_lock); return; } + flush_work(&zram->free_work); + meta = zram->meta; zram->init_done = 0; -- cgit v1.1 From 95fbfb88ab45d57f6b9e48f92457ddd661787f30 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:46:00 -0800 Subject: zram: delay pending free request in read path Sergey reported we don't need to handle pending free request every I/O so that this patch removes it in read path while we remain it in write path. Let's consider below example. Swap subsystem ask to zram "A" block free by swap_slot_free_notify but zram had been pended it without real freeing. Swap subsystem allocates "A" block for new data but request pended for a long time just handled and zram blindly free new data on the "A" block. :( That's why we couldn't remove handle pending free request right before zram-write. Signed-off-by: Minchan Kim Reported-by: Sergey Senozhatsky Tested-by: Sergey Senozhatsky Cc: Nitin Gupta Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 7881508..87fc7f3 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -546,7 +546,6 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, if (rw == READ) { down_read(&zram->lock); - handle_pending_slot_free(zram); ret = zram_bvec_read(zram, bvec, index, offset, bio); up_read(&zram->lock); } else { -- cgit v1.1 From c8f984bff7c876be85c8b4170ca13fa2dc99b516 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:46:01 -0800 Subject: zram: remove unnecessary free Commit a0c516cbfc74 ("zram: don't grab mutex in zram_slot_free_noity") introduced pending zram slot free in zram's write path in case of missing slot free by memory allocation failure in zram_slot_free_notify but it is not necessary because we have already freed the slot right before overwriting. Signed-off-by: Minchan Kim Cc: Nitin Gupta Cc: Jerome Marchand Tested-by: Sergey Senozhatsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 87fc7f3..f39c2f9 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -450,14 +450,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } - /* - * zram_slot_free_notify could miss free so that let's - * double check. - */ - if (unlikely(meta->table[index].handle || - zram_test_flag(meta, index, ZRAM_ZERO))) - zram_free_page(zram, index); - ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, meta->compress_workmem); -- cgit v1.1 From 4d998ac9d52c6f2b3c1a18ec9dbcde198a73643c Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:46:02 -0800 Subject: zram: use atomic operation for stat Some of fields in zram->stats are protected by zram->lock which is rather coarse-grained so let's use atomic operation without explict locking. This patch is ready for removing dependency of zram->lock in read path which is very coarse-grained rw_semaphore. Of course, this patch adds new atomic operation so it might make slow but my 12CPU test couldn't spot any regression. All gain/lose is marginal within stddev. iozone -t -T -l 12 -u 12 -r 16K -s 60M -I +Z -V 0 ==Initial write ==Initial write records: 50 records: 50 avg: 412875.17 avg: 415638.23 std: 38543.12 (9.34%) std: 36601.11 (8.81%) max: 521262.03 max: 502976.72 min: 343263.13 min: 351389.12 ==Rewrite ==Rewrite records: 50 records: 50 avg: 416640.34 avg: 397914.33 std: 60798.92 (14.59%) std: 46150.42 (11.60%) max: 543057.07 max: 522669.17 min: 304071.67 min: 316588.77 ==Read ==Read records: 50 records: 50 avg: 4147338.63 avg: 4070736.51 std: 179333.25 (4.32%) std: 223499.89 (5.49%) max: 4459295.28 max: 4539514.44 min: 3753057.53 min: 3444686.31 ==Re-read ==Re-read records: 50 records: 50 avg: 4096706.71 avg: 4117218.57 std: 229735.04 (5.61%) std: 171676.25 (4.17%) max: 4430012.09 max: 4459263.94 min: 2987217.80 min: 3666904.28 ==Reverse Read ==Reverse Read records: 50 records: 50 avg: 4062763.83 avg: 4078508.32 std: 186208.46 (4.58%) std: 172684.34 (4.23%) max: 4401358.78 max: 4424757.22 min: 3381625.00 min: 3679359.94 ==Stride read ==Stride read records: 50 records: 50 avg: 4094933.49 avg: 4082170.22 std: 185710.52 (4.54%) std: 196346.68 (4.81%) max: 4478241.25 max: 4460060.97 min: 3732593.23 min: 3584125.78 ==Random read ==Random read records: 50 records: 50 avg: 4031070.04 avg: 4074847.49 std: 192065.51 (4.76%) std: 206911.33 (5.08%) max: 4356931.16 max: 4399442.56 min: 3481619.62 min: 3548372.44 ==Mixed workload ==Mixed workload records: 50 records: 50 avg: 149925.73 avg: 149675.54 std: 7701.26 (5.14%) std: 6902.09 (4.61%) max: 191301.56 max: 175162.05 min: 133566.28 min: 137762.87 ==Random write ==Random write records: 50 records: 50 avg: 404050.11 avg: 393021.47 std: 58887.57 (14.57%) std: 42813.70 (10.89%) max: 601798.09 max: 524533.43 min: 325176.99 min: 313255.34 ==Pwrite ==Pwrite records: 50 records: 50 avg: 411217.70 avg: 411237.96 std: 43114.99 (10.48%) std: 33136.29 (8.06%) max: 530766.79 max: 471899.76 min: 320786.84 min: 317906.94 ==Pread ==Pread records: 50 records: 50 avg: 4154908.65 avg: 4087121.92 std: 151272.08 (3.64%) std: 219505.04 (5.37%) max: 4459478.12 max: 4435857.38 min: 3730512.41 min: 3101101.67 Signed-off-by: Minchan Kim Cc: Nitin Gupta Tested-by: Sergey Senozhatsky Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 20 ++++++++++---------- drivers/block/zram/zram_drv.h | 16 ++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index f39c2f9..1200ef3 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -112,7 +112,7 @@ static ssize_t zero_pages_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%u\n", zram->stats.pages_zero); + return sprintf(buf, "%u\n", atomic_read(&zram->stats.pages_zero)); } static ssize_t orig_data_size_show(struct device *dev, @@ -121,7 +121,7 @@ static ssize_t orig_data_size_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - (u64)(zram->stats.pages_stored) << PAGE_SHIFT); + (u64)(atomic_read(&zram->stats.pages_stored)) << PAGE_SHIFT); } static ssize_t compr_data_size_show(struct device *dev, @@ -301,21 +301,21 @@ static void zram_free_page(struct zram *zram, size_t index) */ if (zram_test_flag(meta, index, ZRAM_ZERO)) { zram_clear_flag(meta, index, ZRAM_ZERO); - zram->stats.pages_zero--; + atomic_dec(&zram->stats.pages_zero); } return; } if (unlikely(size > max_zpage_size)) - zram->stats.bad_compress--; + atomic_dec(&zram->stats.bad_compress); zs_free(meta->mem_pool, handle); if (size <= PAGE_SIZE / 2) - zram->stats.good_compress--; + atomic_dec(&zram->stats.good_compress); atomic64_sub(meta->table[index].size, &zram->stats.compr_size); - zram->stats.pages_stored--; + atomic_dec(&zram->stats.pages_stored); meta->table[index].handle = 0; meta->table[index].size = 0; @@ -444,7 +444,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, /* Free memory associated with this sector now. */ zram_free_page(zram, index); - zram->stats.pages_zero++; + atomic_inc(&zram->stats.pages_zero); zram_set_flag(meta, index, ZRAM_ZERO); ret = 0; goto out; @@ -465,7 +465,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, } if (unlikely(clen > max_zpage_size)) { - zram->stats.bad_compress++; + atomic_inc(&zram->stats.bad_compress); clen = PAGE_SIZE; src = NULL; if (is_partial_io(bvec)) @@ -504,9 +504,9 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, /* Update stats */ atomic64_add(clen, &zram->stats.compr_size); - zram->stats.pages_stored++; + atomic_inc(&zram->stats.pages_stored); if (clen <= PAGE_SIZE / 2) - zram->stats.good_compress++; + atomic_inc(&zram->stats.good_compress); out: if (is_partial_io(bvec)) diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index e9b2476..1526c82 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -69,10 +69,6 @@ struct table { u8 flags; } __aligned(4); -/* - * All 64bit fields should only be manipulated by 64bit atomic accessors. - * All modifications to 32bit counter should be protected by zram->lock. - */ struct zram_stats { atomic64_t compr_size; /* compressed size of pages stored */ atomic64_t num_reads; /* failed + successful */ @@ -81,10 +77,10 @@ struct zram_stats { atomic64_t failed_writes; /* can happen when memory is too low */ atomic64_t invalid_io; /* non-page-aligned I/O requests */ atomic64_t notify_free; /* no. of swap slot free notifications */ - u32 pages_zero; /* no. of zero filled pages */ - u32 pages_stored; /* no. of pages currently stored */ - u32 good_compress; /* % of pages with compression ratio<=50% */ - u32 bad_compress; /* % of pages with compression ratio>=75% */ + atomic_t pages_zero; /* no. of zero filled pages */ + atomic_t pages_stored; /* no. of pages currently stored */ + atomic_t good_compress; /* % of pages with compression ratio<=50% */ + atomic_t bad_compress; /* % of pages with compression ratio>=75% */ }; struct zram_meta { @@ -102,8 +98,8 @@ struct zram_slot_free { struct zram { struct zram_meta *meta; struct rw_semaphore lock; /* protect compression buffers, table, - * 32bit stat counters against concurrent - * notifications, reads and writes */ + * reads and writes + */ struct work_struct free_work; /* handle pending free request */ struct zram_slot_free *slot_free_rq; /* list head of free request */ -- cgit v1.1 From 03ea8b59f91726023ad80a7f655814d47978a552 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:46:03 -0800 Subject: zram: introduce zram->tb_lock Currently, the zram table is protected by zram->lock but it's rather coarse-grained lock and it makes hard for scalibility. Let's use own rwlock instead of depending on zram->lock. This patch adds new locking so obviously, it would make slow but this patch is just prepartion for removing coarse-grained rw_semaphore(ie, zram->lock) which is hurdle about zram scalability. Final patch in this patchset series will remove the lock from read-path and change rw_semaphore with mutex in write path. With bonus, we could drop pending slot free mess in next patch. Signed-off-by: Minchan Kim Cc: Nitin Gupta Tested-by: Sergey Senozhatsky Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 26 +++++++++++++++++++++----- drivers/block/zram/zram_drv.h | 3 ++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 1200ef3..62b5727 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -148,6 +148,7 @@ static ssize_t mem_used_total_show(struct device *dev, return sprintf(buf, "%llu\n", val); } +/* flag operations needs meta->tb_lock */ static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { @@ -236,6 +237,7 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) goto free_table; } + rwlock_init(&meta->tb_lock); return meta; free_table: @@ -288,6 +290,7 @@ static void handle_zero_page(struct bio_vec *bvec) flush_dcache_page(page); } +/* NOTE: caller should hold meta->tb_lock with write-side */ static void zram_free_page(struct zram *zram, size_t index) { struct zram_meta *meta = zram->meta; @@ -327,20 +330,26 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) size_t clen = PAGE_SIZE; unsigned char *cmem; struct zram_meta *meta = zram->meta; - unsigned long handle = meta->table[index].handle; + unsigned long handle; + u16 size; + + read_lock(&meta->tb_lock); + handle = meta->table[index].handle; + size = meta->table[index].size; if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { + read_unlock(&meta->tb_lock); clear_page(mem); return 0; } cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); - if (meta->table[index].size == PAGE_SIZE) + if (size == PAGE_SIZE) copy_page(mem, cmem); else - ret = lzo1x_decompress_safe(cmem, meta->table[index].size, - mem, &clen); + ret = lzo1x_decompress_safe(cmem, size, mem, &clen); zs_unmap_object(meta->mem_pool, handle); + read_unlock(&meta->tb_lock); /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret != LZO_E_OK)) { @@ -361,11 +370,14 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, struct zram_meta *meta = zram->meta; page = bvec->bv_page; + read_lock(&meta->tb_lock); if (unlikely(!meta->table[index].handle) || zram_test_flag(meta, index, ZRAM_ZERO)) { + read_unlock(&meta->tb_lock); handle_zero_page(bvec); return 0; } + read_unlock(&meta->tb_lock); if (is_partial_io(bvec)) /* Use a temporary buffer to decompress the page */ @@ -442,10 +454,12 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (page_zero_filled(uncmem)) { kunmap_atomic(user_mem); /* Free memory associated with this sector now. */ + write_lock(&zram->meta->tb_lock); zram_free_page(zram, index); + zram_set_flag(meta, index, ZRAM_ZERO); + write_unlock(&zram->meta->tb_lock); atomic_inc(&zram->stats.pages_zero); - zram_set_flag(meta, index, ZRAM_ZERO); ret = 0; goto out; } @@ -497,10 +511,12 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, * Free memory associated with this sector * before overwriting unused sectors. */ + write_lock(&zram->meta->tb_lock); zram_free_page(zram, index); meta->table[index].handle = handle; meta->table[index].size = clen; + write_unlock(&zram->meta->tb_lock); /* Update stats */ atomic64_add(clen, &zram->stats.compr_size); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 1526c82..6278f42 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -84,6 +84,7 @@ struct zram_stats { }; struct zram_meta { + rwlock_t tb_lock; /* protect table */ void *compress_workmem; void *compress_buffer; struct table *table; @@ -97,7 +98,7 @@ struct zram_slot_free { struct zram { struct zram_meta *meta; - struct rw_semaphore lock; /* protect compression buffers, table, + struct rw_semaphore lock; /* protect compression buffers, * reads and writes */ -- cgit v1.1 From cdbd2a64c191255dba45d99c9ec4008a51b8987e Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:46:04 -0800 Subject: zram: remove workqueue for freeing removed pending slot Commit a0c516cbfc74 ("zram: don't grab mutex in zram_slot_free_noity") introduced free request pending code to avoid scheduling by mutex under spinlock and it was a mess which made code lenghty and increased overhead. Now, we don't need zram->lock any more to free slot so this patch reverts it and then, tb_lock should protect it. Signed-off-by: Minchan Kim Cc: Nitin Gupta Tested-by: Sergey Senozhatsky Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 54 +++++-------------------------------------- drivers/block/zram/zram_drv.h | 10 -------- 2 files changed, 6 insertions(+), 58 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 62b5727..52d27cb 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -533,20 +533,6 @@ out: return ret; } -static void handle_pending_slot_free(struct zram *zram) -{ - struct zram_slot_free *free_rq; - - spin_lock(&zram->slot_free_lock); - while (zram->slot_free_rq) { - free_rq = zram->slot_free_rq; - zram->slot_free_rq = free_rq->next; - zram_free_page(zram, free_rq->index); - kfree(free_rq); - } - spin_unlock(&zram->slot_free_lock); -} - static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, int offset, struct bio *bio, int rw) { @@ -558,7 +544,6 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, up_read(&zram->lock); } else { down_write(&zram->lock); - handle_pending_slot_free(zram); ret = zram_bvec_write(zram, bvec, index, offset); up_write(&zram->lock); } @@ -577,8 +562,6 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) return; } - flush_work(&zram->free_work); - meta = zram->meta; zram->init_done = 0; @@ -780,40 +763,19 @@ error: return 0; } -static void zram_slot_free(struct work_struct *work) -{ - struct zram *zram; - - zram = container_of(work, struct zram, free_work); - down_write(&zram->lock); - handle_pending_slot_free(zram); - up_write(&zram->lock); -} - -static void add_slot_free(struct zram *zram, struct zram_slot_free *free_rq) -{ - spin_lock(&zram->slot_free_lock); - free_rq->next = zram->slot_free_rq; - zram->slot_free_rq = free_rq; - spin_unlock(&zram->slot_free_lock); -} - static void zram_slot_free_notify(struct block_device *bdev, unsigned long index) { struct zram *zram; - struct zram_slot_free *free_rq; + struct zram_meta *meta; zram = bdev->bd_disk->private_data; - atomic64_inc(&zram->stats.notify_free); - - free_rq = kmalloc(sizeof(struct zram_slot_free), GFP_ATOMIC); - if (!free_rq) - return; + meta = zram->meta; - free_rq->index = index; - add_slot_free(zram, free_rq); - schedule_work(&zram->free_work); + write_lock(&meta->tb_lock); + zram_free_page(zram, index); + write_unlock(&meta->tb_lock); + atomic64_inc(&zram->stats.notify_free); } static const struct block_device_operations zram_devops = { @@ -860,10 +822,6 @@ static int create_device(struct zram *zram, int device_id) init_rwsem(&zram->lock); init_rwsem(&zram->init_lock); - INIT_WORK(&zram->free_work, zram_slot_free); - spin_lock_init(&zram->slot_free_lock); - zram->slot_free_rq = NULL; - zram->queue = blk_alloc_queue(GFP_KERNEL); if (!zram->queue) { pr_err("Error allocating disk queue for device %d\n", diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 6278f42..cb954ae 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -91,20 +91,11 @@ struct zram_meta { struct zs_pool *mem_pool; }; -struct zram_slot_free { - unsigned long index; - struct zram_slot_free *next; -}; - struct zram { struct zram_meta *meta; struct rw_semaphore lock; /* protect compression buffers, * reads and writes */ - - struct work_struct free_work; /* handle pending free request */ - struct zram_slot_free *slot_free_rq; /* list head of free request */ - struct request_queue *queue; struct gendisk *disk; int init_done; @@ -115,7 +106,6 @@ struct zram { * we can store in a disk. */ u64 disksize; /* bytes */ - spinlock_t slot_free_lock; struct zram_stats stats; }; -- cgit v1.1 From 8b555b9aa95ade2ccd262abb2ddd37ead2fdc00c Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 30 Jan 2014 15:46:06 -0800 Subject: zram: remove zram->lock in read path and change it with mutex Finally, we separated zram->lock dependency from 32bit stat/ table handling so there is no reason to use rw_semaphore between read and write path so this patch removes the lock from read path totally and changes rw_semaphore with mutex. So, we could do old: read-read: OK read-write: NO write-write: NO Now: read-read: OK read-write: OK write-write: NO The below data proves mixed workload performs well 11 times and there is also enhance on write-write path because current rw-semaphore doesn't support SPIN_ON_OWNER. It's side effect but anyway good thing for us. Write-related tests perform better (from 61% to 1058%) but read path has good/bad(from -2.22% to 1.45%) but they are all marginal within stddev. CPU 12 iozone -t -T -l 12 -u 12 -r 16K -s 60M -I +Z -V 0 ==Initial write ==Initial write records: 10 records: 10 avg: 516189.16 avg: 839907.96 std: 22486.53 (4.36%) std: 47902.17 (5.70%) max: 546970.60 max: 909910.35 min: 481131.54 min: 751148.38 ==Rewrite ==Rewrite records: 10 records: 10 avg: 509527.98 avg: 1050156.37 std: 45799.94 (8.99%) std: 40695.44 (3.88%) max: 611574.27 max: 1111929.26 min: 443679.95 min: 980409.62 ==Read ==Read records: 10 records: 10 avg: 4408624.17 avg: 4472546.76 std: 281152.61 (6.38%) std: 163662.78 (3.66%) max: 4867888.66 max: 4727351.03 min: 4058347.69 min: 4126520.88 ==Re-read ==Re-read records: 10 records: 10 avg: 4462147.53 avg: 4363257.75 std: 283546.11 (6.35%) std: 247292.63 (5.67%) max: 4912894.44 max: 4677241.75 min: 4131386.50 min: 4035235.84 ==Reverse Read ==Reverse Read records: 10 records: 10 avg: 4565865.97 avg: 4485818.08 std: 313395.63 (6.86%) std: 248470.10 (5.54%) max: 5232749.16 max: 4789749.94 min: 4185809.62 min: 3963081.34 ==Stride read ==Stride read records: 10 records: 10 avg: 4515981.80 avg: 4418806.01 std: 211192.32 (4.68%) std: 212837.97 (4.82%) max: 4889287.28 max: 4686967.22 min: 4210362.00 min: 4083041.84 ==Random read ==Random read records: 10 records: 10 avg: 4410525.23 avg: 4387093.18 std: 236693.22 (5.37%) std: 235285.23 (5.36%) max: 4713698.47 max: 4669760.62 min: 4057163.62 min: 3952002.16 ==Mixed workload ==Mixed workload records: 10 records: 10 avg: 243234.25 avg: 2818677.27 std: 28505.07 (11.72%) std: 195569.70 (6.94%) max: 288905.23 max: 3126478.11 min: 212473.16 min: 2484150.69 ==Random write ==Random write records: 10 records: 10 avg: 555887.07 avg: 1053057.79 std: 70841.98 (12.74%) std: 35195.36 (3.34%) max: 683188.28 max: 1096125.73 min: 437299.57 min: 992481.93 ==Pwrite ==Pwrite records: 10 records: 10 avg: 501745.93 avg: 810363.09 std: 16373.54 (3.26%) std: 19245.01 (2.37%) max: 518724.52 max: 833359.70 min: 464208.73 min: 765501.87 ==Pread ==Pread records: 10 records: 10 avg: 4539894.60 avg: 4457680.58 std: 197094.66 (4.34%) std: 188965.60 (4.24%) max: 4877170.38 max: 4689905.53 min: 4226326.03 min: 4095739.72 Change-Id: I59cc9c518bdeddb5b82f1b43aa674291161626bf Signed-off-by: Minchan Kim Cc: Nitin Gupta Tested-by: Sergey Senozhatsky Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 17 ++++++++--------- drivers/block/zram/zram_drv.h | 4 +--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 52d27cb..37296d3 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -238,6 +238,7 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) } rwlock_init(&meta->tb_lock); + mutex_init(&meta->buffer_lock); return meta; free_table: @@ -421,6 +422,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, unsigned char *user_mem, *cmem, *src, *uncmem = NULL; struct zram_meta *meta = zram->meta; static unsigned long zram_rs_time; + bool locked = false; page = bvec->bv_page; src = meta->compress_buffer; @@ -440,6 +442,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } + mutex_lock(&meta->buffer_lock); + locked = true; user_mem = kmap_atomic(page); if (is_partial_io(bvec)) { @@ -466,7 +470,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, meta->compress_workmem); - if (!is_partial_io(bvec)) { kunmap_atomic(user_mem); user_mem = NULL; @@ -525,6 +528,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, atomic_inc(&zram->stats.good_compress); out: + if (locked) + mutex_unlock(&meta->buffer_lock); if (is_partial_io(bvec)) kfree(uncmem); @@ -538,15 +543,10 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, { int ret; - if (rw == READ) { - down_read(&zram->lock); + if (rw == READ) ret = zram_bvec_read(zram, bvec, index, offset, bio); - up_read(&zram->lock); - } else { - down_write(&zram->lock); + else ret = zram_bvec_write(zram, bvec, index, offset); - up_write(&zram->lock); - } return ret; } @@ -819,7 +819,6 @@ static int create_device(struct zram *zram, int device_id) { int ret = -ENOMEM; - init_rwsem(&zram->lock); init_rwsem(&zram->init_lock); zram->queue = blk_alloc_queue(GFP_KERNEL); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index cb954ae..9b9a07f 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -89,13 +89,11 @@ struct zram_meta { void *compress_buffer; struct table *table; struct zs_pool *mem_pool; + struct mutex buffer_lock; /* protect compress buffers */ }; struct zram { struct zram_meta *meta; - struct rw_semaphore lock; /* protect compression buffers, - * reads and writes - */ struct request_queue *queue; struct gendisk *disk; int init_done; -- cgit v1.1 From 0378177cddc2351c087be43c0ef22fa60914611a Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Mon, 3 Mar 2014 15:38:34 -0800 Subject: zram: avoid null access when fail to alloc meta zram_meta_alloc could fail so caller should check it. Otherwise, your system will hang. Signed-off-by: Minchan Kim Acked-by: Jerome Marchand Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 37296d3..4520cb8 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -623,6 +623,8 @@ static ssize_t disksize_store(struct device *dev, disksize = PAGE_ALIGN(disksize); meta = zram_meta_alloc(disksize); + if (!meta) + return -ENOMEM; down_write(&zram->init_lock); if (zram->init_done) { up_write(&zram->init_lock); -- cgit v1.1 From 3b43eae394930a662d2110e22818e3ca86091a8f Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:00 -0700 Subject: zram: drop `init_done' struct zram member Introduce init_done() helper function which allows us to drop `init_done' struct zram member. init_done() uses the fact that ->init_done == 1 equals to ->meta != NULL. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 21 +++++++++++---------- drivers/block/zram/zram_drv.h | 1 - 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 4520cb8..254dc3e 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -50,6 +50,11 @@ static struct zram *zram_devices; /* Module params (documentation at end) */ static unsigned int num_devices = 1; +static inline int init_done(struct zram *zram) +{ + return zram->meta != NULL; +} + static inline struct zram *dev_to_zram(struct device *dev) { return (struct zram *)dev_to_disk(dev)->private_data; @@ -68,7 +73,7 @@ static ssize_t initstate_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%u\n", zram->init_done); + return sprintf(buf, "%u\n", init_done(zram)); } static ssize_t num_reads_show(struct device *dev, @@ -141,7 +146,7 @@ static ssize_t mem_used_total_show(struct device *dev, struct zram_meta *meta = zram->meta; down_read(&zram->init_lock); - if (zram->init_done) + if (init_done(zram)) val = zs_get_total_size_bytes(meta->mem_pool); up_read(&zram->init_lock); @@ -557,14 +562,12 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) struct zram_meta *meta; down_write(&zram->init_lock); - if (!zram->init_done) { + if (!init_done(zram)) { up_write(&zram->init_lock); return; } meta = zram->meta; - zram->init_done = 0; - /* Free all pages that are still in this zram device */ for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) { unsigned long handle = meta->table[index].handle; @@ -605,8 +608,6 @@ static void zram_init_device(struct zram *zram, struct zram_meta *meta) queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); zram->meta = meta; - zram->init_done = 1; - pr_debug("Initialization done!\n"); } @@ -626,7 +627,7 @@ static ssize_t disksize_store(struct device *dev, if (!meta) return -ENOMEM; down_write(&zram->init_lock); - if (zram->init_done) { + if (init_done(zram)) { up_write(&zram->init_lock); zram_meta_free(meta); pr_info("Cannot change disksize for initialized device\n"); @@ -745,7 +746,7 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) struct zram *zram = queue->queuedata; down_read(&zram->init_lock); - if (unlikely(!zram->init_done)) + if (unlikely(!init_done(zram))) goto error; if (!valid_io_request(zram, bio)) { @@ -870,7 +871,7 @@ static int create_device(struct zram *zram, int device_id) goto out_free_disk; } - zram->init_done = 0; + zram->meta = NULL; return 0; out_free_disk: diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 9b9a07f..7387f75 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -96,7 +96,6 @@ struct zram { struct zram_meta *meta; struct request_queue *queue; struct gendisk *disk; - int init_done; /* Prevent concurrent execution of device init, reset and R/W request */ struct rw_semaphore init_lock; /* -- cgit v1.1 From 25a1032b7aeb9020f5237700477f660622698f92 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:01 -0700 Subject: zram: do not pass rw argument to __zram_make_request() Do not pass rw argument down the __zram_make_request() -> zram_bvec_rw() chain, decode it in zram_bvec_rw() instead. Besides, this is the place where we distinguish READ and WRITE bio data directions, so account zram RW stats here, instead of __zram_make_request(). This also allows to account a real number of zram READ/WRITE operations, not just requests (single RW request may cause a number of zram RW ops with separate locking, compression/decompression, etc). Change-Id: Ibc8aa078d076ea84fb952eac6877ac48493e7288 Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 254dc3e..813f37e 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -544,14 +544,18 @@ out: } static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, - int offset, struct bio *bio, int rw) + int offset, struct bio *bio) { int ret; + int rw = bio_data_dir(bio); - if (rw == READ) + if (rw == READ) { + atomic64_inc(&zram->stats.num_reads); ret = zram_bvec_read(zram, bvec, index, offset, bio); - else + } else { + atomic64_inc(&zram->stats.num_writes); ret = zram_bvec_write(zram, bvec, index, offset); + } return ret; } @@ -683,22 +687,13 @@ out: return ret; } -static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) +static void __zram_make_request(struct zram *zram, struct bio *bio) { int i, offset; u32 index; struct bio_vec *bvec; - switch (rw) { - case READ: - atomic64_inc(&zram->stats.num_reads); - break; - case WRITE: - atomic64_inc(&zram->stats.num_writes); - break; - } - - index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; + index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; bio_for_each_segment(bvec, bio, i) { @@ -715,16 +710,15 @@ static void __zram_make_request(struct zram *zram, struct bio *bio, int rw) bv.bv_len = max_transfer_size; bv.bv_offset = bvec->bv_offset; - if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0) + if (zram_bvec_rw(zram, &bv, index, offset, bio) < 0) goto out; bv.bv_len = bvec->bv_len - max_transfer_size; bv.bv_offset += max_transfer_size; - if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0) + if (zram_bvec_rw(zram, &bv, index + 1, 0, bio) < 0) goto out; } else - if (zram_bvec_rw(zram, bvec, index, offset, bio, rw) - < 0) + if (zram_bvec_rw(zram, bvec, index, offset, bio) < 0) goto out; update_position(&index, &offset, bvec); @@ -754,7 +748,7 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) goto error; } - __zram_make_request(zram, bio, bio_data_dir(bio)); + __zram_make_request(zram, bio); up_read(&zram->init_lock); return 0; -- cgit v1.1 From 1d81955756ca5225b3f29fe5ea4afdd998a0789d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:02 -0700 Subject: zram: remove good and bad compress stats Remove `good' and `bad' compressed sub-requests stats. RW request may cause a number of RW sub-requests. zram used to account `good' compressed sub-queries (with compressed size less than 50% of original size), `bad' compressed sub-queries (with compressed size greater that 75% of original size), leaving sub-requests with compression size between 50% and 75% of original size not accounted and not reported. zram already accounts each sub-request's compression size so we can calculate real device compression ratio. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 11 ----------- drivers/block/zram/zram_drv.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 813f37e..ad46f67 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -301,7 +301,6 @@ static void zram_free_page(struct zram *zram, size_t index) { struct zram_meta *meta = zram->meta; unsigned long handle = meta->table[index].handle; - u16 size = meta->table[index].size; if (unlikely(!handle)) { /* @@ -315,14 +314,8 @@ static void zram_free_page(struct zram *zram, size_t index) return; } - if (unlikely(size > max_zpage_size)) - atomic_dec(&zram->stats.bad_compress); - zs_free(meta->mem_pool, handle); - if (size <= PAGE_SIZE / 2) - atomic_dec(&zram->stats.good_compress); - atomic64_sub(meta->table[index].size, &zram->stats.compr_size); atomic_dec(&zram->stats.pages_stored); @@ -487,7 +480,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, } if (unlikely(clen > max_zpage_size)) { - atomic_inc(&zram->stats.bad_compress); clen = PAGE_SIZE; src = NULL; if (is_partial_io(bvec)) @@ -529,9 +521,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, /* Update stats */ atomic64_add(clen, &zram->stats.compr_size); atomic_inc(&zram->stats.pages_stored); - if (clen <= PAGE_SIZE / 2) - atomic_inc(&zram->stats.good_compress); - out: if (locked) mutex_unlock(&meta->buffer_lock); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 7387f75..2ff1535 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -79,8 +79,6 @@ struct zram_stats { atomic64_t notify_free; /* no. of swap slot free notifications */ atomic_t pages_zero; /* no. of zero filled pages */ atomic_t pages_stored; /* no. of pages currently stored */ - atomic_t good_compress; /* % of pages with compression ratio<=50% */ - atomic_t bad_compress; /* % of pages with compression ratio>=75% */ }; struct zram_meta { -- cgit v1.1 From 6ab83f33b8659f5ca6126954d0dc5ad553835c1d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:03 -0700 Subject: zram: use atomic64_t for all zram stats This is a preparation patch for stats code duplication removal. 1) use atomic64_t for `pages_zero' and `pages_stored' zram stats. 2) `compr_size' and `pages_zero' struct zram_stats members did not follow the existing device attr naming scheme: zram_stats.ATTR has ATTR_show() function. rename them: -- compr_size -> compr_data_size -- pages_zero -> zero_pages Minchan Kim's note: If we really have trouble with atomic stat operation, we could change it with percpu_counter so that it could solve atomic overhead and unnecessary memory space by introducing unsigned long instead of 64bit atomic_t. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 18 +++++++++--------- drivers/block/zram/zram_drv.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index ad46f67..5497ef8 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -117,7 +117,7 @@ static ssize_t zero_pages_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%u\n", atomic_read(&zram->stats.pages_zero)); + return sprintf(buf, "%llu\n", (u64)atomic64_read(&zram->stats.zero_pages)); } static ssize_t orig_data_size_show(struct device *dev, @@ -126,7 +126,7 @@ static ssize_t orig_data_size_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - (u64)(atomic_read(&zram->stats.pages_stored)) << PAGE_SHIFT); + (u64)(atomic64_read(&zram->stats.pages_stored)) << PAGE_SHIFT); } static ssize_t compr_data_size_show(struct device *dev, @@ -135,7 +135,7 @@ static ssize_t compr_data_size_show(struct device *dev, struct zram *zram = dev_to_zram(dev); return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.compr_size)); + (u64)atomic64_read(&zram->stats.compr_data_size)); } static ssize_t mem_used_total_show(struct device *dev, @@ -309,15 +309,15 @@ static void zram_free_page(struct zram *zram, size_t index) */ if (zram_test_flag(meta, index, ZRAM_ZERO)) { zram_clear_flag(meta, index, ZRAM_ZERO); - atomic_dec(&zram->stats.pages_zero); + atomic64_dec(&zram->stats.zero_pages); } return; } zs_free(meta->mem_pool, handle); - atomic64_sub(meta->table[index].size, &zram->stats.compr_size); - atomic_dec(&zram->stats.pages_stored); + atomic64_sub(meta->table[index].size, &zram->stats.compr_data_size); + atomic64_dec(&zram->stats.pages_stored); meta->table[index].handle = 0; meta->table[index].size = 0; @@ -461,7 +461,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, zram_set_flag(meta, index, ZRAM_ZERO); write_unlock(&zram->meta->tb_lock); - atomic_inc(&zram->stats.pages_zero); + atomic64_inc(&zram->stats.zero_pages); ret = 0; goto out; } @@ -519,8 +519,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, write_unlock(&zram->meta->tb_lock); /* Update stats */ - atomic64_add(clen, &zram->stats.compr_size); - atomic_inc(&zram->stats.pages_stored); + atomic64_add(clen, &zram->stats.compr_data_size); + atomic64_inc(&zram->stats.pages_stored); out: if (locked) mutex_unlock(&meta->buffer_lock); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 2ff1535..4de17ce 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -70,15 +70,15 @@ struct table { } __aligned(4); struct zram_stats { - atomic64_t compr_size; /* compressed size of pages stored */ + atomic64_t compr_data_size; /* compressed size of pages stored */ atomic64_t num_reads; /* failed + successful */ atomic64_t num_writes; /* --do-- */ atomic64_t failed_reads; /* should NEVER! happen */ atomic64_t failed_writes; /* can happen when memory is too low */ atomic64_t invalid_io; /* non-page-aligned I/O requests */ atomic64_t notify_free; /* no. of swap slot free notifications */ - atomic_t pages_zero; /* no. of zero filled pages */ - atomic_t pages_stored; /* no. of pages currently stored */ + atomic64_t zero_pages; /* no. of zero filled pages */ + atomic64_t pages_stored; /* no. of pages currently stored */ }; struct zram_meta { -- cgit v1.1 From 9ef962eb44d9ee9da3e805cf47d9fab2f053adbe Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:04 -0700 Subject: zram: remove zram stats code duplication Introduce ZRAM_ATTR_RO macro that generates device_attribute and default ATTR show() function for existing atomic64_t zram stats. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 82 ++++++++++++------------------------------- 1 file changed, 23 insertions(+), 59 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 5497ef8..6f1e7b5 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -50,6 +50,17 @@ static struct zram *zram_devices; /* Module params (documentation at end) */ static unsigned int num_devices = 1; +#define ZRAM_ATTR_RO(name) \ +static ssize_t zram_attr_##name##_show(struct device *d, \ + struct device_attribute *attr, char *b) \ +{ \ + struct zram *zram = dev_to_zram(d); \ + return sprintf(b, "%llu\n", \ + (u64)atomic64_read(&zram->stats.name)); \ +} \ +static struct device_attribute dev_attr_##name = \ + __ATTR(name, S_IRUGO, zram_attr_##name##_show, NULL); + static inline int init_done(struct zram *zram) { return zram->meta != NULL; @@ -71,53 +82,14 @@ static ssize_t disksize_show(struct device *dev, static ssize_t initstate_show(struct device *dev, struct device_attribute *attr, char *buf) { + u32 val; struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%u\n", init_done(zram)); -} - -static ssize_t num_reads_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_reads)); -} - -static ssize_t num_writes_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.num_writes)); -} - -static ssize_t invalid_io_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.invalid_io)); -} - -static ssize_t notify_free_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.notify_free)); -} - -static ssize_t zero_pages_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); + down_read(&zram->init_lock); + val = init_done(zram); + up_read(&zram->init_lock); - return sprintf(buf, "%llu\n", (u64)atomic64_read(&zram->stats.zero_pages)); + return sprintf(buf, "%u\n", val); } static ssize_t orig_data_size_show(struct device *dev, @@ -129,15 +101,6 @@ static ssize_t orig_data_size_show(struct device *dev, (u64)(atomic64_read(&zram->stats.pages_stored)) << PAGE_SHIFT); } -static ssize_t compr_data_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct zram *zram = dev_to_zram(dev); - - return sprintf(buf, "%llu\n", - (u64)atomic64_read(&zram->stats.compr_data_size)); -} - static ssize_t mem_used_total_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -773,15 +736,16 @@ static DEVICE_ATTR(disksize, S_IRUGO | S_IWUSR, disksize_show, disksize_store); static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL); static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); -static DEVICE_ATTR(num_reads, S_IRUGO, num_reads_show, NULL); -static DEVICE_ATTR(num_writes, S_IRUGO, num_writes_show, NULL); -static DEVICE_ATTR(invalid_io, S_IRUGO, invalid_io_show, NULL); -static DEVICE_ATTR(notify_free, S_IRUGO, notify_free_show, NULL); -static DEVICE_ATTR(zero_pages, S_IRUGO, zero_pages_show, NULL); static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); -static DEVICE_ATTR(compr_data_size, S_IRUGO, compr_data_size_show, NULL); static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); +ZRAM_ATTR_RO(num_reads); +ZRAM_ATTR_RO(num_writes); +ZRAM_ATTR_RO(invalid_io); +ZRAM_ATTR_RO(notify_free); +ZRAM_ATTR_RO(zero_pages); +ZRAM_ATTR_RO(compr_data_size); + static struct attribute *zram_disk_attrs[] = { &dev_attr_disksize.attr, &dev_attr_initstate.attr, -- cgit v1.1 From 1fd5551e0b6aa20c5ff390e3a53a71b84593c8c8 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:05 -0700 Subject: zram: report failed read and write stats zram accounted but did not report numbers of failed read and write queries. make these stats available as failed_reads and failed_writes attrs. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Acked-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 6f1e7b5..1a53643 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -741,6 +741,8 @@ static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); ZRAM_ATTR_RO(num_reads); ZRAM_ATTR_RO(num_writes); +ZRAM_ATTR_RO(failed_reads); +ZRAM_ATTR_RO(failed_writes); ZRAM_ATTR_RO(invalid_io); ZRAM_ATTR_RO(notify_free); ZRAM_ATTR_RO(zero_pages); @@ -752,6 +754,8 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_reset.attr, &dev_attr_num_reads.attr, &dev_attr_num_writes.attr, + &dev_attr_failed_reads.attr, + &dev_attr_failed_writes.attr, &dev_attr_invalid_io.attr, &dev_attr_notify_free.attr, &dev_attr_zero_pages.attr, -- cgit v1.1 From 0af6b10ff713d6813006fcf2d26d59804e1da162 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:06 -0700 Subject: zram: drop not used table `count' member struct table `count' member is not used. Signed-off-by: Sergey Senozhatsky Cc: Minchan Kim Acked-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 4de17ce..0d5cbcd 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -65,7 +65,6 @@ enum zram_pageflags { struct table { unsigned long handle; u16 size; /* object size (excluding header) */ - u8 count; /* object ref count (not yet used) */ u8 flags; } __aligned(4); -- cgit v1.1 From c20ebd785b1ce8e68f4f9eafdaf1ea1f7fa2eff8 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:07 -0700 Subject: zram: move zram size warning to documentation Move zram warning about disksize and size of memory correlation to zram documentation. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/blockdev/zram.txt | 5 +++++ drivers/block/zram/zram_drv.c | 15 --------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt index 765d790..f82d084 100644 --- a/Documentation/blockdev/zram.txt +++ b/Documentation/blockdev/zram.txt @@ -35,6 +35,11 @@ Following shows a typical sequence of steps for using zram. echo 512M > /sys/block/zram0/disksize echo 1G > /sys/block/zram0/disksize +Note: +There is little point creating a zram of greater than twice the size of memory +since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the +size of the disk when not in use so a huge zram is wasteful. + 3) Activate: mkswap /dev/zram0 swapon /dev/zram0 diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 1a53643..70d7adc 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -546,23 +546,8 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) static void zram_init_device(struct zram *zram, struct zram_meta *meta) { - if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) { - pr_info( - "There is little point creating a zram of greater than " - "twice the size of memory since we expect a 2:1 compression " - "ratio. Note that zram uses about 0.1%% of the size of " - "the disk when not in use so a huge zram is " - "wasteful.\n" - "\tMemory Size: %lu kB\n" - "\tSize you selected: %llu kB\n" - "Continuing anyway ...\n", - (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10 - ); - } - /* zram devices sort of resembles non-rotational disks */ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); - zram->meta = meta; pr_debug("Initialization done!\n"); } -- cgit v1.1 From 64d8df891e3d1f187f129df667f7b347d5612b37 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:09 -0700 Subject: zram: delete zram_init_device() allocate new `zram_meta' in disksize_store() only for uninitialised zram device, saving a number of allocations and deallocations in case if disksize_store() was called on currently used device. at the same time zram_meta stack variable is not necessary, because we can set ->meta directly. there is also no need in setting QUEUE_FLAG_NONROT queue on every disksize_store(), set it once during device creation. [minchan@kernel.org: handle zram->meta alloc fail case] [minchan@kernel.org: prevent lockdep spew of init_lock] Signed-off-by: Sergey Senozhatsky Signed-off-by: Minchan Kim Acked-by: Jerome Marchand Cc: Sasha Levin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 70d7adc..f37f3b0 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -544,14 +544,6 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) up_write(&zram->init_lock); } -static void zram_init_device(struct zram *zram, struct zram_meta *meta) -{ - /* zram devices sort of resembles non-rotational disks */ - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); - zram->meta = meta; - pr_debug("Initialization done!\n"); -} - static ssize_t disksize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -567,17 +559,18 @@ static ssize_t disksize_store(struct device *dev, meta = zram_meta_alloc(disksize); if (!meta) return -ENOMEM; + down_write(&zram->init_lock); if (init_done(zram)) { - up_write(&zram->init_lock); zram_meta_free(meta); + up_write(&zram->init_lock); pr_info("Cannot change disksize for initialized device\n"); return -EBUSY; } + zram->meta = meta; zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); - zram_init_device(zram, meta); up_write(&zram->init_lock); return len; @@ -787,7 +780,8 @@ static int create_device(struct zram *zram, int device_id) /* Actual capacity set using syfs (/sys/block/zram/disksize */ set_capacity(zram->disk, 0); - + /* zram devices sort of resembles non-rotational disks */ + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); /* * To ensure that we always get PAGE_SIZE aligned * and n*PAGE_SIZED sized I/O requests. -- cgit v1.1 From 414bf1cfe2126d3925e62b45763744bd919bcef9 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:11 -0700 Subject: zram: introduce compressing backend abstraction ZRAM performs direct LZO compression algorithm calls, making it the one and only option. While LZO is generally performs well, LZ4 algorithm tends to have a faster decompression (see http://code.google.com/p/lz4/ for full report) Name Ratio C.speed D.speed MB/s MB/s LZ4 (r101) 2.084 422 1820 LZO 2.06 2.106 414 600 Thus, users who have mostly read (decompress) usage scenarious or mixed workflow (writes with relatively high read ops number) will benefit from using LZ4 compression backend. Introduce compressing backend abstraction zcomp in order to support multiple compression algorithms with the following set of operations: .create .destroy .compress .decompress Schematically zram write() usually contains the following steps: 0) preparation (decompression of partioal IO, etc.) 1) lock buffer_lock mutex (protects meta compress buffers) 2) compress (using meta compress buffers) 3) alloc and map zs_pool object 4) copy compressed data (from meta compress buffers) to object allocated by 3) 5) free previous pool page, assign a new one 6) unlock buffer_lock mutex As we can see, compressing buffers must remain untouched from 1) to 4), because, otherwise, concurrent write() can overwrite data. At the same time, zram_meta must be aware of a) specific compression algorithm memory requirements and b) necessary locking to protect compression buffers. To remove requirement a) new struct zcomp_strm introduced, which contains a compress/decompress `buffer' and compression algorithm `private' part. While struct zcomp implements zcomp_strm stream handling and locking and removes requirement b) from zram meta. zcomp ->create() and ->destroy(), respectively, allocate and deallocate algorithm specific zcomp_strm `private' part. Every zcomp has zcomp stream and mutex to protect its compression stream. Stream usage semantics remains the same -- only one write can hold stream lock and use its buffers. zcomp_strm_find() turns caller into exclusive user of a stream (holding stream mutex until zram release stream), and zcomp_strm_release() makes zcomp stream available (unlock the stream mutex). Hence no concurrent write (compression) operations possible at the moment. iozone -t 3 -R -r 16K -s 60M -I +Z test base patched -------------------------------------------------- Initial write 597992.91 591660.58 Rewrite 609674.34 616054.97 Read 2404771.75 2452909.12 Re-read 2459216.81 2470074.44 Reverse Read 1652769.66 1589128.66 Stride read 2202441.81 2202173.31 Random read 2236311.47 2276565.31 Mixed workload 1423760.41 1709760.06 Random write 579584.08 615933.86 Pwrite 597550.02 594933.70 Pread 1703672.53 1718126.72 Fwrite 1330497.06 1461054.00 Fread 3922851.00 3957242.62 Usage examples: comp = zcomp_create(NAME) /* NAME e.g. "lzo" */ which initialises compressing backend if requested algorithm is supported. Compress: zstrm = zcomp_strm_find(comp) zcomp_compress(comp, zstrm, src, &dst_len) [..] /* copy compressed data */ zcomp_strm_release(comp, zstrm) Decompress: zcomp_decompress(comp, src, src_len, dst); Free compessing backend and its zcomp stream: zcomp_destroy(comp) Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zcomp.c | 115 +++++++++++++++++++++++++++++++++++++++++ drivers/block/zram/zcomp.h | 58 +++++++++++++++++++++ drivers/block/zram/zcomp_lzo.c | 47 +++++++++++++++++ drivers/block/zram/zcomp_lzo.h | 17 ++++++ 4 files changed, 237 insertions(+) create mode 100644 drivers/block/zram/zcomp.c create mode 100644 drivers/block/zram/zcomp.h create mode 100644 drivers/block/zram/zcomp_lzo.c create mode 100644 drivers/block/zram/zcomp_lzo.h diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c new file mode 100644 index 0000000..22f4ae2 --- /dev/null +++ b/drivers/block/zram/zcomp.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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 "zcomp.h" +#include "zcomp_lzo.h" + +static struct zcomp_backend *find_backend(const char *compress) +{ + if (strncmp(compress, "lzo", 3) == 0) + return &zcomp_lzo; + return NULL; +} + +static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) +{ + if (zstrm->private) + comp->backend->destroy(zstrm->private); + free_pages((unsigned long)zstrm->buffer, 1); + kfree(zstrm); +} + +/* + * allocate new zcomp_strm structure with ->private initialized by + * backend, return NULL on error + */ +static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) +{ + struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL); + if (!zstrm) + return NULL; + + zstrm->private = comp->backend->create(); + /* + * allocate 2 pages. 1 for compressed data, plus 1 extra for the + * case when compressed size is larger than the original one + */ + zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!zstrm->private || !zstrm->buffer) { + zcomp_strm_free(comp, zstrm); + zstrm = NULL; + } + return zstrm; +} + +struct zcomp_strm *zcomp_strm_find(struct zcomp *comp) +{ + mutex_lock(&comp->strm_lock); + return comp->zstrm; +} + +void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm) +{ + mutex_unlock(&comp->strm_lock); +} + +int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, + const unsigned char *src, size_t *dst_len) +{ + return comp->backend->compress(src, zstrm->buffer, dst_len, + zstrm->private); +} + +int zcomp_decompress(struct zcomp *comp, const unsigned char *src, + size_t src_len, unsigned char *dst) +{ + return comp->backend->decompress(src, src_len, dst); +} + +void zcomp_destroy(struct zcomp *comp) +{ + zcomp_strm_free(comp, comp->zstrm); + kfree(comp); +} + +/* + * search available compressors for requested algorithm. + * allocate new zcomp and initialize it. return NULL + * if requested algorithm is not supported or in case + * of init error + */ +struct zcomp *zcomp_create(const char *compress) +{ + struct zcomp *comp; + struct zcomp_backend *backend; + + backend = find_backend(compress); + if (!backend) + return NULL; + + comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); + if (!comp) + return NULL; + + comp->backend = backend; + mutex_init(&comp->strm_lock); + + comp->zstrm = zcomp_strm_alloc(comp); + if (!comp->zstrm) { + kfree(comp); + return NULL; + } + return comp; +} diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h new file mode 100644 index 0000000..c9a98e1 --- /dev/null +++ b/drivers/block/zram/zcomp.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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. + */ + +#ifndef _ZCOMP_H_ +#define _ZCOMP_H_ + +#include + +struct zcomp_strm { + /* compression/decompression buffer */ + void *buffer; + /* + * The private data of the compression stream, only compression + * stream backend can touch this (e.g. compression algorithm + * working memory) + */ + void *private; +}; + +/* static compression backend */ +struct zcomp_backend { + int (*compress)(const unsigned char *src, unsigned char *dst, + size_t *dst_len, void *private); + + int (*decompress)(const unsigned char *src, size_t src_len, + unsigned char *dst); + + void *(*create)(void); + void (*destroy)(void *private); + + const char *name; +}; + +/* dynamic per-device compression frontend */ +struct zcomp { + struct mutex strm_lock; + struct zcomp_strm *zstrm; + struct zcomp_backend *backend; +}; + +struct zcomp *zcomp_create(const char *comp); +void zcomp_destroy(struct zcomp *comp); + +struct zcomp_strm *zcomp_strm_find(struct zcomp *comp); +void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm); + +int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, + const unsigned char *src, size_t *dst_len); + +int zcomp_decompress(struct zcomp *comp, const unsigned char *src, + size_t src_len, unsigned char *dst); +#endif /* _ZCOMP_H_ */ diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c new file mode 100644 index 0000000..da1bc47 --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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 "zcomp_lzo.h" + +static void *lzo_create(void) +{ + return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); +} + +static void lzo_destroy(void *private) +{ + kfree(private); +} + +static int lzo_compress(const unsigned char *src, unsigned char *dst, + size_t *dst_len, void *private) +{ + int ret = lzo1x_1_compress(src, PAGE_SIZE, dst, dst_len, private); + return ret == LZO_E_OK ? 0 : ret; +} + +static int lzo_decompress(const unsigned char *src, size_t src_len, + unsigned char *dst) +{ + size_t dst_len = PAGE_SIZE; + int ret = lzo1x_decompress_safe(src, src_len, dst, &dst_len); + return ret == LZO_E_OK ? 0 : ret; +} + +struct zcomp_backend zcomp_lzo = { + .compress = lzo_compress, + .decompress = lzo_decompress, + .create = lzo_create, + .destroy = lzo_destroy, + .name = "lzo", +}; diff --git a/drivers/block/zram/zcomp_lzo.h b/drivers/block/zram/zcomp_lzo.h new file mode 100644 index 0000000..128c580 --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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. + */ + +#ifndef _ZCOMP_LZO_H_ +#define _ZCOMP_LZO_H_ + +#include "zcomp.h" + +extern struct zcomp_backend zcomp_lzo; + +#endif /* _ZCOMP_LZO_H_ */ -- cgit v1.1 From 18221099f0957d7c21c932c38cb2247534f377b2 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:12 -0700 Subject: zram: use zcomp compressing backends Do not perform direct LZO compress/decompress calls, initialise and use zcomp LZO backend (single compression stream) instead. [akpm@linux-foundation.org: resolve conflicts with zram-delete-zram_init_device-fix.patch] Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Change-Id: I0aeab9e6396f8a90dc7f0959cff0ef29b5682301 --- drivers/block/zram/Makefile | 2 +- drivers/block/zram/zram_drv.c | 69 +++++++++++++++++++------------------------ drivers/block/zram/zram_drv.h | 8 ++--- 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile index cb0f9ce..757c6a5 100644 --- a/drivers/block/zram/Makefile +++ b/drivers/block/zram/Makefile @@ -1,3 +1,3 @@ -zram-y := zram_drv.o +zram-y := zcomp_lzo.o zcomp.o zram_drv.o obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index f37f3b0..9678407 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -40,6 +39,7 @@ /* Globals */ static int zram_major; static struct zram *zram_devices; +static const char *default_compressor = "lzo"; /* * We don't need to see memory allocation errors more than once every 1 @@ -167,8 +167,6 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) static void zram_meta_free(struct zram_meta *meta) { zs_destroy_pool(meta->mem_pool); - kfree(meta->compress_workmem); - free_pages((unsigned long)meta->compress_buffer, 1); vfree(meta->table); kfree(meta); } @@ -180,22 +178,11 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) if (!meta) goto out; - meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); - if (!meta->compress_workmem) - goto free_meta; - - meta->compress_buffer = - (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!meta->compress_buffer) { - pr_err("Error allocating compressor buffer space\n"); - goto free_workmem; - } - num_pages = disksize >> PAGE_SHIFT; meta->table = vzalloc(num_pages * sizeof(*meta->table)); if (!meta->table) { pr_err("Error allocating zram address table\n"); - goto free_buffer; + goto free_meta; } meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM | @@ -206,15 +193,10 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) } rwlock_init(&meta->tb_lock); - mutex_init(&meta->buffer_lock); return meta; free_table: vfree(meta->table); -free_buffer: - free_pages((unsigned long)meta->compress_buffer, 1); -free_workmem: - kfree(meta->compress_workmem); free_meta: kfree(meta); meta = NULL; @@ -288,8 +270,7 @@ static void zram_free_page(struct zram *zram, size_t index) static int zram_decompress_page(struct zram *zram, char *mem, u32 index) { - int ret = LZO_E_OK; - size_t clen = PAGE_SIZE; + int ret = 0; unsigned char *cmem; struct zram_meta *meta = zram->meta; unsigned long handle; @@ -309,12 +290,12 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) if (size == PAGE_SIZE) copy_page(mem, cmem); else - ret = lzo1x_decompress_safe(cmem, size, mem, &clen); + ret = zcomp_decompress(zram->comp, cmem, size, mem); zs_unmap_object(meta->mem_pool, handle); read_unlock(&meta->tb_lock); /* Should NEVER happen. Return bio error if it does. */ - if (unlikely(ret != LZO_E_OK)) { + if (unlikely(ret)) { pr_err("Decompression failed! err=%d, page=%u\n", ret, index); atomic64_inc(&zram->stats.failed_reads); return ret; @@ -357,7 +338,7 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, ret = zram_decompress_page(zram, uncmem, index); /* Should NEVER happen. Return bio error if it does. */ - if (unlikely(ret != LZO_E_OK)) + if (unlikely(ret)) goto out_cleanup; if (is_partial_io(bvec)) @@ -383,11 +364,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, unsigned char *user_mem, *cmem, *src, *uncmem = NULL; struct zram_meta *meta = zram->meta; static unsigned long zram_rs_time; + struct zcomp_strm *zstrm; bool locked = false; page = bvec->bv_page; - src = meta->compress_buffer; - if (is_partial_io(bvec)) { /* * This is a partial IO. We need to read the full page @@ -403,7 +383,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } - mutex_lock(&meta->buffer_lock); + zstrm = zcomp_strm_find(zram->comp); locked = true; user_mem = kmap_atomic(page); @@ -429,22 +409,20 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } - ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, - meta->compress_workmem); + ret = zcomp_compress(zram->comp, zstrm, uncmem, &clen); if (!is_partial_io(bvec)) { kunmap_atomic(user_mem); user_mem = NULL; uncmem = NULL; } - if (unlikely(ret != LZO_E_OK)) { + if (unlikely(ret)) { pr_err("Compression failed! err=%d\n", ret); goto out; } - + src = zstrm->buffer; if (unlikely(clen > max_zpage_size)) { clen = PAGE_SIZE; - src = NULL; if (is_partial_io(bvec)) src = uncmem; } @@ -468,6 +446,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, memcpy(cmem, src, clen); } + zcomp_strm_release(zram->comp, zstrm); + locked = false; zs_unmap_object(meta->mem_pool, handle); /* @@ -486,10 +466,9 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, atomic64_inc(&zram->stats.pages_stored); out: if (locked) - mutex_unlock(&meta->buffer_lock); + zcomp_strm_release(zram->comp, zstrm); if (is_partial_io(bvec)) kfree(uncmem); - if (ret) atomic64_inc(&zram->stats.failed_writes); return ret; @@ -533,6 +512,7 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) zs_free(meta->mem_pool, handle); } + zcomp_destroy(zram->comp); zram_meta_free(zram->meta); zram->meta = NULL; /* Reset stats */ @@ -550,6 +530,7 @@ static ssize_t disksize_store(struct device *dev, u64 disksize; struct zram_meta *meta; struct zram *zram = dev_to_zram(dev); + int err; disksize = memparse(buf, NULL); if (!disksize) @@ -562,10 +543,17 @@ static ssize_t disksize_store(struct device *dev, down_write(&zram->init_lock); if (init_done(zram)) { - zram_meta_free(meta); - up_write(&zram->init_lock); pr_info("Cannot change disksize for initialized device\n"); - return -EBUSY; + err = -EBUSY; + goto out_free_meta; + } + + zram->comp = zcomp_create(default_compressor); + if (!zram->comp) { + pr_info("Cannot initialise %s compressing backend\n", + default_compressor); + err = -EINVAL; + goto out_free_meta; } zram->meta = meta; @@ -574,6 +562,11 @@ static ssize_t disksize_store(struct device *dev, up_write(&zram->init_lock); return len; + +out_free_meta: + up_write(&zram->init_lock); + zram_meta_free(meta); + return err; } static ssize_t reset_store(struct device *dev, diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 0d5cbcd..c6b910e 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -17,9 +17,10 @@ #define _ZRAM_DRV_H_ #include -#include #include +#include "zcomp.h" + /* * Some arbitrary value. This is just to catch * invalid value for num_devices module parameter. @@ -82,17 +83,16 @@ struct zram_stats { struct zram_meta { rwlock_t tb_lock; /* protect table */ - void *compress_workmem; - void *compress_buffer; struct table *table; struct zs_pool *mem_pool; - struct mutex buffer_lock; /* protect compress buffers */ }; struct zram { struct zram_meta *meta; struct request_queue *queue; struct gendisk *disk; + struct zcomp *comp; + /* Prevent concurrent execution of device init, reset and R/W request */ struct rw_semaphore init_lock; /* -- cgit v1.1 From 0379e619c298e0b53543df4761818f42e0d5a481 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:13 -0700 Subject: zram: factor out single stream compression This is preparation patch to add multi stream support to zcomp. Introduce struct zcomp_strm_single and a set of functions to manage zcomp_strm stream access. zcomp_strm_single implements single compession stream, same way as current zcomp implementation. This moves zcomp_strm stream control and locking from zcomp, so compressing backend zcomp is not aware of required locking. Single and multi streams require different locking schemes. Minchan Kim reported that spinlock-based locking scheme (which is used in multi stream implementation) has demonstrated a severe perfomance regression for single compression stream case, comparing to mutex-based. see https://lkml.org/lkml/2014/2/18/16 The following set of functions added: - zcomp_strm_single_find()/zcomp_strm_single_release() find and release a compression stream, implement required locking - zcomp_strm_single_create()/zcomp_strm_single_destroy() create and destroy zcomp_strm_single New ->strm_find() and ->strm_release() callbacks added to zcomp, which are set to zcomp_strm_single_find() and zcomp_strm_single_release() during initialisation. Instead of direct locking and zcomp_strm access from zcomp_strm_find() and zcomp_strm_release(), zcomp now calls ->strm_find() and ->strm_release() correspondingly. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zcomp.c | 62 ++++++++++++++++++++++++++++++++++++++++------ drivers/block/zram/zcomp.h | 7 ++++-- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index 22f4ae2..72e8071 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -16,6 +16,14 @@ #include "zcomp.h" #include "zcomp_lzo.h" +/* + * single zcomp_strm backend + */ +struct zcomp_strm_single { + struct mutex strm_lock; + struct zcomp_strm *zstrm; +}; + static struct zcomp_backend *find_backend(const char *compress) { if (strncmp(compress, "lzo", 3) == 0) @@ -54,15 +62,56 @@ static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) return zstrm; } +static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp) +{ + struct zcomp_strm_single *zs = comp->stream; + mutex_lock(&zs->strm_lock); + return zs->zstrm; +} + +static void zcomp_strm_single_release(struct zcomp *comp, + struct zcomp_strm *zstrm) +{ + struct zcomp_strm_single *zs = comp->stream; + mutex_unlock(&zs->strm_lock); +} + +static void zcomp_strm_single_destroy(struct zcomp *comp) +{ + struct zcomp_strm_single *zs = comp->stream; + zcomp_strm_free(comp, zs->zstrm); + kfree(zs); +} + +static int zcomp_strm_single_create(struct zcomp *comp) +{ + struct zcomp_strm_single *zs; + + comp->destroy = zcomp_strm_single_destroy; + comp->strm_find = zcomp_strm_single_find; + comp->strm_release = zcomp_strm_single_release; + zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL); + if (!zs) + return -ENOMEM; + + comp->stream = zs; + mutex_init(&zs->strm_lock); + zs->zstrm = zcomp_strm_alloc(comp); + if (!zs->zstrm) { + kfree(zs); + return -ENOMEM; + } + return 0; +} + struct zcomp_strm *zcomp_strm_find(struct zcomp *comp) { - mutex_lock(&comp->strm_lock); - return comp->zstrm; + return comp->strm_find(comp); } void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm) { - mutex_unlock(&comp->strm_lock); + comp->strm_release(comp, zstrm); } int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, @@ -80,7 +129,7 @@ int zcomp_decompress(struct zcomp *comp, const unsigned char *src, void zcomp_destroy(struct zcomp *comp) { - zcomp_strm_free(comp, comp->zstrm); + comp->destroy(comp); kfree(comp); } @@ -104,10 +153,7 @@ struct zcomp *zcomp_create(const char *compress) return NULL; comp->backend = backend; - mutex_init(&comp->strm_lock); - - comp->zstrm = zcomp_strm_alloc(comp); - if (!comp->zstrm) { + if (zcomp_strm_single_create(comp) != 0) { kfree(comp); return NULL; } diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index c9a98e1..dc3500d 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -39,9 +39,12 @@ struct zcomp_backend { /* dynamic per-device compression frontend */ struct zcomp { - struct mutex strm_lock; - struct zcomp_strm *zstrm; + void *stream; struct zcomp_backend *backend; + + struct zcomp_strm *(*strm_find)(struct zcomp *comp); + void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm); + void (*destroy)(struct zcomp *comp); }; struct zcomp *zcomp_create(const char *comp); -- cgit v1.1 From 21d7e34531302ffb35a4ca8c6fd67c14060b4100 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:08 -0700 Subject: zram: document failed_reads, failed_writes stats Document `failed_reads' and `failed_writes' device attributes. Remove info about `discard' - there is no such zram attr. Signed-off-by: Sergey Senozhatsky Cc: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-block-zram | 26 ++++++++++++++++---------- Documentation/blockdev/zram.txt | 3 ++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-block-zram b/Documentation/ABI/testing/sysfs-block-zram index c8b3b48..d460d12 100644 --- a/Documentation/ABI/testing/sysfs-block-zram +++ b/Documentation/ABI/testing/sysfs-block-zram @@ -42,6 +42,21 @@ Description: The invalid_io file is read-only and specifies the number of non-page-size-aligned I/O requests issued to this device. +What: /sys/block/zram/failed_reads +Date: February 2014 +Contact: Sergey Senozhatsky +Description: + The failed_reads file is read-only and specifies the number of + failed reads happened on this device. + + +What: /sys/block/zram/failed_writes +Date: February 2014 +Contact: Sergey Senozhatsky +Description: + The failed_writes file is read-only and specifies the number of + failed writes happened on this device. + What: /sys/block/zram/notify_free Date: August 2010 Contact: Nitin Gupta @@ -52,15 +67,6 @@ Description: is freed. This statistic is applicable only when this disk is being used as a swap disk. -What: /sys/block/zram/discard -Date: August 2010 -Contact: Nitin Gupta -Description: - The discard file is read-only and specifies the number of - discard requests received by this device. These requests - provide information to block device regarding blocks which are - no longer used by filesystem. - What: /sys/block/zram/zero_pages Date: August 2010 Contact: Nitin Gupta @@ -96,4 +102,4 @@ Description: overhead, allocated for this disk. So, allocator space efficiency can be calculated using compr_data_size and this statistic. - Unit: bytes \ No newline at end of file + Unit: bytes diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt index f82d084..d389fb7 100644 --- a/Documentation/blockdev/zram.txt +++ b/Documentation/blockdev/zram.txt @@ -53,9 +53,10 @@ size of the disk when not in use so a huge zram is wasteful. disksize num_reads num_writes + failed_reads + failed_writes invalid_io notify_free - discard zero_pages orig_data_size compr_data_size -- cgit v1.1 From 7db9d9ddd367aa63c90dd88cde271c9e2a9dcebf Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:14 -0700 Subject: zram: add multi stream functionality Existing zram (zcomp) implementation has only one compression stream (buffer and algorithm private part), so in order to prevent data corruption only one write (compress operation) can use this compression stream, forcing all concurrent write operations to wait for stream lock to be released. This patch changes zcomp to keep a compression streams list of user-defined size (via sysfs device attr). Each write operation still exclusively holds compression stream, the difference is that we can have N write operations (depending on size of streams list) executing in parallel. See TEST section later in commit message for performance data. Introduce struct zcomp_strm_multi and a set of functions to manage zcomp_strm stream access. zcomp_strm_multi has a list of idle zcomp_strm structs, spinlock to protect idle list and wait queue, making it possible to perform parallel compressions. The following set of functions added: - zcomp_strm_multi_find()/zcomp_strm_multi_release() find and release a compression stream, implement required locking - zcomp_strm_multi_create()/zcomp_strm_multi_destroy() create and destroy zcomp_strm_multi zcomp ->strm_find() and ->strm_release() callbacks are set during initialisation to zcomp_strm_multi_find()/zcomp_strm_multi_release() correspondingly. Each time zcomp issues a zcomp_strm_multi_find() call, the following set of operations performed: - spin lock strm_lock - if idle list is not empty, remove zcomp_strm from idle list, spin unlock and return zcomp stream pointer to caller - if idle list is empty, current adds itself to wait queue. it will be awaken by zcomp_strm_multi_release() caller. zcomp_strm_multi_release(): - spin lock strm_lock - add zcomp stream to idle list - spin unlock, wake up sleeper Minchan Kim reported that spinlock-based locking scheme has demonstrated a severe perfomance regression for single compression stream case, comparing to mutex-based (see https://lkml.org/lkml/2014/2/18/16) base spinlock mutex ==Initial write ==Initial write ==Initial write records: 5 records: 5 records: 5 avg: 1642424.35 avg: 699610.40 avg: 1655583.71 std: 39890.95(2.43%) std: 232014.19(33.16%) std: 52293.96 max: 1690170.94 max: 1163473.45 max: 1697164.75 min: 1568669.52 min: 573429.88 min: 1553410.23 ==Rewrite ==Rewrite ==Rewrite records: 5 records: 5 records: 5 avg: 1611775.39 avg: 501406.64 avg: 1684419.11 std: 17144.58(1.06%) std: 15354.41(3.06%) std: 18367.42 max: 1641800.95 max: 531356.78 max: 1706445.84 min: 1593515.27 min: 488817.78 min: 1655335.73 When only one compression stream available, mutex with spin on owner tends to perform much better than frequent wait_event()/wake_up(). This is why single stream implemented as a special case with mutex locking. Introduce and document zram device attribute max_comp_streams. This attr shows and stores current zcomp's max number of zcomp streams (max_strm). Extend zcomp's zcomp_create() with `max_strm' parameter. `max_strm' limits the number of zcomp_strm structs in compression backend's idle list (max_comp_streams). max_comp_streams used during initialisation as follows: -- passing to zcomp_create() max_strm equals to 1 will initialise zcomp using single compression stream zcomp_strm_single (mutex-based locking). -- passing to zcomp_create() max_strm greater than 1 will initialise zcomp using multi compression stream zcomp_strm_multi (spinlock-based locking). default max_comp_streams value is 1, meaning that zram with single stream will be initialised. Later patch will introduce configuration knob to change max_comp_streams on already initialised and used zcomp. TEST iozone -t 3 -R -r 16K -s 60M -I +Z test base 1 strm (mutex) 3 strm (spinlock) ----------------------------------------------------------------------- Initial write 589286.78 583518.39 718011.05 Rewrite 604837.97 596776.38 1515125.72 Random write 584120.11 595714.58 1388850.25 Pwrite 535731.17 541117.38 739295.27 Fwrite 1418083.88 1478612.72 1484927.06 Usage example: set max_comp_streams to 4 echo 4 > /sys/block/zram0/max_comp_streams show current max_comp_streams (default value is 1). cat /sys/block/zram0/max_comp_streams Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-block-zram | 9 ++- Documentation/blockdev/zram.txt | 31 ++++++-- drivers/block/zram/zcomp.c | 124 ++++++++++++++++++++++++++++- drivers/block/zram/zcomp.h | 4 +- drivers/block/zram/zram_drv.c | 42 +++++++++- drivers/block/zram/zram_drv.h | 2 +- 6 files changed, 201 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-block-zram b/Documentation/ABI/testing/sysfs-block-zram index d460d12..9fe4c51 100644 --- a/Documentation/ABI/testing/sysfs-block-zram +++ b/Documentation/ABI/testing/sysfs-block-zram @@ -49,7 +49,6 @@ Description: The failed_reads file is read-only and specifies the number of failed reads happened on this device. - What: /sys/block/zram/failed_writes Date: February 2014 Contact: Sergey Senozhatsky @@ -57,6 +56,14 @@ Description: The failed_writes file is read-only and specifies the number of failed writes happened on this device. +What: /sys/block/zram/max_comp_streams +Date: February 2014 +Contact: Sergey Senozhatsky +Description: + The max_comp_streams file is read-write and specifies the + number of backend's zcomp_strm compression streams (number of + concurrent compress operations). + What: /sys/block/zram/notify_free Date: August 2010 Contact: Nitin Gupta diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt index d389fb7..ad29acf 100644 --- a/Documentation/blockdev/zram.txt +++ b/Documentation/blockdev/zram.txt @@ -23,7 +23,28 @@ Following shows a typical sequence of steps for using zram. This creates 4 devices: /dev/zram{0,1,2,3} (num_devices parameter is optional. Default: 1) -2) Set Disksize +2) Set max number of compression streams + Compression backend may use up to max_comp_streams compression streams, + thus allowing up to max_comp_streams concurrent compression operations. + By default, compression backend uses single compression stream. + + Examples: + #show max compression streams number + cat /sys/block/zram0/max_comp_streams + + #set max compression streams number to 3 + echo 3 > /sys/block/zram0/max_comp_streams + +Note: +In order to enable compression backend's multi stream support max_comp_streams +must be initially set to desired concurrency level before ZRAM device +initialisation. Once the device initialised as a single stream compression +backend (max_comp_streams equals to 0) changing the value of max_comp_streams +will not take any effect, because single stream compression backend implemented +as a special case and does not support dynamic max_comp_streams. Only multi +stream backend supports dynamic max_comp_streams adjustment. + +3) Set Disksize Set disk size by writing the value to sysfs node 'disksize'. The value can be either in bytes or you can use mem suffixes. Examples: @@ -40,14 +61,14 @@ There is little point creating a zram of greater than twice the size of memory since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the size of the disk when not in use so a huge zram is wasteful. -3) Activate: +4) Activate: mkswap /dev/zram0 swapon /dev/zram0 mkfs.ext4 /dev/zram1 mount /dev/zram1 /tmp -4) Stats: +5) Stats: Per-device statistics are exported as various nodes under /sys/block/zram/ disksize @@ -62,11 +83,11 @@ size of the disk when not in use so a huge zram is wasteful. compr_data_size mem_used_total -5) Deactivate: +6) Deactivate: swapoff /dev/zram0 umount /dev/zram1 -6) Reset: +7) Reset: Write any positive value to 'reset' sysfs node echo 1 > /sys/block/zram0/reset echo 1 > /sys/block/zram1/reset diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index 72e8071..c06f75f 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -24,6 +24,21 @@ struct zcomp_strm_single { struct zcomp_strm *zstrm; }; +/* + * multi zcomp_strm backend + */ +struct zcomp_strm_multi { + /* protect strm list */ + spinlock_t strm_lock; + /* max possible number of zstrm streams */ + int max_strm; + /* number of available zstrm streams */ + int avail_strm; + /* list of available strms */ + struct list_head idle_strm; + wait_queue_head_t strm_wait; +}; + static struct zcomp_backend *find_backend(const char *compress) { if (strncmp(compress, "lzo", 3) == 0) @@ -62,6 +77,107 @@ static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) return zstrm; } +/* + * get idle zcomp_strm or wait until other process release + * (zcomp_strm_release()) one for us + */ +static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp) +{ + struct zcomp_strm_multi *zs = comp->stream; + struct zcomp_strm *zstrm; + + while (1) { + spin_lock(&zs->strm_lock); + if (!list_empty(&zs->idle_strm)) { + zstrm = list_entry(zs->idle_strm.next, + struct zcomp_strm, list); + list_del(&zstrm->list); + spin_unlock(&zs->strm_lock); + return zstrm; + } + /* zstrm streams limit reached, wait for idle stream */ + if (zs->avail_strm >= zs->max_strm) { + spin_unlock(&zs->strm_lock); + wait_event(zs->strm_wait, !list_empty(&zs->idle_strm)); + continue; + } + /* allocate new zstrm stream */ + zs->avail_strm++; + spin_unlock(&zs->strm_lock); + + zstrm = zcomp_strm_alloc(comp); + if (!zstrm) { + spin_lock(&zs->strm_lock); + zs->avail_strm--; + spin_unlock(&zs->strm_lock); + wait_event(zs->strm_wait, !list_empty(&zs->idle_strm)); + continue; + } + break; + } + return zstrm; +} + +/* add stream back to idle list and wake up waiter or free the stream */ +static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstrm) +{ + struct zcomp_strm_multi *zs = comp->stream; + + spin_lock(&zs->strm_lock); + if (zs->avail_strm <= zs->max_strm) { + list_add(&zstrm->list, &zs->idle_strm); + spin_unlock(&zs->strm_lock); + wake_up(&zs->strm_wait); + return; + } + + zs->avail_strm--; + spin_unlock(&zs->strm_lock); + zcomp_strm_free(comp, zstrm); +} + +static void zcomp_strm_multi_destroy(struct zcomp *comp) +{ + struct zcomp_strm_multi *zs = comp->stream; + struct zcomp_strm *zstrm; + + while (!list_empty(&zs->idle_strm)) { + zstrm = list_entry(zs->idle_strm.next, + struct zcomp_strm, list); + list_del(&zstrm->list); + zcomp_strm_free(comp, zstrm); + } + kfree(zs); +} + +static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm) +{ + struct zcomp_strm *zstrm; + struct zcomp_strm_multi *zs; + + comp->destroy = zcomp_strm_multi_destroy; + comp->strm_find = zcomp_strm_multi_find; + comp->strm_release = zcomp_strm_multi_release; + zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL); + if (!zs) + return -ENOMEM; + + comp->stream = zs; + spin_lock_init(&zs->strm_lock); + INIT_LIST_HEAD(&zs->idle_strm); + init_waitqueue_head(&zs->strm_wait); + zs->max_strm = max_strm; + zs->avail_strm = 1; + + zstrm = zcomp_strm_alloc(comp); + if (!zstrm) { + kfree(zs); + return -ENOMEM; + } + list_add(&zstrm->list, &zs->idle_strm); + return 0; +} + static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp) { struct zcomp_strm_single *zs = comp->stream; @@ -139,7 +255,7 @@ void zcomp_destroy(struct zcomp *comp) * if requested algorithm is not supported or in case * of init error */ -struct zcomp *zcomp_create(const char *compress) +struct zcomp *zcomp_create(const char *compress, int max_strm) { struct zcomp *comp; struct zcomp_backend *backend; @@ -153,7 +269,11 @@ struct zcomp *zcomp_create(const char *compress) return NULL; comp->backend = backend; - if (zcomp_strm_single_create(comp) != 0) { + if (max_strm > 1) + zcomp_strm_multi_create(comp, max_strm); + else + zcomp_strm_single_create(comp); + if (!comp->stream) { kfree(comp); return NULL; } diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index dc3500d..2a36844 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -21,6 +21,8 @@ struct zcomp_strm { * working memory) */ void *private; + /* used in multi stream backend, protected by backend strm_lock */ + struct list_head list; }; /* static compression backend */ @@ -47,7 +49,7 @@ struct zcomp { void (*destroy)(struct zcomp *comp); }; -struct zcomp *zcomp_create(const char *comp); +struct zcomp *zcomp_create(const char *comp, int max_strm); void zcomp_destroy(struct zcomp *comp); struct zcomp_strm *zcomp_strm_find(struct zcomp *comp); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 9678407..d498c82 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -116,6 +116,40 @@ static ssize_t mem_used_total_show(struct device *dev, return sprintf(buf, "%llu\n", val); } +static ssize_t max_comp_streams_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + val = zram->max_comp_streams; + up_read(&zram->init_lock); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t max_comp_streams_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int num; + struct zram *zram = dev_to_zram(dev); + + if (kstrtoint(buf, 0, &num)) + return -EINVAL; + if (num < 1) + return -EINVAL; + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't set max_comp_streams for initialized device\n"); + return -EBUSY; + } + zram->max_comp_streams = num; + up_write(&zram->init_lock); + return len; +} + /* flag operations needs meta->tb_lock */ static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) @@ -513,6 +547,8 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) } zcomp_destroy(zram->comp); + zram->max_comp_streams = 1; + zram_meta_free(zram->meta); zram->meta = NULL; /* Reset stats */ @@ -548,7 +584,7 @@ static ssize_t disksize_store(struct device *dev, goto out_free_meta; } - zram->comp = zcomp_create(default_compressor); + zram->comp = zcomp_create(default_compressor, zram->max_comp_streams); if (!zram->comp) { pr_info("Cannot initialise %s compressing backend\n", default_compressor); @@ -709,6 +745,8 @@ static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL); static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store); static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); +static DEVICE_ATTR(max_comp_streams, S_IRUGO | S_IWUSR, + max_comp_streams_show, max_comp_streams_store); ZRAM_ATTR_RO(num_reads); ZRAM_ATTR_RO(num_writes); @@ -733,6 +771,7 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_orig_data_size.attr, &dev_attr_compr_data_size.attr, &dev_attr_mem_used_total.attr, + &dev_attr_max_comp_streams.attr, NULL, }; @@ -795,6 +834,7 @@ static int create_device(struct zram *zram, int device_id) } zram->meta = NULL; + zram->max_comp_streams = 1; return 0; out_free_disk: diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index c6b910e..9e1d3fd 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -100,7 +100,7 @@ struct zram { * we can store in a disk. */ u64 disksize; /* bytes */ - + int max_comp_streams; struct zram_stats stats; }; #endif -- cgit v1.1 From e6e0680d6346a62f42724e15a193eb5d5a23be0d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:15 -0700 Subject: zram: add set_max_streams knob This patch allows to change max_comp_streams on initialised zcomp. Introduce zcomp set_max_streams() knob, zcomp_strm_multi_set_max_streams() and zcomp_strm_single_set_max_streams() callbacks to change streams limit for zcomp_strm_multi and zcomp_strm_single, accordingly. set_max_streams for single steam zcomp does nothing. If user has lowered the limit, then zcomp_strm_multi_set_max_streams() attempts to immediately free extra streams (as much as it can, depending on idle streams availability). Note, this patch does not allow to change stream 'policy' from single to multi stream (or vice versa) on already initialised compression backend. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zcomp.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/block/zram/zcomp.h | 3 +++ drivers/block/zram/zram_drv.c | 5 ++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index c06f75f..ac276f7 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -136,6 +136,29 @@ static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstr zcomp_strm_free(comp, zstrm); } +/* change max_strm limit */ +static int zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm) +{ + struct zcomp_strm_multi *zs = comp->stream; + struct zcomp_strm *zstrm; + + spin_lock(&zs->strm_lock); + zs->max_strm = num_strm; + /* + * if user has lowered the limit and there are idle streams, + * immediately free as much streams (and memory) as we can. + */ + while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) { + zstrm = list_entry(zs->idle_strm.next, + struct zcomp_strm, list); + list_del(&zstrm->list); + zcomp_strm_free(comp, zstrm); + zs->avail_strm--; + } + spin_unlock(&zs->strm_lock); + return 0; +} + static void zcomp_strm_multi_destroy(struct zcomp *comp) { struct zcomp_strm_multi *zs = comp->stream; @@ -158,6 +181,7 @@ static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm) comp->destroy = zcomp_strm_multi_destroy; comp->strm_find = zcomp_strm_multi_find; comp->strm_release = zcomp_strm_multi_release; + comp->set_max_streams = zcomp_strm_multi_set_max_streams; zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL); if (!zs) return -ENOMEM; @@ -192,6 +216,12 @@ static void zcomp_strm_single_release(struct zcomp *comp, mutex_unlock(&zs->strm_lock); } +static int zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm) +{ + /* zcomp_strm_single support only max_comp_streams == 1 */ + return -ENOTSUPP; +} + static void zcomp_strm_single_destroy(struct zcomp *comp) { struct zcomp_strm_single *zs = comp->stream; @@ -206,6 +236,7 @@ static int zcomp_strm_single_create(struct zcomp *comp) comp->destroy = zcomp_strm_single_destroy; comp->strm_find = zcomp_strm_single_find; comp->strm_release = zcomp_strm_single_release; + comp->set_max_streams = zcomp_strm_single_set_max_streams; zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL); if (!zs) return -ENOMEM; @@ -220,6 +251,11 @@ static int zcomp_strm_single_create(struct zcomp *comp) return 0; } +int zcomp_set_max_streams(struct zcomp *comp, int num_strm) +{ + return comp->set_max_streams(comp, num_strm); +} + struct zcomp_strm *zcomp_strm_find(struct zcomp *comp) { return comp->strm_find(comp); diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index 2a36844..bd11d59 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -46,6 +46,7 @@ struct zcomp { struct zcomp_strm *(*strm_find)(struct zcomp *comp); void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm); + int (*set_max_streams)(struct zcomp *comp, int num_strm); void (*destroy)(struct zcomp *comp); }; @@ -60,4 +61,6 @@ int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, int zcomp_decompress(struct zcomp *comp, const unsigned char *src, size_t src_len, unsigned char *dst); + +int zcomp_set_max_streams(struct zcomp *comp, int num_strm); #endif /* _ZCOMP_H_ */ diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index d498c82..05aaf98 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -141,9 +141,8 @@ static ssize_t max_comp_streams_store(struct device *dev, return -EINVAL; down_write(&zram->init_lock); if (init_done(zram)) { - up_write(&zram->init_lock); - pr_info("Can't set max_comp_streams for initialized device\n"); - return -EBUSY; + if (zcomp_set_max_streams(zram->comp, num)) + pr_info("Cannot change max compression streams\n"); } zram->max_comp_streams = num; up_write(&zram->init_lock); -- cgit v1.1 From 6182c0174a8829fc3bbdf78d9afd5b3927d3691d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:17 -0700 Subject: zram: make compression algorithm selection possible Add and document `comp_algorithm' device attribute. This attribute allows to show supported compression and currently selected compression algorithms: cat /sys/block/zram0/comp_algorithm [lzo] lz4 and change selected compression algorithm: echo lzo > /sys/block/zram0/comp_algorithm Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-block-zram | 8 +++++++ Documentation/blockdev/zram.txt | 24 +++++++++++++++---- drivers/block/zram/zcomp.c | 32 +++++++++++++++++++++++--- drivers/block/zram/zcomp.h | 2 ++ drivers/block/zram/zram_drv.c | 37 +++++++++++++++++++++++++++--- drivers/block/zram/zram_drv.h | 1 + 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-block-zram b/Documentation/ABI/testing/sysfs-block-zram index 9fe4c51..4cba43c 100644 --- a/Documentation/ABI/testing/sysfs-block-zram +++ b/Documentation/ABI/testing/sysfs-block-zram @@ -64,6 +64,14 @@ Description: number of backend's zcomp_strm compression streams (number of concurrent compress operations). +What: /sys/block/zram/comp_algorithm +Date: February 2014 +Contact: Sergey Senozhatsky +Description: + The comp_algorithm file is read-write and lets to show + available and selected compression algorithms, change + compression algorithm selection. + What: /sys/block/zram/notify_free Date: August 2010 Contact: Nitin Gupta diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt index ad29acf..7055bab 100644 --- a/Documentation/blockdev/zram.txt +++ b/Documentation/blockdev/zram.txt @@ -44,7 +44,21 @@ will not take any effect, because single stream compression backend implemented as a special case and does not support dynamic max_comp_streams. Only multi stream backend supports dynamic max_comp_streams adjustment. -3) Set Disksize +3) Select compression algorithm + Using comp_algorithm device attribute one can see available and + currently selected (shown in square brackets) compression algortithms, + change selected compression algorithm (once the device is initialised + there is no way to change compression algorithm). + + Examples: + #show supported compression algorithms + cat /sys/block/zram0/comp_algorithm + lzo [lz4] + + #select lzo compression algorithm + echo lzo > /sys/block/zram0/comp_algorithm + +4) Set Disksize Set disk size by writing the value to sysfs node 'disksize'. The value can be either in bytes or you can use mem suffixes. Examples: @@ -61,14 +75,14 @@ There is little point creating a zram of greater than twice the size of memory since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the size of the disk when not in use so a huge zram is wasteful. -4) Activate: +5) Activate: mkswap /dev/zram0 swapon /dev/zram0 mkfs.ext4 /dev/zram1 mount /dev/zram1 /tmp -5) Stats: +6) Stats: Per-device statistics are exported as various nodes under /sys/block/zram/ disksize @@ -83,11 +97,11 @@ size of the disk when not in use so a huge zram is wasteful. compr_data_size mem_used_total -6) Deactivate: +7) Deactivate: swapoff /dev/zram0 umount /dev/zram1 -7) Reset: +8) Reset: Write any positive value to 'reset' sysfs node echo 1 > /sys/block/zram0/reset echo 1 > /sys/block/zram1/reset diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index ac276f7..aad533a 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -39,11 +39,20 @@ struct zcomp_strm_multi { wait_queue_head_t strm_wait; }; +static struct zcomp_backend *backends[] = { + &zcomp_lzo, + NULL +}; + static struct zcomp_backend *find_backend(const char *compress) { - if (strncmp(compress, "lzo", 3) == 0) - return &zcomp_lzo; - return NULL; + int i = 0; + while (backends[i]) { + if (sysfs_streq(compress, backends[i]->name)) + break; + i++; + } + return backends[i]; } static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) @@ -251,6 +260,23 @@ static int zcomp_strm_single_create(struct zcomp *comp) return 0; } +/* show available compressors */ +ssize_t zcomp_available_show(const char *comp, char *buf) +{ + ssize_t sz = 0; + int i = 0; + + while (backends[i]) { + if (sysfs_streq(comp, backends[i]->name)) + sz += sprintf(buf + sz, "[%s] ", backends[i]->name); + else + sz += sprintf(buf + sz, "%s ", backends[i]->name); + i++; + } + sz += sprintf(buf + sz, "\n"); + return sz; +} + int zcomp_set_max_streams(struct zcomp *comp, int num_strm) { return comp->set_max_streams(comp, num_strm); diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index bd11d59..8b8997f 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -50,6 +50,8 @@ struct zcomp { void (*destroy)(struct zcomp *comp); }; +ssize_t zcomp_available_show(const char *comp, char *buf); + struct zcomp *zcomp_create(const char *comp, int max_strm); void zcomp_destroy(struct zcomp *comp); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 05aaf98..6217e49 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -149,6 +149,34 @@ static ssize_t max_comp_streams_store(struct device *dev, return len; } +static ssize_t comp_algorithm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t sz; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + sz = zcomp_available_show(zram->compressor, buf); + up_read(&zram->init_lock); + + return sz; +} + +static ssize_t comp_algorithm_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't change algorithm for initialized device\n"); + return -EBUSY; + } + strlcpy(zram->compressor, buf, sizeof(zram->compressor)); + up_write(&zram->init_lock); + return len; +} + /* flag operations needs meta->tb_lock */ static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) @@ -583,10 +611,10 @@ static ssize_t disksize_store(struct device *dev, goto out_free_meta; } - zram->comp = zcomp_create(default_compressor, zram->max_comp_streams); + zram->comp = zcomp_create(zram->compressor, zram->max_comp_streams); if (!zram->comp) { pr_info("Cannot initialise %s compressing backend\n", - default_compressor); + zram->compressor); err = -EINVAL; goto out_free_meta; } @@ -746,6 +774,8 @@ static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL); static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL); static DEVICE_ATTR(max_comp_streams, S_IRUGO | S_IWUSR, max_comp_streams_show, max_comp_streams_store); +static DEVICE_ATTR(comp_algorithm, S_IRUGO | S_IWUSR, + comp_algorithm_show, comp_algorithm_store); ZRAM_ATTR_RO(num_reads); ZRAM_ATTR_RO(num_writes); @@ -771,6 +801,7 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_compr_data_size.attr, &dev_attr_mem_used_total.attr, &dev_attr_max_comp_streams.attr, + &dev_attr_comp_algorithm.attr, NULL, }; @@ -831,7 +862,7 @@ static int create_device(struct zram *zram, int device_id) pr_warn("Error creating sysfs group"); goto out_free_disk; } - + strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor)); zram->meta = NULL; zram->max_comp_streams = 1; return 0; diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 9e1d3fd..d3de73f 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -102,5 +102,6 @@ struct zram { u64 disksize; /* bytes */ int max_comp_streams; struct zram_stats stats; + char compressor[10]; }; #endif -- cgit v1.1 From d91c97b975f5af93400ffcab2a553fac03dee7a0 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:18 -0700 Subject: zram: add lz4 algorithm backend Introduce LZ4 compression backend and make it available for selection. LZ4 support is optional and requires user to set ZRAM_LZ4_COMPRESS config option. The default compression backend is LZO. TEST (x86_64, core i5, 2 cores + 2 hyperthreading, zram disk size 1G, ext4 file system, 3 compression streams) iozone -t 3 -R -r 16K -s 60M -I +Z Test LZO LZ4 ---------------------------------------------- Initial write 1642744.62 1317005.09 Rewrite 2498980.88 1800645.16 Read 3957026.38 5877043.75 Re-read 3950997.38 5861847.00 Reverse Read 2937114.56 5047384.00 Stride read 2948163.19 4929587.38 Random read 3292692.69 4880793.62 Mixed workload 1545602.62 3502940.38 Random write 2448039.75 1758786.25 Pwrite 1670051.03 1338329.69 Pread 2530682.00 5097177.62 Fwrite 3232085.62 3275942.56 Fread 6306880.25 6645271.12 So on my system LZ4 is slower in write-only tests, while it performs better in read-only and mixed (reads + writes) tests. Official LZ4 benchmarks available here http://code.google.com/p/lz4/ (linux kernel uses revision r90). Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/Kconfig | 10 +++++++++ drivers/block/zram/Makefile | 2 ++ drivers/block/zram/zcomp.c | 6 ++++++ drivers/block/zram/zcomp_lz4.c | 47 ++++++++++++++++++++++++++++++++++++++++++ drivers/block/zram/zcomp_lz4.h | 17 +++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 drivers/block/zram/zcomp_lz4.c create mode 100644 drivers/block/zram/zcomp_lz4.h diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig index 983314c..0f95557 100644 --- a/drivers/block/zram/Kconfig +++ b/drivers/block/zram/Kconfig @@ -16,6 +16,16 @@ config ZRAM See zram.txt for more information. Project home: +config ZRAM_LZ4_COMPRESS + bool "Enable LZ4 algorithm support" + depends on ZRAM + select LZ4_COMPRESS + select LZ4_DECOMPRESS + default n + help + This option enables LZ4 compression algorithm support. Compression + algorithm can be changed using `comp_algorithm' device attribute. + config ZRAM_DEBUG bool "Compressed RAM block device debug support" depends on ZRAM diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile index 757c6a5..be0763f 100644 --- a/drivers/block/zram/Makefile +++ b/drivers/block/zram/Makefile @@ -1,3 +1,5 @@ zram-y := zcomp_lzo.o zcomp.o zram_drv.o +zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o + obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index aad533a..d591903 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -15,6 +15,9 @@ #include "zcomp.h" #include "zcomp_lzo.h" +#ifdef CONFIG_ZRAM_LZ4_COMPRESS +#include "zcomp_lz4.h" +#endif /* * single zcomp_strm backend @@ -41,6 +44,9 @@ struct zcomp_strm_multi { static struct zcomp_backend *backends[] = { &zcomp_lzo, +#ifdef CONFIG_ZRAM_LZ4_COMPRESS + &zcomp_lz4, +#endif NULL }; diff --git a/drivers/block/zram/zcomp_lz4.c b/drivers/block/zram/zcomp_lz4.c new file mode 100644 index 0000000..f2afb7e --- /dev/null +++ b/drivers/block/zram/zcomp_lz4.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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 "zcomp_lz4.h" + +static void *zcomp_lz4_create(void) +{ + return kzalloc(LZ4_MEM_COMPRESS, GFP_KERNEL); +} + +static void zcomp_lz4_destroy(void *private) +{ + kfree(private); +} + +static int zcomp_lz4_compress(const unsigned char *src, unsigned char *dst, + size_t *dst_len, void *private) +{ + /* return : Success if return 0 */ + return lz4_compress(src, PAGE_SIZE, dst, dst_len, private); +} + +static int zcomp_lz4_decompress(const unsigned char *src, size_t src_len, + unsigned char *dst) +{ + size_t dst_len = PAGE_SIZE; + /* return : Success if return 0 */ + return lz4_decompress_unknownoutputsize(src, src_len, dst, &dst_len); +} + +struct zcomp_backend zcomp_lz4 = { + .compress = zcomp_lz4_compress, + .decompress = zcomp_lz4_decompress, + .create = zcomp_lz4_create, + .destroy = zcomp_lz4_destroy, + .name = "lz4", +}; diff --git a/drivers/block/zram/zcomp_lz4.h b/drivers/block/zram/zcomp_lz4.h new file mode 100644 index 0000000..60613fb --- /dev/null +++ b/drivers/block/zram/zcomp_lz4.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + * + * 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. + */ + +#ifndef _ZCOMP_LZ4_H_ +#define _ZCOMP_LZ4_H_ + +#include "zcomp.h" + +extern struct zcomp_backend zcomp_lz4; + +#endif /* _ZCOMP_LZ4_H_ */ -- cgit v1.1 From 0b609bd6091a0c80a422d704ff585a559caf2beb Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:19 -0700 Subject: zram: move comp allocation out of init_lock While fixing lockdep spew of ->init_lock reported by Sasha Levin [1], Minchan Kim noted [2] that it's better to move compression backend allocation (using GPF_KERNEL) out of the ->init_lock lock, same way as with zram_meta_alloc(), in order to prevent the same lockdep spew. [1] https://lkml.org/lkml/2014/2/27/337 [2] https://lkml.org/lkml/2014/3/3/32 Signed-off-by: Sergey Senozhatsky Reported-by: Minchan Kim Acked-by: Minchan Kim Cc: Sasha Levin Acked-by: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 6217e49..29f09b9 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -591,9 +591,10 @@ static ssize_t disksize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { u64 disksize; + struct zcomp *comp; struct zram_meta *meta; struct zram *zram = dev_to_zram(dev); - int err; + int err = -EINVAL; disksize = memparse(buf, NULL); if (!disksize) @@ -604,30 +605,32 @@ static ssize_t disksize_store(struct device *dev, if (!meta) return -ENOMEM; + comp = zcomp_create(zram->compressor, zram->max_comp_streams); + if (!comp) { + pr_info("Cannot initialise %s compressing backend\n", + zram->compressor); + goto out_cleanup; + } + down_write(&zram->init_lock); if (init_done(zram)) { + up_write(&zram->init_lock); pr_info("Cannot change disksize for initialized device\n"); err = -EBUSY; - goto out_free_meta; - } - - zram->comp = zcomp_create(zram->compressor, zram->max_comp_streams); - if (!zram->comp) { - pr_info("Cannot initialise %s compressing backend\n", - zram->compressor); - err = -EINVAL; - goto out_free_meta; + goto out_cleanup; } zram->meta = meta; + zram->comp = comp; zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); up_write(&zram->init_lock); return len; -out_free_meta: - up_write(&zram->init_lock); +out_cleanup: + if (comp) + zcomp_destroy(comp); zram_meta_free(meta); return err; } -- cgit v1.1 From 4f995e62b6196b96a9fcdd0e1e86612f133a2940 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:20 -0700 Subject: zram: return error-valued pointer from zcomp_create() Instead of returning just NULL, return ERR_PTR from zcomp_create() if compressing backend creation has failed. ERR_PTR(-EINVAL) for unsupported compression algorithm request, ERR_PTR(-ENOMEM) for allocation (zcomp or compression stream) error. Perform IS_ERR() check of returned from zcomp_create() value in disksize_store() and set return code to PTR_ERR(). Change suggested by Jerome Marchand. [akpm@linux-foundation.org: clean up error recovery flow] Signed-off-by: Sergey Senozhatsky Reported-by: Jerome Marchand Cc: Minchan Kim Cc: Nitin Gupta Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Change-Id: I3a6907a5cecbf087facea2251b60df75ec70bb21 --- drivers/block/zram/zcomp.c | 14 ++++++++------ drivers/block/zram/zram_drv.c | 19 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index d591903..5647d8f 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -319,9 +320,10 @@ void zcomp_destroy(struct zcomp *comp) /* * search available compressors for requested algorithm. - * allocate new zcomp and initialize it. return NULL - * if requested algorithm is not supported or in case - * of init error + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in + * case of allocation error. */ struct zcomp *zcomp_create(const char *compress, int max_strm) { @@ -330,11 +332,11 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) backend = find_backend(compress); if (!backend) - return NULL; + return ERR_PTR(-EINVAL); comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); if (!comp) - return NULL; + return ERR_PTR(-ENOMEM); comp->backend = backend; if (max_strm > 1) @@ -343,7 +345,7 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) zcomp_strm_single_create(comp); if (!comp->stream) { kfree(comp); - return NULL; + return ERR_PTR(-ENOMEM); } return comp; } diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 29f09b9..a136e23 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "zram_drv.h" @@ -594,7 +595,7 @@ static ssize_t disksize_store(struct device *dev, struct zcomp *comp; struct zram_meta *meta; struct zram *zram = dev_to_zram(dev); - int err = -EINVAL; + int err; disksize = memparse(buf, NULL); if (!disksize) @@ -606,18 +607,18 @@ static ssize_t disksize_store(struct device *dev, return -ENOMEM; comp = zcomp_create(zram->compressor, zram->max_comp_streams); - if (!comp) { + if (IS_ERR(comp)) { pr_info("Cannot initialise %s compressing backend\n", zram->compressor); - goto out_cleanup; + err = PTR_ERR(comp); + goto out_free_meta; } down_write(&zram->init_lock); if (init_done(zram)) { - up_write(&zram->init_lock); pr_info("Cannot change disksize for initialized device\n"); err = -EBUSY; - goto out_cleanup; + goto out_destroy_comp; } zram->meta = meta; @@ -625,12 +626,12 @@ static ssize_t disksize_store(struct device *dev, zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); up_write(&zram->init_lock); - return len; -out_cleanup: - if (comp) - zcomp_destroy(comp); +out_destroy_comp: + up_write(&zram->init_lock); + zcomp_destroy(comp); +out_free_meta: zram_meta_free(meta); return err; } -- cgit v1.1 From 91328ed10d255697782e22f3c3576b63c1892519 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Mon, 7 Apr 2014 15:38:21 -0700 Subject: zram: propagate error to user When we initialized zcomp with single, we couldn't change max_comp_streams without zram reset but current interface doesn't show any error to user and even it changes max_comp_streams's value without any effect so it would make user very confusing. This patch prevents max_comp_streams's change when zcomp was initialized as single zcomp and emit the error to user(ex, echo). [akpm@linux-foundation.org: don't return with the lock held, per Sergey] [fengguang.wu@intel.com: fix coccinelle warnings] Signed-off-by: Minchan Kim Cc: Nitin Gupta Cc: Jerome Marchand Acked-by: Sergey Senozhatsky Signed-off-by: Fengguang Wu Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/blockdev/zram.txt | 9 +++++---- drivers/block/zram/zcomp.c | 10 +++++----- drivers/block/zram/zcomp.h | 4 ++-- drivers/block/zram/zram_drv.c | 17 +++++++++++++---- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt index 7055bab..986b923 100644 --- a/Documentation/blockdev/zram.txt +++ b/Documentation/blockdev/zram.txt @@ -39,10 +39,11 @@ Note: In order to enable compression backend's multi stream support max_comp_streams must be initially set to desired concurrency level before ZRAM device initialisation. Once the device initialised as a single stream compression -backend (max_comp_streams equals to 0) changing the value of max_comp_streams -will not take any effect, because single stream compression backend implemented -as a special case and does not support dynamic max_comp_streams. Only multi -stream backend supports dynamic max_comp_streams adjustment. +backend (max_comp_streams equals to 1), you will see error if you try to change +the value of max_comp_streams because single stream compression backend +implemented as a special case by lock overhead issue and does not support +dynamic max_comp_streams. Only multi stream backend supports dynamic +max_comp_streams adjustment. 3) Select compression algorithm Using comp_algorithm device attribute one can see available and diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index 5647d8f..b0e7592 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -153,7 +153,7 @@ static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstr } /* change max_strm limit */ -static int zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm) +static bool zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm) { struct zcomp_strm_multi *zs = comp->stream; struct zcomp_strm *zstrm; @@ -172,7 +172,7 @@ static int zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm) zs->avail_strm--; } spin_unlock(&zs->strm_lock); - return 0; + return true; } static void zcomp_strm_multi_destroy(struct zcomp *comp) @@ -232,10 +232,10 @@ static void zcomp_strm_single_release(struct zcomp *comp, mutex_unlock(&zs->strm_lock); } -static int zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm) +static bool zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm) { /* zcomp_strm_single support only max_comp_streams == 1 */ - return -ENOTSUPP; + return false; } static void zcomp_strm_single_destroy(struct zcomp *comp) @@ -284,7 +284,7 @@ ssize_t zcomp_available_show(const char *comp, char *buf) return sz; } -int zcomp_set_max_streams(struct zcomp *comp, int num_strm) +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm) { return comp->set_max_streams(comp, num_strm); } diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index 8b8997f..c59d1fc 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -46,7 +46,7 @@ struct zcomp { struct zcomp_strm *(*strm_find)(struct zcomp *comp); void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm); - int (*set_max_streams)(struct zcomp *comp, int num_strm); + bool (*set_max_streams)(struct zcomp *comp, int num_strm); void (*destroy)(struct zcomp *comp); }; @@ -64,5 +64,5 @@ int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, int zcomp_decompress(struct zcomp *comp, const unsigned char *src, size_t src_len, unsigned char *dst); -int zcomp_set_max_streams(struct zcomp *comp, int num_strm); +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm); #endif /* _ZCOMP_H_ */ diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index a136e23..7e862b9 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -135,19 +135,28 @@ static ssize_t max_comp_streams_store(struct device *dev, { int num; struct zram *zram = dev_to_zram(dev); + int ret; - if (kstrtoint(buf, 0, &num)) - return -EINVAL; + ret = kstrtoint(buf, 0, &num); + if (ret < 0) + return ret; if (num < 1) return -EINVAL; + down_write(&zram->init_lock); if (init_done(zram)) { - if (zcomp_set_max_streams(zram->comp, num)) + if (!zcomp_set_max_streams(zram->comp, num)) { pr_info("Cannot change max compression streams\n"); + ret = -EINVAL; + goto out; + } } + zram->max_comp_streams = num; + ret = len; +out: up_write(&zram->init_lock); - return len; + return ret; } static ssize_t comp_algorithm_show(struct device *dev, -- cgit v1.1 From 35f7bb02007e0de1e3e6fdf3b14ce5f5ccee32db Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 7 Apr 2014 15:38:22 -0700 Subject: zram: use scnprintf() in attrs show() methods sysfs.txt documentation lists the following requirements: - The buffer will always be PAGE_SIZE bytes in length. On i386, this is 4096. - show() methods should return the number of bytes printed into the buffer. This is the return value of scnprintf(). - show() should always use scnprintf(). Use scnprintf() in show() functions. Signed-off-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zcomp.c | 8 +++++--- drivers/block/zram/zram_drv.c | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index b0e7592..f1ff39a 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -275,12 +275,14 @@ ssize_t zcomp_available_show(const char *comp, char *buf) while (backends[i]) { if (sysfs_streq(comp, backends[i]->name)) - sz += sprintf(buf + sz, "[%s] ", backends[i]->name); + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", backends[i]->name); else - sz += sprintf(buf + sz, "%s ", backends[i]->name); + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "%s ", backends[i]->name); i++; } - sz += sprintf(buf + sz, "\n"); + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); return sz; } diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 7e862b9..5ede2ef 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -56,7 +56,7 @@ static ssize_t zram_attr_##name##_show(struct device *d, \ struct device_attribute *attr, char *b) \ { \ struct zram *zram = dev_to_zram(d); \ - return sprintf(b, "%llu\n", \ + return scnprintf(b, PAGE_SIZE, "%llu\n", \ (u64)atomic64_read(&zram->stats.name)); \ } \ static struct device_attribute dev_attr_##name = \ @@ -77,7 +77,7 @@ static ssize_t disksize_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%llu\n", zram->disksize); + return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize); } static ssize_t initstate_show(struct device *dev, @@ -90,7 +90,7 @@ static ssize_t initstate_show(struct device *dev, val = init_done(zram); up_read(&zram->init_lock); - return sprintf(buf, "%u\n", val); + return scnprintf(buf, PAGE_SIZE, "%u\n", val); } static ssize_t orig_data_size_show(struct device *dev, @@ -98,7 +98,7 @@ static ssize_t orig_data_size_show(struct device *dev, { struct zram *zram = dev_to_zram(dev); - return sprintf(buf, "%llu\n", + return scnprintf(buf, PAGE_SIZE, "%llu\n", (u64)(atomic64_read(&zram->stats.pages_stored)) << PAGE_SHIFT); } @@ -114,7 +114,7 @@ static ssize_t mem_used_total_show(struct device *dev, val = zs_get_total_size_bytes(meta->mem_pool); up_read(&zram->init_lock); - return sprintf(buf, "%llu\n", val); + return scnprintf(buf, PAGE_SIZE, "%llu\n", val); } static ssize_t max_comp_streams_show(struct device *dev, @@ -127,7 +127,7 @@ static ssize_t max_comp_streams_show(struct device *dev, val = zram->max_comp_streams; up_read(&zram->init_lock); - return sprintf(buf, "%d\n", val); + return scnprintf(buf, PAGE_SIZE, "%d\n", val); } static ssize_t max_comp_streams_store(struct device *dev, -- cgit v1.1 From e1a61f0e687dbaa5106b2df60daf3c7f52d2c9c8 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Mon, 7 Apr 2014 15:38:24 -0700 Subject: zram: support REQ_DISCARD zram is ram based block device and can be used by backend of filesystem. When filesystem deletes a file, it normally doesn't do anything on data block of that file. It just marks on metadata of that file. This behavior has no problem on disk based block device, but has problems on ram based block device, since we can't free memory used for data block. To overcome this disadvantage, there is REQ_DISCARD functionality. If block device support REQ_DISCARD and filesystem is mounted with discard option, filesystem sends REQ_DISCARD to block device whenever some data blocks are discarded. All we have to do is to handle this request. This patch implements to flag up QUEUE_FLAG_DISCARD and handle this REQ_DISCARD request. With it, we can free memory used by zram if it isn't used. [akpm@linux-foundation.org: tweak comments] Signed-off-by: Joonsoo Kim Cc: Minchan Kim Cc: Nitin Gupta Cc: Sergey Senozhatsky Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Change-Id: I9831047aa7ab4162bf761fdd8897e2d1e7a4b34f --- drivers/block/zram/zram_drv.c | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 5ede2ef..567a684 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -562,6 +562,47 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, return ret; } +/* + * zram_bio_discard - handler on discard request + * @index: physical block index in PAGE_SIZE units + * @offset: byte offset within physical block + */ +static void zram_bio_discard(struct zram *zram, u32 index, + int offset, struct bio *bio) +{ + size_t n = bio->bi_size; + + /* + * zram manages data in physical block size units. Because logical block + * size isn't identical with physical block size on some arch, we + * could get a discard request pointing to a specific offset within a + * certain physical block. Although we can handle this request by + * reading that physiclal block and decompressing and partially zeroing + * and re-compressing and then re-storing it, this isn't reasonable + * because our intent with a discard request is to save memory. So + * skipping this logical block is appropriate here. + */ + if (offset) { + if (n < offset) + return; + + n -= offset; + index++; + } + + while (n >= PAGE_SIZE) { + /* + * Discard request can be large so the lock hold times could be + * lengthy. So take the lock once per page. + */ + write_lock(&zram->meta->tb_lock); + zram_free_page(zram, index); + write_unlock(&zram->meta->tb_lock); + index++; + n -= PAGE_SIZE; + } +} + static void zram_reset_device(struct zram *zram, bool reset_capacity) { size_t index; @@ -695,6 +736,12 @@ static void __zram_make_request(struct zram *zram, struct bio *bio) index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + if (unlikely(bio->bi_rw & REQ_DISCARD)) { + zram_bio_discard(zram, index, offset, bio); + bio_endio(bio, 0); + return; + } + bio_for_each_segment(bvec, bio, i) { int max_transfer_size = PAGE_SIZE - offset; @@ -866,6 +913,21 @@ static int create_device(struct zram *zram, int device_id) ZRAM_LOGICAL_BLOCK_SIZE); blk_queue_io_min(zram->disk->queue, PAGE_SIZE); blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); + zram->disk->queue->limits.discard_granularity = PAGE_SIZE; + zram->disk->queue->limits.max_discard_sectors = UINT_MAX; + /* + * zram_bio_discard() will clear all logical blocks if logical block + * size is identical with physical block size(PAGE_SIZE). But if it is + * different, we will skip discarding some parts of logical blocks in + * the part of the request range which isn't aligned to physical block + * size. So we can't ensure that all discarded logical blocks are + * zeroed. + */ + if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE) + zram->disk->queue->limits.discard_zeroes_data = 1; + else + zram->disk->queue->limits.discard_zeroes_data = 0; + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, zram->disk->queue); add_disk(zram->disk); -- cgit v1.1 From c475acf40485f3cb3206184545f9c52986fbd108 Mon Sep 17 00:00:00 2001 From: Weijie Yang Date: Wed, 4 Jun 2014 16:11:06 -0700 Subject: zram: correct offset usage in zram_bio_discard We want to skip the physical block(PAGE_SIZE) which is partially covered by the discard bio, so we check the remaining size and subtract it if there is a need to goto the next physical block. The current offset usage in zram_bio_discard is incorrect, it will cause its upper filesystem breakdown. Consider the following scenario: On some architecture or config, PAGE_SIZE is 64K for example, filesystem is set up on zram disk without PAGE_SIZE aligned, a discard bio leads to a offset = 4K and size=72K, normally, it should not really discard any physical block as it partially cover two physical blocks. However, with the current offset usage, it will discard the second physical block and free its memory, which will cause filesystem breakdown. This patch corrects the offset usage in zram_bio_discard. Signed-off-by: Weijie Yang Cc: Minchan Kim Cc: Nitin Gupta Acked-by: Joonsoo Kim Cc: Sergey Senozhatsky Cc: Bob Liu Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 567a684..ca7f4e6 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -583,10 +583,10 @@ static void zram_bio_discard(struct zram *zram, u32 index, * skipping this logical block is appropriate here. */ if (offset) { - if (n < offset) + if (n <= (PAGE_SIZE - offset)) return; - n -= offset; + n -= (PAGE_SIZE - offset); index++; } -- cgit v1.1 From 9a9f224a63dbc24a93ef6fcb7e04cba045466583 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 2 Jul 2014 15:22:36 -0700 Subject: zram: revalidate disk after capacity change Alexander reported mkswap on /dev/zram0 is failed if other process is opening the block device file. Step is as follows, 0. Reset the unused zram device. 1. Use a program that opens /dev/zram0 with O_RDWR and sleeps until killed. 2. While that program sleeps, echo the correct value to /sys/block/zram0/disksize. 3. Verify (e.g. in /proc/partitions) that the disk size is applied correctly. It is. 4. While that program still sleeps, attempt to mkswap /dev/zram0. This fails: mkswap: error: swap area needs to be at least 40 KiB When I investigated, the size get by ioctl(fd, BLKGETSIZE64, xxx) on mkswap to get a size of blockdev was zero although zram0 has right size by 2. The reason is zram didn't revalidate disk after changing capacity so that size of blockdev's inode is not uptodate until all of file is close. This patch should fix the BUG. Signed-off-by: Minchan Kim Reported-by: Alexander E. Patrakov Tested-by: Alexander E. Patrakov Reviewed-by: Sergey Senozhatsky Cc: Nitin Gupta Acked-by: Jerome Marchand Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index ca7f4e6..4d59e5f 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -633,8 +633,10 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) memset(&zram->stats, 0, sizeof(zram->stats)); zram->disksize = 0; - if (reset_capacity) + if (reset_capacity) { set_capacity(zram->disk, 0); + revalidate_disk(zram->disk); + } up_write(&zram->init_lock); } @@ -675,6 +677,7 @@ static ssize_t disksize_store(struct device *dev, zram->comp = comp; zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + revalidate_disk(zram->disk); up_write(&zram->init_lock); return len; -- cgit v1.1 From 187a9023926594c43c701ffac31dfe7cd2241594 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 23 Jul 2014 14:00:04 -0700 Subject: zram: avoid lockdep splat by revalidate_disk Sasha reported lockdep warning [1] introduced by [2]. It could be fixed by doing disk revalidation out of the init_lock. It's okay because disk capacity change is protected by init_lock so that revalidate_disk always sees up-to-date value so there is no race. [1] https://lkml.org/lkml/2014/7/3/735 [2] zram: revalidate disk after capacity change Fixes 2e32baea46ce ("zram: revalidate disk after capacity change"). Signed-off-by: Minchan Kim Reported-by: Sasha Levin Cc: "Alexander E. Patrakov" Cc: Nitin Gupta Cc: Jerome Marchand Cc: Sergey Senozhatsky CC: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 4d59e5f..8d1fa24 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -633,11 +633,18 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity) memset(&zram->stats, 0, sizeof(zram->stats)); zram->disksize = 0; - if (reset_capacity) { + if (reset_capacity) set_capacity(zram->disk, 0); - revalidate_disk(zram->disk); - } + up_write(&zram->init_lock); + + /* + * Revalidate disk out of the init_lock to avoid lockdep splat. + * It's okay because disk's capacity is protected by init_lock + * so that revalidate_disk always sees up-to-date capacity. + */ + if (reset_capacity) + revalidate_disk(zram->disk); } static ssize_t disksize_store(struct device *dev, @@ -677,8 +684,15 @@ static ssize_t disksize_store(struct device *dev, zram->comp = comp; zram->disksize = disksize; set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); - revalidate_disk(zram->disk); up_write(&zram->init_lock); + + /* + * Revalidate disk out of the init_lock to avoid lockdep splat. + * It's okay because disk's capacity is protected by init_lock + * so that revalidate_disk always sees up-to-date capacity. + */ + revalidate_disk(zram->disk); + return len; out_destroy_comp: -- cgit v1.1 From 2b13c20b2f2df98fe36f6051de8c47fef2f79fcd Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Wed, 6 Aug 2014 16:08:25 -0700 Subject: zram: rename struct `table' to `zram_table_entry' Andrew Morton has recently noted that `struct table' actually represents table entry and, thus, should be renamed. Rename to `zram_table_entry'. Signed-off-by: Sergey Senozhatsky Cc: Minchan Kim Cc: Nitin Gupta Cc: Weijie Yang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index d3de73f..0be6466 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -63,7 +63,7 @@ enum zram_pageflags { /*-- Data structures */ /* Allocated for each disk page */ -struct table { +struct zram_table_entry { unsigned long handle; u16 size; /* object size (excluding header) */ u8 flags; @@ -83,7 +83,7 @@ struct zram_stats { struct zram_meta { rwlock_t tb_lock; /* protect table */ - struct table *table; + struct zram_table_entry *table; struct zs_pool *mem_pool; }; -- cgit v1.1 From 517fd6744845288003dc2369d2b1a679e6f3c937 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Wed, 6 Aug 2014 16:08:27 -0700 Subject: zram: remove unused SECTOR_SIZE define Drop SECTOR_SIZE define, because it's not used. Signed-off-by: Sergey Senozhatsky Cc: Minchan Kim Cc: Nitin Gupta Cc: Weijie Yang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 0be6466..c875e38 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -44,7 +44,6 @@ static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; /*-- End of configurable params */ #define SECTOR_SHIFT 9 -#define SECTOR_SIZE (1 << SECTOR_SHIFT) #define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) #define ZRAM_LOGICAL_BLOCK_SHIFT 12 -- cgit v1.1 From 8709b6bafc908bf5b4e3a7788e4071397aabfa87 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 6 Aug 2014 16:08:29 -0700 Subject: zram: use size_t instead of u16 Some architectures (eg, hexagon and PowerPC) could use PAGE_SHIFT of 16 or more. In these cases u16 is not sufficiently large to represent a compressed page's size so use size_t. Signed-off-by: Minchan Kim Reported-by: Weijie Yang Acked-by: Sergey Senozhatsky Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 8d1fa24..1e118e5 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -345,7 +345,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) unsigned char *cmem; struct zram_meta *meta = zram->meta; unsigned long handle; - u16 size; + size_t size; read_lock(&meta->tb_lock); handle = meta->table[index].handle; -- cgit v1.1 From 075241c2cd2032101ddf5c6a2b32218f24f69061 Mon Sep 17 00:00:00 2001 From: Weijie Yang Date: Thu, 12 Mar 2015 23:24:56 -0400 Subject: zram: replace global tb_lock with fine grain lock Currently, we use a rwlock tb_lock to protect concurrent access to the whole zram meta table. However, according to the actual access model, there is only a small chance for upper user to access the same table[index], so the current lock granularity is too big. The idea of optimization is to change the lock granularity from whole meta table to per table entry (table -> table[index]), so that we can protect concurrent access to the same table[index], meanwhile allow the maximum concurrency. With this in mind, several kinds of locks which could be used as a per-entry lock were tested and compared: Test environment: x86-64 Intel Core2 Q8400, system memory 4GB, Ubuntu 12.04, kernel v3.15.0-rc3 as base, zram with 4 max_comp_streams LZO. iozone test: iozone -t 4 -R -r 16K -s 200M -I +Z (1GB zram with ext4 filesystem, take the average of 10 tests, KB/s) Test base CAS spinlock rwlock bit_spinlock ------------------------------------------------------------------- Initial write 1381094 1425435 1422860 1423075 1421521 Rewrite 1529479 1641199 1668762 1672855 1654910 Read 8468009 11324979 11305569 11117273 10997202 Re-read 8467476 11260914 11248059 11145336 10906486 Reverse Read 6821393 8106334 8282174 8279195 8109186 Stride read 7191093 8994306 9153982 8961224 9004434 Random read 7156353 8957932 9167098 8980465 8940476 Mixed workload 4172747 5680814 5927825 5489578 5972253 Random write 1483044 1605588 1594329 1600453 1596010 Pwrite 1276644 1303108 1311612 1314228 1300960 Pread 4324337 4632869 4618386 4457870 4500166 To enhance the possibility of access the same table[index] concurrently, set zram a small disksize(10MB) and let threads run with large loop count. fio test: fio --bs=32k --randrepeat=1 --randseed=100 --refill_buffers --scramble_buffers=1 --direct=1 --loops=3000 --numjobs=4 --filename=/dev/zram0 --name=seq-write --rw=write --stonewall --name=seq-read --rw=read --stonewall --name=seq-readwrite --rw=rw --stonewall --name=rand-readwrite --rw=randrw --stonewall (10MB zram raw block device, take the average of 10 tests, KB/s) Test base CAS spinlock rwlock bit_spinlock ------------------------------------------------------------- seq-write 933789 999357 1003298 995961 1001958 seq-read 5634130 6577930 6380861 6243912 6230006 seq-rw 1405687 1638117 1640256 1633903 1634459 rand-rw 1386119 1614664 1617211 1609267 1612471 All the optimization methods show a higher performance than the base, however, it is hard to say which method is the most appropriate. On the other hand, zram is mostly used on small embedded system, so we don't want to increase any memory footprint. This patch pick the bit_spinlock method, pack object size and page_flag into an unsigned long table.value, so as to not increase any memory overhead on both 32-bit and 64-bit system. On the third hand, even though different kinds of locks have different performances, we can ignore this difference, because: if zram is used as zram swapfile, the swap subsystem can prevent concurrent access to the same swapslot; if zram is used as zram-blk for set up filesystem on it, the upper filesystem and the page cache also prevent concurrent access of the same block mostly. So we can ignore the different performances among locks. Change-Id: I85b58fc94a794a85be4713988b218ef6e52f2a27 Acked-by: Sergey Senozhatsky Reviewed-by: Davidlohr Bueso Signed-off-by: Weijie Yang Signed-off-by: Minchan Kim Cc: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 69 ++++++++++++++++++++++++++----------------- drivers/block/zram/zram_drv.h | 24 +++++++++++---- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 1e118e5..859ab91 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -191,19 +191,32 @@ static ssize_t comp_algorithm_store(struct device *dev, static int zram_test_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - return meta->table[index].flags & BIT(flag); + return meta->table[index].value & BIT(flag); } static void zram_set_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - meta->table[index].flags |= BIT(flag); + meta->table[index].value |= BIT(flag); } static void zram_clear_flag(struct zram_meta *meta, u32 index, enum zram_pageflags flag) { - meta->table[index].flags &= ~BIT(flag); + meta->table[index].value &= ~BIT(flag); +} + +static size_t zram_get_obj_size(struct zram_meta *meta, u32 index) +{ + return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1); +} + +static void zram_set_obj_size(struct zram_meta *meta, + u32 index, size_t size) +{ + unsigned long flags = meta->table[index].value >> ZRAM_FLAG_SHIFT; + + meta->table[index].value = (flags << ZRAM_FLAG_SHIFT) | size; } static inline int is_partial_io(struct bio_vec *bvec) @@ -263,7 +276,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) goto free_table; } - rwlock_init(&meta->tb_lock); return meta; free_table: @@ -312,7 +324,12 @@ static void handle_zero_page(struct bio_vec *bvec) flush_dcache_page(page); } -/* NOTE: caller should hold meta->tb_lock with write-side */ + +/* + * To protect concurrent access to the same index entry, + * caller should hold this table index entry's bit_spinlock to + * indicate this index entry is accessing. + */ static void zram_free_page(struct zram *zram, size_t index) { struct zram_meta *meta = zram->meta; @@ -332,11 +349,12 @@ static void zram_free_page(struct zram *zram, size_t index) zs_free(meta->mem_pool, handle); - atomic64_sub(meta->table[index].size, &zram->stats.compr_data_size); + atomic64_sub(zram_get_obj_size(meta, index), + &zram->stats.compr_data_size); atomic64_dec(&zram->stats.pages_stored); meta->table[index].handle = 0; - meta->table[index].size = 0; + zram_set_obj_size(meta, index, 0); } static int zram_decompress_page(struct zram *zram, char *mem, u32 index) @@ -347,12 +365,12 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) unsigned long handle; size_t size; - read_lock(&meta->tb_lock); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); handle = meta->table[index].handle; - size = meta->table[index].size; + size = zram_get_obj_size(meta, index); if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { - read_unlock(&meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); clear_page(mem); return 0; } @@ -363,7 +381,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) else ret = zcomp_decompress(zram->comp, cmem, size, mem); zs_unmap_object(meta->mem_pool, handle); - read_unlock(&meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret)) { @@ -384,14 +402,14 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, struct zram_meta *meta = zram->meta; page = bvec->bv_page; - read_lock(&meta->tb_lock); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); if (unlikely(!meta->table[index].handle) || zram_test_flag(meta, index, ZRAM_ZERO)) { - read_unlock(&meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); handle_zero_page(bvec); return 0; } - read_unlock(&meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); if (is_partial_io(bvec)) /* Use a temporary buffer to decompress the page */ @@ -470,10 +488,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (page_zero_filled(uncmem)) { kunmap_atomic(user_mem); /* Free memory associated with this sector now. */ - write_lock(&zram->meta->tb_lock); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); zram_set_flag(meta, index, ZRAM_ZERO); - write_unlock(&zram->meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); atomic64_inc(&zram->stats.zero_pages); ret = 0; @@ -525,12 +543,12 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, * Free memory associated with this sector * before overwriting unused sectors. */ - write_lock(&zram->meta->tb_lock); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); meta->table[index].handle = handle; - meta->table[index].size = clen; - write_unlock(&zram->meta->tb_lock); + zram_set_obj_size(meta, index, clen); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); /* Update stats */ atomic64_add(clen, &zram->stats.compr_data_size); @@ -571,6 +589,7 @@ static void zram_bio_discard(struct zram *zram, u32 index, int offset, struct bio *bio) { size_t n = bio->bi_size; + struct zram_meta *meta = zram->meta; /* * zram manages data in physical block size units. Because logical block @@ -591,13 +610,9 @@ static void zram_bio_discard(struct zram *zram, u32 index, } while (n >= PAGE_SIZE) { - /* - * Discard request can be large so the lock hold times could be - * lengthy. So take the lock once per page. - */ - write_lock(&zram->meta->tb_lock); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); - write_unlock(&zram->meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); index++; n -= PAGE_SIZE; } @@ -832,9 +847,9 @@ static void zram_slot_free_notify(struct block_device *bdev, zram = bdev->bd_disk->private_data; meta = zram->meta; - write_lock(&meta->tb_lock); + bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); - write_unlock(&meta->tb_lock); + bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); atomic64_inc(&zram->stats.notify_free); } diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index c875e38..10df795 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -51,10 +51,24 @@ static const size_t max_zpage_size = PAGE_SIZE / 10 * 9; #define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) -/* Flags for zram pages (table[page_no].flags) */ + +/* + * The lower ZRAM_FLAG_SHIFT bits of table.value is for + * object size (excluding header), the higher bits is for + * zram_pageflags. + * + * zram is mainly used for memory efficiency so we want to keep memory + * footprint small so we can squeeze size and flags into a field. + * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header), + * the higher bits is for zram_pageflags. + */ +#define ZRAM_FLAG_SHIFT 24 + +/* Flags for zram pages (table[page_no].value) */ enum zram_pageflags { /* Page consists entirely of zeros */ - ZRAM_ZERO, + ZRAM_ZERO = ZRAM_FLAG_SHIFT + 1, + ZRAM_ACCESS, /* page in now accessed */ __NR_ZRAM_PAGEFLAGS, }; @@ -64,9 +78,8 @@ enum zram_pageflags { /* Allocated for each disk page */ struct zram_table_entry { unsigned long handle; - u16 size; /* object size (excluding header) */ - u8 flags; -} __aligned(4); + unsigned long value; +}; struct zram_stats { atomic64_t compr_data_size; /* compressed size of pages stored */ @@ -81,7 +94,6 @@ struct zram_stats { }; struct zram_meta { - rwlock_t tb_lock; /* protect table */ struct zram_table_entry *table; struct zs_pool *mem_pool; }; -- cgit v1.1 From 1f82667f879c07647e9af7d13a6cbe9d482493ef Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Aug 2014 15:18:37 -0700 Subject: zram: fix incorrect stat with failed_reads Since we allocate a temporary buffer in zram_bvec_read to handle partial page operations in commit 924bd88d703e ("Staging: zram: allow partial page operations"), our ->failed_reads value may be incorrect as we do not increase its value when failing to allocate the temporary buffer. Let's fix this issue and correct the annotation of failed_reads. Signed-off-by: Chao Yu Acked-by: Minchan Kim Cc: Nitin Gupta Acked-by: Jerome Marchand Acked-by: Sergey Senozhatsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zram_drv.c | 10 +++++++--- drivers/block/zram/zram_drv.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 859ab91..2bb3f2c 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -386,7 +386,6 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) /* Should NEVER happen. Return bio error if it does. */ if (unlikely(ret)) { pr_err("Decompression failed! err=%d, page=%u\n", ret, index); - atomic64_inc(&zram->stats.failed_reads); return ret; } @@ -558,8 +557,6 @@ out: zcomp_strm_release(zram->comp, zstrm); if (is_partial_io(bvec)) kfree(uncmem); - if (ret) - atomic64_inc(&zram->stats.failed_writes); return ret; } @@ -577,6 +574,13 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, ret = zram_bvec_write(zram, bvec, index, offset); } + if (unlikely(ret)) { + if (rw == READ) + atomic64_inc(&zram->stats.failed_reads); + else + atomic64_inc(&zram->stats.failed_writes); + } + return ret; } diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 10df795..2a4f29c 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -85,7 +85,7 @@ struct zram_stats { atomic64_t compr_data_size; /* compressed size of pages stored */ atomic64_t num_reads; /* failed + successful */ atomic64_t num_writes; /* --do-- */ - atomic64_t failed_reads; /* should NEVER! happen */ + atomic64_t failed_reads; /* can happen when memory is too low */ atomic64_t failed_writes; /* can happen when memory is too low */ atomic64_t invalid_io; /* non-page-aligned I/O requests */ atomic64_t notify_free; /* no. of swap slot free notifications */ -- cgit v1.1 From 3948a0f192249f3cb564a1b5010b4089cc685bee Mon Sep 17 00:00:00 2001 From: Kyungsik Lee Date: Mon, 8 Jul 2013 16:01:45 -0700 Subject: decompressor: add LZ4 decompressor module Add support for LZ4 decompression in the Linux Kernel. LZ4 Decompression APIs for kernel are based on LZ4 implementation by Yann Collet. Benchmark Results(PATCH v3) Compiler: Linaro ARM gcc 4.6.2 1. ARMv7, 1.5GHz based board Kernel: linux 3.4 Uncompressed Kernel Size: 14MB Compressed Size Decompression Speed LZO 6.7MB 20.1MB/s, 25.2MB/s(UA) LZ4 7.3MB 29.1MB/s, 45.6MB/s(UA) 2. ARMv7, 1.7GHz based board Kernel: linux 3.7 Uncompressed Kernel Size: 14MB Compressed Size Decompression Speed LZO 6.0MB 34.1MB/s, 52.2MB/s(UA) LZ4 6.5MB 86.7MB/s - UA: Unaligned memory Access support - Latest patch set for LZO applied This patch set is for adding support for LZ4-compressed Kernel. LZ4 is a very fast lossless compression algorithm and it also features an extremely fast decoder [1]. But we have five of decompressors already and one question which does arise, however, is that of where do we stop adding new ones? This issue had been discussed and came to the conclusion [2]. Russell King said that we should have: - one decompressor which is the fastest - one decompressor for the highest compression ratio - one popular decompressor (eg conventional gzip) If we have a replacement one for one of these, then it should do exactly that: replace it. The benchmark shows that an 8% increase in image size vs a 66% increase in decompression speed compared to LZO(which has been known as the fastest decompressor in the Kernel). Therefore the "fast but may not be small" compression title has clearly been taken by LZ4 [3]. [1] http://code.google.com/p/lz4/ [2] http://thread.gmane.org/gmane.linux.kbuild.devel/9157 [3] http://thread.gmane.org/gmane.linux.kbuild.devel/9347 LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html LZ4 source repository: http://code.google.com/p/lz4/ Signed-off-by: Kyungsik Lee Signed-off-by: Yann Collet Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Russell King Cc: Borislav Petkov Cc: Florian Fainelli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/lz4.h | 51 ++++++++ lib/lz4/lz4_decompress.c | 326 +++++++++++++++++++++++++++++++++++++++++++++++ lib/lz4/lz4defs.h | 94 ++++++++++++++ 3 files changed, 471 insertions(+) create mode 100644 include/linux/lz4.h create mode 100644 lib/lz4/lz4_decompress.c create mode 100644 lib/lz4/lz4defs.h diff --git a/include/linux/lz4.h b/include/linux/lz4.h new file mode 100644 index 0000000..7f6c75a --- /dev/null +++ b/include/linux/lz4.h @@ -0,0 +1,51 @@ +#ifndef __LZ4_H__ +#define __LZ4_H__ +/* + * LZ4 Kernel Interface + * + * Copyright (C) 2013, LG Electronics, Kyungsik Lee + * + * 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. + */ + +/* + * lz4_compressbound() + * Provides the maximum size that LZ4 may output in a "worst case" scenario + * (input data not compressible) + */ +static inline size_t lz4_compressbound(size_t isize) +{ + return isize + (isize / 255) + 16; +} + +/* + * lz4_decompress() + * src : source address of the compressed data + * src_len : is the input size, whcih is returned after decompress done + * dest : output buffer address of the decompressed data + * actual_dest_len: is the size of uncompressed data, supposing it's known + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer must be already allocated. + * slightly faster than lz4_decompress_unknownoutputsize() + */ +int lz4_decompress(const char *src, size_t *src_len, char *dest, + size_t actual_dest_len); + +/* + * lz4_decompress_unknownoutputsize() + * src : source address of the compressed data + * src_len : is the input size, therefore the compressed size + * dest : output buffer address of the decompressed data + * dest_len: is the max size of the destination buffer, which is + * returned with actual size of decompressed data after + * decompress done + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer must be already allocated. + */ +int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, + char *dest, size_t *dest_len); +#endif diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c new file mode 100644 index 0000000..dcc8975 --- /dev/null +++ b/lib/lz4/lz4_decompress.c @@ -0,0 +1,326 @@ +/* + * LZ4 Decompressor for Linux kernel + * + * Copyright (C) 2013 LG Electronics Co., Ltd. (http://www.lge.com/) + * + * Based on LZ4 implementation by Yann Collet. + * + * LZ4 - Fast LZ compression algorithm + * Copyright (C) 2011-2012, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + */ + +#ifndef STATIC +#include +#include +#endif +#include + +#include + +#include "lz4defs.h" + +static int lz4_uncompress(const char *source, char *dest, int osize) +{ + const BYTE *ip = (const BYTE *) source; + const BYTE *ref; + BYTE *op = (BYTE *) dest; + BYTE * const oend = op + osize; + BYTE *cpy; + unsigned token; + size_t length; + size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0}; +#if LZ4_ARCH64 + size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3}; +#endif + + while (1) { + + /* get runlength */ + token = *ip++; + length = (token >> ML_BITS); + if (length == RUN_MASK) { + size_t len; + + len = *ip++; + for (; len == 255; length += 255) + len = *ip++; + length += len; + } + + /* copy literals */ + cpy = op + length; + if (unlikely(cpy > oend - COPYLENGTH)) { + /* + * Error: not enough place for another match + * (min 4) + 5 literals + */ + if (cpy != oend) + goto _output_error; + + memcpy(op, ip, length); + ip += length; + break; /* EOF */ + } + LZ4_WILDCOPY(ip, op, cpy); + ip -= (op - cpy); + op = cpy; + + /* get offset */ + LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip); + ip += 2; + + /* Error: offset create reference outside destination buffer */ + if (unlikely(ref < (BYTE *const) dest)) + goto _output_error; + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + for (; *ip == 255; length += 255) + ip++; + length += *ip++; + } + + /* copy repeated sequence */ + if (unlikely((op - ref) < STEPSIZE)) { +#if LZ4_ARCH64 + size_t dec64 = dec64table[op - ref]; +#else + const int dec64 = 0; +#endif + op[0] = ref[0]; + op[1] = ref[1]; + op[2] = ref[2]; + op[3] = ref[3]; + op += 4; + ref += 4; + ref -= dec32table[op-ref]; + PUT4(ref, op); + op += STEPSIZE - 4; + ref -= dec64; + } else { + LZ4_COPYSTEP(ref, op); + } + cpy = op + length - (STEPSIZE - 4); + if (cpy > (oend - COPYLENGTH)) { + + /* Error: request to write beyond destination buffer */ + if (cpy > oend) + goto _output_error; + LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); + while (op < cpy) + *op++ = *ref++; + op = cpy; + /* + * Check EOF (should never happen, since last 5 bytes + * are supposed to be literals) + */ + if (op == oend) + goto _output_error; + continue; + } + LZ4_SECURECOPY(ref, op, cpy); + op = cpy; /* correction */ + } + /* end of decoding */ + return (int) (((char *)ip) - source); + + /* write overflow error detected */ +_output_error: + return (int) (-(((char *)ip) - source)); +} + +static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, + int isize, size_t maxoutputsize) +{ + const BYTE *ip = (const BYTE *) source; + const BYTE *const iend = ip + isize; + const BYTE *ref; + + + BYTE *op = (BYTE *) dest; + BYTE * const oend = op + maxoutputsize; + BYTE *cpy; + + size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0}; +#if LZ4_ARCH64 + size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3}; +#endif + + /* Main Loop */ + while (ip < iend) { + + unsigned token; + size_t length; + + /* get runlength */ + token = *ip++; + length = (token >> ML_BITS); + if (length == RUN_MASK) { + int s = 255; + while ((ip < iend) && (s == 255)) { + s = *ip++; + length += s; + } + } + /* copy literals */ + cpy = op + length; + if ((cpy > oend - COPYLENGTH) || + (ip + length > iend - COPYLENGTH)) { + + if (cpy > oend) + goto _output_error;/* writes beyond buffer */ + + if (ip + length != iend) + goto _output_error;/* + * Error: LZ4 format requires + * to consume all input + * at this stage + */ + memcpy(op, ip, length); + op += length; + break;/* Necessarily EOF, due to parsing restrictions */ + } + LZ4_WILDCOPY(ip, op, cpy); + ip -= (op - cpy); + op = cpy; + + /* get offset */ + LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip); + ip += 2; + if (ref < (BYTE * const) dest) + goto _output_error; + /* + * Error : offset creates reference + * outside of destination buffer + */ + + /* get matchlength */ + length = (token & ML_MASK); + if (length == ML_MASK) { + while (ip < iend) { + int s = *ip++; + length += s; + if (s == 255) + continue; + break; + } + } + + /* copy repeated sequence */ + if (unlikely((op - ref) < STEPSIZE)) { +#if LZ4_ARCH64 + size_t dec64 = dec64table[op - ref]; +#else + const int dec64 = 0; +#endif + op[0] = ref[0]; + op[1] = ref[1]; + op[2] = ref[2]; + op[3] = ref[3]; + op += 4; + ref += 4; + ref -= dec32table[op - ref]; + PUT4(ref, op); + op += STEPSIZE - 4; + ref -= dec64; + } else { + LZ4_COPYSTEP(ref, op); + } + cpy = op + length - (STEPSIZE-4); + if (cpy > oend - COPYLENGTH) { + if (cpy > oend) + goto _output_error; /* write outside of buf */ + + LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); + while (op < cpy) + *op++ = *ref++; + op = cpy; + /* + * Check EOF (should never happen, since last 5 bytes + * are supposed to be literals) + */ + if (op == oend) + goto _output_error; + continue; + } + LZ4_SECURECOPY(ref, op, cpy); + op = cpy; /* correction */ + } + /* end of decoding */ + return (int) (((char *) op) - dest); + + /* write overflow error detected */ +_output_error: + return (int) (-(((char *) ip) - source)); +} + +int lz4_decompress(const char *src, size_t *src_len, char *dest, + size_t actual_dest_len) +{ + int ret = -1; + int input_len = 0; + + input_len = lz4_uncompress(src, dest, actual_dest_len); + if (input_len < 0) + goto exit_0; + *src_len = input_len; + + return 0; +exit_0: + return ret; +} +#ifndef STATIC +EXPORT_SYMBOL_GPL(lz4_decompress); +#endif + +int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, + char *dest, size_t *dest_len) +{ + int ret = -1; + int out_len = 0; + + out_len = lz4_uncompress_unknownoutputsize(src, dest, src_len, + *dest_len); + if (out_len < 0) + goto exit_0; + *dest_len = out_len; + + return 0; +exit_0: + return ret; +} +#ifndef STATIC +EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4 Decompressor"); +#endif diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h new file mode 100644 index 0000000..43ac31d --- /dev/null +++ b/lib/lz4/lz4defs.h @@ -0,0 +1,94 @@ +/* + * lz4defs.h -- architecture specific defines + * + * Copyright (C) 2013, LG Electronics, Kyungsik Lee + * + * 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. + */ + +/* + * Detects 64 bits mode + */ +#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) \ + || defined(__ppc64__) || defined(__LP64__)) +#define LZ4_ARCH64 1 +#else +#define LZ4_ARCH64 0 +#endif + +/* + * Architecture-specific macros + */ +#define BYTE u8 +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) \ + || defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6 \ + && defined(ARM_EFFICIENT_UNALIGNED_ACCESS) +typedef struct _U32_S { u32 v; } U32_S; +typedef struct _U64_S { u64 v; } U64_S; + +#define A32(x) (((U32_S *)(x))->v) +#define A64(x) (((U64_S *)(x))->v) + +#define PUT4(s, d) (A32(d) = A32(s)) +#define PUT8(s, d) (A64(d) = A64(s)) +#else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + +#define PUT4(s, d) \ + put_unaligned(get_unaligned((const u32 *) s), (u32 *) d) +#define PUT8(s, d) \ + put_unaligned(get_unaligned((const u64 *) s), (u64 *) d) +#endif + +#define COPYLENGTH 8 +#define ML_BITS 4 +#define ML_MASK ((1U << ML_BITS) - 1) +#define RUN_BITS (8 - ML_BITS) +#define RUN_MASK ((1U << RUN_BITS) - 1) + +#if LZ4_ARCH64/* 64-bit */ +#define STEPSIZE 8 + +#define LZ4_COPYSTEP(s, d) \ + do { \ + PUT8(s, d); \ + d += 8; \ + s += 8; \ + } while (0) + +#define LZ4_COPYPACKET(s, d) LZ4_COPYSTEP(s, d) + +#define LZ4_SECURECOPY(s, d, e) \ + do { \ + if (d < e) { \ + LZ4_WILDCOPY(s, d, e); \ + } \ + } while (0) + +#else /* 32-bit */ +#define STEPSIZE 4 + +#define LZ4_COPYSTEP(s, d) \ + do { \ + PUT4(s, d); \ + d += 4; \ + s += 4; \ + } while (0) + +#define LZ4_COPYPACKET(s, d) \ + do { \ + LZ4_COPYSTEP(s, d); \ + LZ4_COPYSTEP(s, d); \ + } while (0) + +#define LZ4_SECURECOPY LZ4_WILDCOPY +#endif + +#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \ + (d = s - get_unaligned_le16(p)) + +#define LZ4_WILDCOPY(s, d, e) \ + do { \ + LZ4_COPYPACKET(s, d); \ + } while (d < e) -- cgit v1.1 From ae59e751939d817c5dea1f07981d4b19c30c3a04 Mon Sep 17 00:00:00 2001 From: Kyungsik Lee Date: Mon, 8 Jul 2013 16:01:46 -0700 Subject: lib: add support for LZ4-compressed kernel Add support for extracting LZ4-compressed kernel images, as well as LZ4-compressed ramdisk images in the kernel boot process. Signed-off-by: Kyungsik Lee Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Russell King Cc: Borislav Petkov Cc: Florian Fainelli Cc: Yann Collet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: scripts/Makefile.lib Change-Id: I2ad2607d9edf0f41c7e7a621f1da72174b142e2d --- include/linux/decompress/unlz4.h | 10 +++ init/Kconfig | 17 +++- lib/Kconfig | 7 ++ lib/Makefile | 2 + lib/decompress.c | 5 ++ lib/decompress_unlz4.c | 187 +++++++++++++++++++++++++++++++++++++++ lib/lz4/Makefile | 1 + lib/lz4/lz4_decompress.c | 2 +- scripts/Makefile.lib | 5 ++ usr/Kconfig | 9 ++ 10 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 include/linux/decompress/unlz4.h create mode 100644 lib/decompress_unlz4.c create mode 100644 lib/lz4/Makefile diff --git a/include/linux/decompress/unlz4.h b/include/linux/decompress/unlz4.h new file mode 100644 index 0000000..d5b68bf --- /dev/null +++ b/include/linux/decompress/unlz4.h @@ -0,0 +1,10 @@ +#ifndef DECOMPRESS_UNLZ4_H +#define DECOMPRESS_UNLZ4_H + +int unlz4(unsigned char *inbuf, int len, + int(*fill)(void*, unsigned int), + int(*flush)(void*, unsigned int), + unsigned char *output, + int *pos, + void(*error)(char *x)); +#endif diff --git a/init/Kconfig b/init/Kconfig index ae99e63..ac45380 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -130,10 +130,13 @@ config HAVE_KERNEL_XZ config HAVE_KERNEL_LZO bool +config HAVE_KERNEL_LZ4 + bool + choice prompt "Kernel compression mode" default KERNEL_GZIP - depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO + depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO || HAVE_KERNEL_LZ4 help The linux kernel is a kind of self-extracting executable. Several compression algorithms are available, which differ @@ -201,6 +204,18 @@ config KERNEL_LZO size is about 10% bigger than gzip; however its speed (both compression and decompression) is the fastest. +config KERNEL_LZ4 + bool "LZ4" + depends on HAVE_KERNEL_LZ4 + help + LZ4 is an LZ77-type compressor with a fixed, byte-oriented encoding. + A preliminary version of LZ4 de/compression tool is available at + . + + Its compression ratio is worse than LZO. The size of the kernel + is about 8% bigger than LZO. But the decompression speed is + faster than LZO. + endchoice config DEFAULT_HOSTNAME diff --git a/lib/Kconfig b/lib/Kconfig index 830181c..20f4147 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -99,6 +99,9 @@ config LZO_COMPRESS config LZO_DECOMPRESS tristate +config LZ4_DECOMPRESS + tristate + source "lib/xz/Kconfig" # @@ -123,6 +126,10 @@ config DECOMPRESS_LZO select LZO_DECOMPRESS tristate +config DECOMPRESS_LZ4 + select LZ4_DECOMPRESS + tristate + # # Generic allocator support is selected if needed # diff --git a/lib/Makefile b/lib/Makefile index 578414a..22ffafd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ obj-$(CONFIG_BCH) += bch.o obj-$(CONFIG_LZO_COMPRESS) += lzo/ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ +obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-$(CONFIG_XZ_DEC) += xz/ obj-$(CONFIG_RAID6_PQ) += raid6/ @@ -77,6 +78,7 @@ lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o +lib-$(CONFIG_DECOMPRESS_LZ4) += decompress_unlz4.o obj-$(CONFIG_TEXTSEARCH) += textsearch.o obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o diff --git a/lib/decompress.c b/lib/decompress.c index 3d766b7..fc3f2dd 100644 --- a/lib/decompress.c +++ b/lib/decompress.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,9 @@ #ifndef CONFIG_DECOMPRESS_LZO # define unlzo NULL #endif +#ifndef CONFIG_DECOMPRESS_LZ4 +# define unlz4 NULL +#endif static const struct compress_format { unsigned char magic[2]; @@ -42,6 +46,7 @@ static const struct compress_format { { {0x5d, 0x00}, "lzma", unlzma }, { {0xfd, 0x37}, "xz", unxz }, { {0x89, 0x4c}, "lzo", unlzo }, + { {0x02, 0x21}, "lz4", unlz4 }, { {0, 0}, NULL, NULL } }; diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c new file mode 100644 index 0000000..3e67cfa --- /dev/null +++ b/lib/decompress_unlz4.c @@ -0,0 +1,187 @@ +/* + * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd + * + * Copyright (C) 2013, LG Electronics, Kyungsik Lee + * + * 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. + */ + +#ifdef STATIC +#define PREBOOT +#include "lz4/lz4_decompress.c" +#else +#include +#endif +#include +#include +#include +#include + +#include + +/* + * Note: Uncompressed chunk size is used in the compressor side + * (userspace side for compression). + * It is hardcoded because there is not proper way to extract it + * from the binary stream which is generated by the preliminary + * version of LZ4 tool so far. + */ +#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20) +#define ARCHIVE_MAGICNUMBER 0x184C2102 + +STATIC inline int INIT unlz4(u8 *input, int in_len, + int (*fill) (void *, unsigned int), + int (*flush) (void *, unsigned int), + u8 *output, int *posp, + void (*error) (char *x)) +{ + int ret = -1; + size_t chunksize = 0; + size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE; + u8 *inp; + u8 *inp_start; + u8 *outp; + int size = in_len; +#ifdef PREBOOT + size_t out_len = get_unaligned_le32(input + in_len); +#endif + size_t dest_len; + + + if (output) { + outp = output; + } else if (!flush) { + error("NULL output pointer and no flush function provided"); + goto exit_0; + } else { + outp = large_malloc(uncomp_chunksize); + if (!outp) { + error("Could not allocate output buffer"); + goto exit_0; + } + } + + if (input && fill) { + error("Both input pointer and fill function provided,"); + goto exit_1; + } else if (input) { + inp = input; + } else if (!fill) { + error("NULL input pointer and missing fill function"); + goto exit_1; + } else { + inp = large_malloc(lz4_compressbound(uncomp_chunksize)); + if (!inp) { + error("Could not allocate input buffer"); + goto exit_1; + } + } + inp_start = inp; + + if (posp) + *posp = 0; + + if (fill) + fill(inp, 4); + + chunksize = get_unaligned_le32(inp); + if (chunksize == ARCHIVE_MAGICNUMBER) { + inp += 4; + size -= 4; + } else { + error("invalid header"); + goto exit_2; + } + + if (posp) + *posp += 4; + + for (;;) { + + if (fill) + fill(inp, 4); + + chunksize = get_unaligned_le32(inp); + if (chunksize == ARCHIVE_MAGICNUMBER) { + inp += 4; + size -= 4; + if (posp) + *posp += 4; + continue; + } + inp += 4; + size -= 4; + + if (posp) + *posp += 4; + + if (fill) { + if (chunksize > lz4_compressbound(uncomp_chunksize)) { + error("chunk length is longer than allocated"); + goto exit_2; + } + fill(inp, chunksize); + } +#ifdef PREBOOT + if (out_len >= uncomp_chunksize) { + dest_len = uncomp_chunksize; + out_len -= dest_len; + } else + dest_len = out_len; + ret = lz4_decompress(inp, &chunksize, outp, dest_len); +#else + dest_len = uncomp_chunksize; + ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp, + &dest_len); +#endif + if (ret < 0) { + error("Decoding failed"); + goto exit_2; + } + + if (flush && flush(outp, dest_len) != dest_len) + goto exit_2; + if (output) + outp += dest_len; + if (posp) + *posp += chunksize; + + size -= chunksize; + + if (size == 0) + break; + else if (size < 0) { + error("data corrupted"); + goto exit_2; + } + + inp += chunksize; + if (fill) + inp = inp_start; + } + + ret = 0; +exit_2: + if (!input) + large_free(inp_start); +exit_1: + if (!output) + large_free(outp); +exit_0: + return ret; +} + +#ifdef PREBOOT +STATIC int INIT decompress(unsigned char *buf, int in_len, + int(*fill)(void*, unsigned int), + int(*flush)(void*, unsigned int), + unsigned char *output, + int *posp, + void(*error)(char *x) + ) +{ + return unlz4(buf, in_len - 4, fill, flush, output, posp, error); +} +#endif diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile new file mode 100644 index 0000000..7f548c6 --- /dev/null +++ b/lib/lz4/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index dcc8975..d3414ea 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -1,7 +1,7 @@ /* * LZ4 Decompressor for Linux kernel * - * Copyright (C) 2013 LG Electronics Co., Ltd. (http://www.lge.com/) + * Copyright (C) 2013, LG Electronics, Kyungsik Lee * * Based on LZ4 implementation by Yann Collet. * diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 93b2b59..7eb36fd 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -262,6 +262,11 @@ cmd_lzo = (cat $(filter-out FORCE,$^) | \ lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ (rm -f $@ ; false) +quiet_cmd_lz4 = LZ4 $@ +cmd_lz4 = (cat $(filter-out FORCE,$^) | \ + lz4c -l -c1 stdin stdout && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ + (rm -f $@ ; false) + # XZ # --------------------------------------------------------------------------- # Use xzkern to compress the kernel image and xzmisc to compress other things. diff --git a/usr/Kconfig b/usr/Kconfig index 65b845b..16ffe99 100644 --- a/usr/Kconfig +++ b/usr/Kconfig @@ -90,6 +90,15 @@ config RD_LZO Support loading of a LZO encoded initial ramdisk or cpio buffer If unsure, say N. +config RD_LZ4 + bool "Support initial ramdisks compressed using LZ4" if EXPERT + default !EXPERT + depends on BLK_DEV_INITRD + select DECOMPRESS_LZ4 + help + Support loading of a LZ4 encoded initial ramdisk or cpio buffer + If unsure, say N. + choice prompt "Built-in initramfs compression mode" if INITRAMFS_SOURCE!="" help -- cgit v1.1 From 42ebaa6de16ebbef4a9e240b1726633f053b8e38 Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Mon, 8 Jul 2013 16:01:49 -0700 Subject: lib: add lz4 compressor module This patchset is for supporting LZ4 compression and the crypto API using it. As shown below, the size of data is a little bit bigger but compressing speed is faster under the enabled unaligned memory access. We can use lz4 de/compression through crypto API as well. Also, It will be useful for another potential user of lz4 compression. lz4 Compression Benchmark: Compiler: ARM gcc 4.6.4 ARMv7, 1 GHz based board Kernel: linux 3.4 Uncompressed data Size: 101 MB Compressed Size compression Speed LZO 72.1MB 32.1MB/s, 33.0MB/s(UA) LZ4 75.1MB 30.4MB/s, 35.9MB/s(UA) LZ4HC 59.8MB 2.4MB/s, 2.5MB/s(UA) - UA: Unaligned memory Access support - Latest patch set for LZO applied This patch: Add support for LZ4 compression in the Linux Kernel. LZ4 Compression APIs for kernel are based on LZ4 implementation by Yann Collet and were changed for kernel coding style. LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html LZ4 source repository : http://code.google.com/p/lz4/ svn revision : r90 Two APIs are added: lz4_compress() support basic lz4 compression whereas lz4hc_compress() support high compression or CPU performance get lower but compression ratio get higher. Also, we require the pre-allocated working memory with the defined size and destination buffer must be allocated with the size of lz4_compressbound. [akpm@linux-foundation.org: make lz4_compresshcctx() static] Signed-off-by: Chanho Min Cc: "Darrick J. Wong" Cc: Bob Pearson Cc: Richard Weinberger Cc: Herbert Xu Cc: Yann Collet Cc: Kyungsik Lee Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/lz4.h | 36 ++++ lib/Kconfig | 6 + lib/Makefile | 2 + lib/lz4/Makefile | 2 + lib/lz4/lz4_compress.c | 443 ++++++++++++++++++++++++++++++++++++++ lib/lz4/lz4defs.h | 66 +++++- lib/lz4/lz4hc_compress.c | 539 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1092 insertions(+), 2 deletions(-) create mode 100644 lib/lz4/lz4_compress.c create mode 100644 lib/lz4/lz4hc_compress.c diff --git a/include/linux/lz4.h b/include/linux/lz4.h index 7f6c75a..d21c13f 100644 --- a/include/linux/lz4.h +++ b/include/linux/lz4.h @@ -9,6 +9,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define LZ4_MEM_COMPRESS (4096 * sizeof(unsigned char *)) +#define LZ4HC_MEM_COMPRESS (65538 * sizeof(unsigned char *)) /* * lz4_compressbound() @@ -21,6 +23,40 @@ static inline size_t lz4_compressbound(size_t isize) } /* + * lz4_compress() + * src : source address of the original data + * src_len : size of the original data + * dst : output buffer address of the compressed data + * This requires 'dst' of size LZ4_COMPRESSBOUND. + * dst_len : is the output size, which is returned after compress done + * workmem : address of the working memory. + * This requires 'workmem' of size LZ4_MEM_COMPRESS. + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer and workmem must be already allocated with + * the defined size. + */ +int lz4_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem); + + /* + * lz4hc_compress() + * src : source address of the original data + * src_len : size of the original data + * dst : output buffer address of the compressed data + * This requires 'dst' of size LZ4_COMPRESSBOUND. + * dst_len : is the output size, which is returned after compress done + * workmem : address of the working memory. + * This requires 'workmem' of size LZ4HC_MEM_COMPRESS. + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer and workmem must be already allocated with + * the defined size. + */ +int lz4hc_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem); + +/* * lz4_decompress() * src : source address of the compressed data * src_len : is the input size, whcih is returned after decompress done diff --git a/lib/Kconfig b/lib/Kconfig index 20f4147..e07b9a0 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -99,6 +99,12 @@ config LZO_COMPRESS config LZO_DECOMPRESS tristate +config LZ4_COMPRESS + tristate + +config LZ4HC_COMPRESS + tristate + config LZ4_DECOMPRESS tristate diff --git a/lib/Makefile b/lib/Makefile index 22ffafd..a2b9ff8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -69,6 +69,8 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ obj-$(CONFIG_BCH) += bch.o obj-$(CONFIG_LZO_COMPRESS) += lzo/ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ +obj-$(CONFIG_LZ4_COMPRESS) += lz4/ +obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/ obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-$(CONFIG_XZ_DEC) += xz/ obj-$(CONFIG_RAID6_PQ) += raid6/ diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile index 7f548c6..8085d04 100644 --- a/lib/lz4/Makefile +++ b/lib/lz4/Makefile @@ -1 +1,3 @@ +obj-$(CONFIG_LZ4_COMPRESS) += lz4_compress.o +obj-$(CONFIG_LZ4HC_COMPRESS) += lz4hc_compress.o obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c new file mode 100644 index 0000000..fd94058 --- /dev/null +++ b/lib/lz4/lz4_compress.c @@ -0,0 +1,443 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Copyright (C) 2011-2012, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + * + * Changed for kernel use by: + * Chanho Min + */ + +#include +#include +#include +#include +#include "lz4defs.h" + +/* + * LZ4_compressCtx : + * ----------------- + * Compress 'isize' bytes from 'source' into an output buffer 'dest' of + * maximum size 'maxOutputSize'. * If it cannot achieve it, compression + * will stop, and result of the function will be zero. + * return : the number of bytes written in buffer 'dest', or 0 if the + * compression fails + */ +static inline int lz4_compressctx(void *ctx, + const char *source, + char *dest, + int isize, + int maxoutputsize) +{ + HTYPE *hashtable = (HTYPE *)ctx; + const u8 *ip = (u8 *)source; +#if LZ4_ARCH64 + const BYTE * const base = ip; +#else + const int base = 0; +#endif + const u8 *anchor = ip; + const u8 *const iend = ip + isize; + const u8 *const mflimit = iend - MFLIMIT; + #define MATCHLIMIT (iend - LASTLITERALS) + + u8 *op = (u8 *) dest; + u8 *const oend = op + maxoutputsize; + int length; + const int skipstrength = SKIPSTRENGTH; + u32 forwardh; + int lastrun; + + /* Init */ + if (isize < MINLENGTH) + goto _last_literals; + + memset((void *)hashtable, 0, LZ4_MEM_COMPRESS); + + /* First Byte */ + hashtable[LZ4_HASH_VALUE(ip)] = ip - base; + ip++; + forwardh = LZ4_HASH_VALUE(ip); + + /* Main Loop */ + for (;;) { + int findmatchattempts = (1U << skipstrength) + 3; + const u8 *forwardip = ip; + const u8 *ref; + u8 *token; + + /* Find a match */ + do { + u32 h = forwardh; + int step = findmatchattempts++ >> skipstrength; + ip = forwardip; + forwardip = ip + step; + + if (unlikely(forwardip > mflimit)) + goto _last_literals; + + forwardh = LZ4_HASH_VALUE(forwardip); + ref = base + hashtable[h]; + hashtable[h] = ip - base; + } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip))); + + /* Catch up */ + while ((ip > anchor) && (ref > (u8 *)source) && + unlikely(ip[-1] == ref[-1])) { + ip--; + ref--; + } + + /* Encode Literal length */ + length = (int)(ip - anchor); + token = op++; + /* check output limit */ + if (unlikely(op + length + (2 + 1 + LASTLITERALS) + + (length >> 8) > oend)) + return 0; + + if (length >= (int)RUN_MASK) { + int len; + *token = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254 ; len -= 255) + *op++ = 255; + *op++ = (u8)len; + } else + *token = (length << ML_BITS); + + /* Copy Literals */ + LZ4_BLINDCOPY(anchor, op, length); +_next_match: + /* Encode Offset */ + LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref)); + + /* Start Counting */ + ip += MINMATCH; + /* MinMatch verified */ + ref += MINMATCH; + anchor = ip; + while (likely(ip < MATCHLIMIT - (STEPSIZE - 1))) { + #if LZ4_ARCH64 + u64 diff = A64(ref) ^ A64(ip); + #else + u32 diff = A32(ref) ^ A32(ip); + #endif + if (!diff) { + ip += STEPSIZE; + ref += STEPSIZE; + continue; + } + ip += LZ4_NBCOMMONBYTES(diff); + goto _endcount; + } + #if LZ4_ARCH64 + if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) { + ip += 4; + ref += 4; + } + #endif + if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) { + ip += 2; + ref += 2; + } + if ((ip < MATCHLIMIT) && (*ref == *ip)) + ip++; +_endcount: + /* Encode MatchLength */ + length = (int)(ip - anchor); + /* Check output limit */ + if (unlikely(op + (1 + LASTLITERALS) + (length >> 8) > oend)) + return 0; + if (length >= (int)ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for (; length > 509 ; length -= 510) { + *op++ = 255; + *op++ = 255; + } + if (length > 254) { + length -= 255; + *op++ = 255; + } + *op++ = (u8)length; + } else + *token += length; + + /* Test end of chunk */ + if (ip > mflimit) { + anchor = ip; + break; + } + + /* Fill table */ + hashtable[LZ4_HASH_VALUE(ip-2)] = ip - 2 - base; + + /* Test next position */ + ref = base + hashtable[LZ4_HASH_VALUE(ip)]; + hashtable[LZ4_HASH_VALUE(ip)] = ip - base; + if ((ref > ip - (MAX_DISTANCE + 1)) && (A32(ref) == A32(ip))) { + token = op++; + *token = 0; + goto _next_match; + } + + /* Prepare next loop */ + anchor = ip++; + forwardh = LZ4_HASH_VALUE(ip); + } + +_last_literals: + /* Encode Last Literals */ + lastrun = (int)(iend - anchor); + if (((char *)op - dest) + lastrun + 1 + + ((lastrun + 255 - RUN_MASK) / 255) > (u32)maxoutputsize) + return 0; + + if (lastrun >= (int)RUN_MASK) { + *op++ = (RUN_MASK << ML_BITS); + lastrun -= RUN_MASK; + for (; lastrun > 254 ; lastrun -= 255) + *op++ = 255; + *op++ = (u8)lastrun; + } else + *op++ = (lastrun << ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend - anchor; + + /* End */ + return (int)(((char *)op) - dest); +} + +static inline int lz4_compress64kctx(void *ctx, + const char *source, + char *dest, + int isize, + int maxoutputsize) +{ + u16 *hashtable = (u16 *)ctx; + const u8 *ip = (u8 *) source; + const u8 *anchor = ip; + const u8 *const base = ip; + const u8 *const iend = ip + isize; + const u8 *const mflimit = iend - MFLIMIT; + #define MATCHLIMIT (iend - LASTLITERALS) + + u8 *op = (u8 *) dest; + u8 *const oend = op + maxoutputsize; + int len, length; + const int skipstrength = SKIPSTRENGTH; + u32 forwardh; + int lastrun; + + /* Init */ + if (isize < MINLENGTH) + goto _last_literals; + + memset((void *)hashtable, 0, LZ4_MEM_COMPRESS); + + /* First Byte */ + ip++; + forwardh = LZ4_HASH64K_VALUE(ip); + + /* Main Loop */ + for (;;) { + int findmatchattempts = (1U << skipstrength) + 3; + const u8 *forwardip = ip; + const u8 *ref; + u8 *token; + + /* Find a match */ + do { + u32 h = forwardh; + int step = findmatchattempts++ >> skipstrength; + ip = forwardip; + forwardip = ip + step; + + if (forwardip > mflimit) + goto _last_literals; + + forwardh = LZ4_HASH64K_VALUE(forwardip); + ref = base + hashtable[h]; + hashtable[h] = (u16)(ip - base); + } while (A32(ref) != A32(ip)); + + /* Catch up */ + while ((ip > anchor) && (ref > (u8 *)source) + && (ip[-1] == ref[-1])) { + ip--; + ref--; + } + + /* Encode Literal length */ + length = (int)(ip - anchor); + token = op++; + /* Check output limit */ + if (unlikely(op + length + (2 + 1 + LASTLITERALS) + + (length >> 8) > oend)) + return 0; + if (length >= (int)RUN_MASK) { + *token = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254 ; len -= 255) + *op++ = 255; + *op++ = (u8)len; + } else + *token = (length << ML_BITS); + + /* Copy Literals */ + LZ4_BLINDCOPY(anchor, op, length); + +_next_match: + /* Encode Offset */ + LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref)); + + /* Start Counting */ + ip += MINMATCH; + /* MinMatch verified */ + ref += MINMATCH; + anchor = ip; + + while (ip < MATCHLIMIT - (STEPSIZE - 1)) { + #if LZ4_ARCH64 + u64 diff = A64(ref) ^ A64(ip); + #else + u32 diff = A32(ref) ^ A32(ip); + #endif + + if (!diff) { + ip += STEPSIZE; + ref += STEPSIZE; + continue; + } + ip += LZ4_NBCOMMONBYTES(diff); + goto _endcount; + } + #if LZ4_ARCH64 + if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) { + ip += 4; + ref += 4; + } + #endif + if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) { + ip += 2; + ref += 2; + } + if ((ip < MATCHLIMIT) && (*ref == *ip)) + ip++; +_endcount: + + /* Encode MatchLength */ + len = (int)(ip - anchor); + /* Check output limit */ + if (unlikely(op + (1 + LASTLITERALS) + (len >> 8) > oend)) + return 0; + if (len >= (int)ML_MASK) { + *token += ML_MASK; + len -= ML_MASK; + for (; len > 509 ; len -= 510) { + *op++ = 255; + *op++ = 255; + } + if (len > 254) { + len -= 255; + *op++ = 255; + } + *op++ = (u8)len; + } else + *token += len; + + /* Test end of chunk */ + if (ip > mflimit) { + anchor = ip; + break; + } + + /* Fill table */ + hashtable[LZ4_HASH64K_VALUE(ip-2)] = (u16)(ip - 2 - base); + + /* Test next position */ + ref = base + hashtable[LZ4_HASH64K_VALUE(ip)]; + hashtable[LZ4_HASH64K_VALUE(ip)] = (u16)(ip - base); + if (A32(ref) == A32(ip)) { + token = op++; + *token = 0; + goto _next_match; + } + + /* Prepare next loop */ + anchor = ip++; + forwardh = LZ4_HASH64K_VALUE(ip); + } + +_last_literals: + /* Encode Last Literals */ + lastrun = (int)(iend - anchor); + if (op + lastrun + 1 + (lastrun - RUN_MASK + 255) / 255 > oend) + return 0; + if (lastrun >= (int)RUN_MASK) { + *op++ = (RUN_MASK << ML_BITS); + lastrun -= RUN_MASK; + for (; lastrun > 254 ; lastrun -= 255) + *op++ = 255; + *op++ = (u8)lastrun; + } else + *op++ = (lastrun << ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend - anchor; + /* End */ + return (int)(((char *)op) - dest); +} + +int lz4_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem) +{ + int ret = -1; + int out_len = 0; + + if (src_len < LZ4_64KLIMIT) + out_len = lz4_compress64kctx(wrkmem, src, dst, src_len, + lz4_compressbound(src_len)); + else + out_len = lz4_compressctx(wrkmem, src, dst, src_len, + lz4_compressbound(src_len)); + + if (out_len < 0) + goto exit; + + *dst_len = out_len; + + return 0; +exit: + return ret; +} +EXPORT_SYMBOL_GPL(lz4_compress); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4 compressor"); diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h index 43ac31d..abcecdc 100644 --- a/lib/lz4/lz4defs.h +++ b/lib/lz4/lz4defs.h @@ -22,23 +22,40 @@ * Architecture-specific macros */ #define BYTE u8 +typedef struct _U16_S { u16 v; } U16_S; +typedef struct _U32_S { u32 v; } U32_S; +typedef struct _U64_S { u64 v; } U64_S; #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) \ || defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6 \ && defined(ARM_EFFICIENT_UNALIGNED_ACCESS) -typedef struct _U32_S { u32 v; } U32_S; -typedef struct _U64_S { u64 v; } U64_S; +#define A16(x) (((U16_S *)(x))->v) #define A32(x) (((U32_S *)(x))->v) #define A64(x) (((U64_S *)(x))->v) #define PUT4(s, d) (A32(d) = A32(s)) #define PUT8(s, d) (A64(d) = A64(s)) +#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \ + do { \ + A16(p) = v; \ + p += 2; \ + } while (0) #else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */ +#define A64(x) get_unaligned((u64 *)&(((U16_S *)(x))->v)) +#define A32(x) get_unaligned((u32 *)&(((U16_S *)(x))->v)) +#define A16(x) get_unaligned((u16 *)&(((U16_S *)(x))->v)) + #define PUT4(s, d) \ put_unaligned(get_unaligned((const u32 *) s), (u32 *) d) #define PUT8(s, d) \ put_unaligned(get_unaligned((const u64 *) s), (u64 *) d) + +#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \ + do { \ + put_unaligned(v, (u16 *)(p)); \ + p += 2; \ + } while (0) #endif #define COPYLENGTH 8 @@ -46,6 +63,29 @@ typedef struct _U64_S { u64 v; } U64_S; #define ML_MASK ((1U << ML_BITS) - 1) #define RUN_BITS (8 - ML_BITS) #define RUN_MASK ((1U << RUN_BITS) - 1) +#define MEMORY_USAGE 14 +#define MINMATCH 4 +#define SKIPSTRENGTH 6 +#define LASTLITERALS 5 +#define MFLIMIT (COPYLENGTH + MINMATCH) +#define MINLENGTH (MFLIMIT + 1) +#define MAXD_LOG 16 +#define MAXD (1 << MAXD_LOG) +#define MAXD_MASK (u32)(MAXD - 1) +#define MAX_DISTANCE (MAXD - 1) +#define HASH_LOG (MAXD_LOG - 1) +#define HASHTABLESIZE (1 << HASH_LOG) +#define MAX_NB_ATTEMPTS 256 +#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) +#define LZ4_64KLIMIT ((1<<16) + (MFLIMIT - 1)) +#define HASHLOG64K ((MEMORY_USAGE - 2) + 1) +#define HASH64KTABLESIZE (1U << HASHLOG64K) +#define LZ4_HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \ + ((MINMATCH * 8) - (MEMORY_USAGE-2))) +#define LZ4_HASH64K_VALUE(p) (((A32(p)) * 2654435761U) >> \ + ((MINMATCH * 8) - HASHLOG64K)) +#define HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \ + ((MINMATCH * 8) - HASH_LOG)) #if LZ4_ARCH64/* 64-bit */ #define STEPSIZE 8 @@ -65,6 +105,13 @@ typedef struct _U64_S { u64 v; } U64_S; LZ4_WILDCOPY(s, d, e); \ } \ } while (0) +#define HTYPE u32 + +#ifdef __BIG_ENDIAN +#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3) +#else +#define LZ4_NBCOMMONBYTES(val) (__builtin_ctzll(val) >> 3) +#endif #else /* 32-bit */ #define STEPSIZE 4 @@ -83,6 +130,14 @@ typedef struct _U64_S { u64 v; } U64_S; } while (0) #define LZ4_SECURECOPY LZ4_WILDCOPY +#define HTYPE const u8* + +#ifdef __BIG_ENDIAN +#define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3) +#else +#define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3) +#endif + #endif #define LZ4_READ_LITTLEENDIAN_16(d, s, p) \ @@ -92,3 +147,10 @@ typedef struct _U64_S { u64 v; } U64_S; do { \ LZ4_COPYPACKET(s, d); \ } while (d < e) + +#define LZ4_BLINDCOPY(s, d, l) \ + do { \ + u8 *e = (d) + l; \ + LZ4_WILDCOPY(s, d, e); \ + d = e; \ + } while (0) diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c new file mode 100644 index 0000000..eb1a74f --- /dev/null +++ b/lib/lz4/lz4hc_compress.c @@ -0,0 +1,539 @@ +/* + * LZ4 HC - High Compression Mode of LZ4 + * Copyright (C) 2011-2012, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + * + * Changed for kernel use by: + * Chanho Min + */ + +#include +#include +#include +#include +#include "lz4defs.h" + +struct lz4hc_data { + const u8 *base; + HTYPE hashtable[HASHTABLESIZE]; + u16 chaintable[MAXD]; + const u8 *nexttoupdate; +} __attribute__((__packed__)); + +static inline int lz4hc_init(struct lz4hc_data *hc4, const u8 *base) +{ + memset((void *)hc4->hashtable, 0, sizeof(hc4->hashtable)); + memset(hc4->chaintable, 0xFF, sizeof(hc4->chaintable)); + +#if LZ4_ARCH64 + hc4->nexttoupdate = base + 1; +#else + hc4->nexttoupdate = base; +#endif + hc4->base = base; + return 1; +} + +/* Update chains up to ip (excluded) */ +static inline void lz4hc_insert(struct lz4hc_data *hc4, const u8 *ip) +{ + u16 *chaintable = hc4->chaintable; + HTYPE *hashtable = hc4->hashtable; +#if LZ4_ARCH64 + const BYTE * const base = hc4->base; +#else + const int base = 0; +#endif + + while (hc4->nexttoupdate < ip) { + const u8 *p = hc4->nexttoupdate; + size_t delta = p - (hashtable[HASH_VALUE(p)] + base); + if (delta > MAX_DISTANCE) + delta = MAX_DISTANCE; + chaintable[(size_t)(p) & MAXD_MASK] = (u16)delta; + hashtable[HASH_VALUE(p)] = (p) - base; + hc4->nexttoupdate++; + } +} + +static inline size_t lz4hc_commonlength(const u8 *p1, const u8 *p2, + const u8 *const matchlimit) +{ + const u8 *p1t = p1; + + while (p1t < matchlimit - (STEPSIZE - 1)) { +#if LZ4_ARCH64 + u64 diff = A64(p2) ^ A64(p1t); +#else + u32 diff = A32(p2) ^ A32(p1t); +#endif + if (!diff) { + p1t += STEPSIZE; + p2 += STEPSIZE; + continue; + } + p1t += LZ4_NBCOMMONBYTES(diff); + return p1t - p1; + } +#if LZ4_ARCH64 + if ((p1t < (matchlimit-3)) && (A32(p2) == A32(p1t))) { + p1t += 4; + p2 += 4; + } +#endif + + if ((p1t < (matchlimit - 1)) && (A16(p2) == A16(p1t))) { + p1t += 2; + p2 += 2; + } + if ((p1t < matchlimit) && (*p2 == *p1t)) + p1t++; + return p1t - p1; +} + +static inline int lz4hc_insertandfindbestmatch(struct lz4hc_data *hc4, + const u8 *ip, const u8 *const matchlimit, const u8 **matchpos) +{ + u16 *const chaintable = hc4->chaintable; + HTYPE *const hashtable = hc4->hashtable; + const u8 *ref; +#if LZ4_ARCH64 + const BYTE * const base = hc4->base; +#else + const int base = 0; +#endif + int nbattempts = MAX_NB_ATTEMPTS; + size_t repl = 0, ml = 0; + u16 delta; + + /* HC4 match finder */ + lz4hc_insert(hc4, ip); + ref = hashtable[HASH_VALUE(ip)] + base; + + /* potential repetition */ + if (ref >= ip-4) { + /* confirmed */ + if (A32(ref) == A32(ip)) { + delta = (u16)(ip-ref); + repl = ml = lz4hc_commonlength(ip + MINMATCH, + ref + MINMATCH, matchlimit) + MINMATCH; + *matchpos = ref; + } + ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK]; + } + + while ((ref >= ip - MAX_DISTANCE) && nbattempts) { + nbattempts--; + if (*(ref + ml) == *(ip + ml)) { + if (A32(ref) == A32(ip)) { + size_t mlt = + lz4hc_commonlength(ip + MINMATCH, + ref + MINMATCH, matchlimit) + MINMATCH; + if (mlt > ml) { + ml = mlt; + *matchpos = ref; + } + } + } + ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK]; + } + + /* Complete table */ + if (repl) { + const BYTE *ptr = ip; + const BYTE *end; + end = ip + repl - (MINMATCH-1); + /* Pre-Load */ + while (ptr < end - delta) { + chaintable[(size_t)(ptr) & MAXD_MASK] = delta; + ptr++; + } + do { + chaintable[(size_t)(ptr) & MAXD_MASK] = delta; + /* Head of chain */ + hashtable[HASH_VALUE(ptr)] = (ptr) - base; + ptr++; + } while (ptr < end); + hc4->nexttoupdate = end; + } + + return (int)ml; +} + +static inline int lz4hc_insertandgetwidermatch(struct lz4hc_data *hc4, + const u8 *ip, const u8 *startlimit, const u8 *matchlimit, int longest, + const u8 **matchpos, const u8 **startpos) +{ + u16 *const chaintable = hc4->chaintable; + HTYPE *const hashtable = hc4->hashtable; +#if LZ4_ARCH64 + const BYTE * const base = hc4->base; +#else + const int base = 0; +#endif + const u8 *ref; + int nbattempts = MAX_NB_ATTEMPTS; + int delta = (int)(ip - startlimit); + + /* First Match */ + lz4hc_insert(hc4, ip); + ref = hashtable[HASH_VALUE(ip)] + base; + + while ((ref >= ip - MAX_DISTANCE) && (ref >= hc4->base) + && (nbattempts)) { + nbattempts--; + if (*(startlimit + longest) == *(ref - delta + longest)) { + if (A32(ref) == A32(ip)) { + const u8 *reft = ref + MINMATCH; + const u8 *ipt = ip + MINMATCH; + const u8 *startt = ip; + + while (ipt < matchlimit-(STEPSIZE - 1)) { + #if LZ4_ARCH64 + u64 diff = A64(reft) ^ A64(ipt); + #else + u32 diff = A32(reft) ^ A32(ipt); + #endif + + if (!diff) { + ipt += STEPSIZE; + reft += STEPSIZE; + continue; + } + ipt += LZ4_NBCOMMONBYTES(diff); + goto _endcount; + } + #if LZ4_ARCH64 + if ((ipt < (matchlimit - 3)) + && (A32(reft) == A32(ipt))) { + ipt += 4; + reft += 4; + } + ipt += 2; + #endif + if ((ipt < (matchlimit - 1)) + && (A16(reft) == A16(ipt))) { + reft += 2; + } + if ((ipt < matchlimit) && (*reft == *ipt)) + ipt++; +_endcount: + reft = ref; + + while ((startt > startlimit) + && (reft > hc4->base) + && (startt[-1] == reft[-1])) { + startt--; + reft--; + } + + if ((ipt - startt) > longest) { + longest = (int)(ipt - startt); + *matchpos = reft; + *startpos = startt; + } + } + } + ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK]; + } + return longest; +} + +static inline int lz4_encodesequence(const u8 **ip, u8 **op, const u8 **anchor, + int ml, const u8 *ref) +{ + int length, len; + u8 *token; + + /* Encode Literal length */ + length = (int)(*ip - *anchor); + token = (*op)++; + if (length >= (int)RUN_MASK) { + *token = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254 ; len -= 255) + *(*op)++ = 255; + *(*op)++ = (u8)len; + } else + *token = (length << ML_BITS); + + /* Copy Literals */ + LZ4_BLINDCOPY(*anchor, *op, length); + + /* Encode Offset */ + LZ4_WRITE_LITTLEENDIAN_16(*op, (u16)(*ip - ref)); + + /* Encode MatchLength */ + len = (int)(ml - MINMATCH); + if (len >= (int)ML_MASK) { + *token += ML_MASK; + len -= ML_MASK; + for (; len > 509 ; len -= 510) { + *(*op)++ = 255; + *(*op)++ = 255; + } + if (len > 254) { + len -= 255; + *(*op)++ = 255; + } + *(*op)++ = (u8)len; + } else + *token += len; + + /* Prepare next loop */ + *ip += ml; + *anchor = *ip; + + return 0; +} + +static int lz4_compresshcctx(struct lz4hc_data *ctx, + const char *source, + char *dest, + int isize) +{ + const u8 *ip = (const u8 *)source; + const u8 *anchor = ip; + const u8 *const iend = ip + isize; + const u8 *const mflimit = iend - MFLIMIT; + const u8 *const matchlimit = (iend - LASTLITERALS); + + u8 *op = (u8 *)dest; + + int ml, ml2, ml3, ml0; + const u8 *ref = NULL; + const u8 *start2 = NULL; + const u8 *ref2 = NULL; + const u8 *start3 = NULL; + const u8 *ref3 = NULL; + const u8 *start0; + const u8 *ref0; + int lastrun; + + ip++; + + /* Main Loop */ + while (ip < mflimit) { + ml = lz4hc_insertandfindbestmatch(ctx, ip, matchlimit, (&ref)); + if (!ml) { + ip++; + continue; + } + + /* saved, in case we would skip too much */ + start0 = ip; + ref0 = ref; + ml0 = ml; +_search2: + if (ip+ml < mflimit) + ml2 = lz4hc_insertandgetwidermatch(ctx, ip + ml - 2, + ip + 1, matchlimit, ml, &ref2, &start2); + else + ml2 = ml; + /* No better match */ + if (ml2 == ml) { + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + continue; + } + + if (start0 < ip) { + /* empirical */ + if (start2 < ip + ml0) { + ip = start0; + ref = ref0; + ml = ml0; + } + } + /* + * Here, start0==ip + * First Match too small : removed + */ + if ((start2 - ip) < 3) { + ml = ml2; + ip = start2; + ref = ref2; + goto _search2; + } + +_search3: + /* + * Currently we have : + * ml2 > ml1, and + * ip1+3 <= ip2 (usually < ip1+ml1) + */ + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + int new_ml = ml; + if (new_ml > OPTIMAL_ML) + new_ml = OPTIMAL_ML; + if (ip + new_ml > start2 + ml2 - MINMATCH) + new_ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + /* + * Now, we have start2 = ip+new_ml, + * with new_ml=min(ml, OPTIMAL_ML=18) + */ + if (start2 + ml2 < mflimit) + ml3 = lz4hc_insertandgetwidermatch(ctx, + start2 + ml2 - 3, start2, matchlimit, + ml2, &ref3, &start3); + else + ml3 = ml2; + + /* No better match : 2 sequences to encode */ + if (ml3 == ml2) { + /* ip & ref are known; Now for ml */ + if (start2 < ip+ml) + ml = (int)(start2 - ip); + + /* Now, encode 2 sequences */ + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + ip = start2; + lz4_encodesequence(&ip, &op, &anchor, ml2, ref2); + continue; + } + + /* Not enough space for match 2 : remove it */ + if (start3 < ip + ml + 3) { + /* + * can write Seq1 immediately ==> Seq2 is removed, + * so Seq3 becomes Seq1 + */ + if (start3 >= (ip + ml)) { + if (start2 < ip + ml) { + int correction = + (int)(ip + ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + ip = start3; + ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _search3; + } + + /* + * OK, now we have 3 ascending matches; let's write at least + * the first one ip & ref are known; Now for ml + */ + if (start2 < ip + ml) { + if ((start2 - ip) < (int)ML_MASK) { + int correction; + if (ml > OPTIMAL_ML) + ml = OPTIMAL_ML; + if (ip + ml > start2 + ml2 - MINMATCH) + ml = (int)(start2 - ip) + ml2 + - MINMATCH; + correction = ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } else + ml = (int)(start2 - ip); + } + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + + ip = start2; + ref = ref2; + ml = ml2; + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + + goto _search3; + } + + /* Encode Last Literals */ + lastrun = (int)(iend - anchor); + if (lastrun >= (int)RUN_MASK) { + *op++ = (RUN_MASK << ML_BITS); + lastrun -= RUN_MASK; + for (; lastrun > 254 ; lastrun -= 255) + *op++ = 255; + *op++ = (u8) lastrun; + } else + *op++ = (lastrun << ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend - anchor; + /* End */ + return (int) (((char *)op) - dest); +} + +int lz4hc_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem) +{ + int ret = -1; + int out_len = 0; + + struct lz4hc_data *hc4 = (struct lz4hc_data *)wrkmem; + lz4hc_init(hc4, (const u8 *)src); + out_len = lz4_compresshcctx((struct lz4hc_data *)hc4, (const u8 *)src, + (char *)dst, (int)src_len); + + if (out_len < 0) + goto exit; + + *dst_len = out_len; + return 0; + +exit: + return ret; +} +EXPORT_SYMBOL_GPL(lz4hc_compress); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4HC compressor"); -- cgit v1.1 From fcf0112a91940273afaa2f4caad57b0d9b4d2997 Mon Sep 17 00:00:00 2001 From: Richard Laager Date: Thu, 22 Aug 2013 16:35:47 -0700 Subject: lib/lz4: correct the LZ4 license The LZ4 code is listed as using the "BSD 2-Clause License". Signed-off-by: Richard Laager Acked-by: Kyungsik Lee Cc: Chanho Min Cc: Richard Yao Signed-off-by: Andrew Morton [ The 2-clause BSD can be just converted into GPL, but that's rude and pointless, so don't do it - Linus ] Signed-off-by: Linus Torvalds --- lib/lz4/lz4_compress.c | 4 ++-- lib/lz4/lz4_decompress.c | 6 +++--- lib/lz4/lz4hc_compress.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c index fd94058..28321d8 100644 --- a/lib/lz4/lz4_compress.c +++ b/lib/lz4/lz4_compress.c @@ -437,7 +437,7 @@ int lz4_compress(const unsigned char *src, size_t src_len, exit: return ret; } -EXPORT_SYMBOL_GPL(lz4_compress); +EXPORT_SYMBOL(lz4_compress); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4 compressor"); diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index d3414ea..411be80 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -299,7 +299,7 @@ exit_0: return ret; } #ifndef STATIC -EXPORT_SYMBOL_GPL(lz4_decompress); +EXPORT_SYMBOL(lz4_decompress); #endif int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, @@ -319,8 +319,8 @@ exit_0: return ret; } #ifndef STATIC -EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize); +EXPORT_SYMBOL(lz4_decompress_unknownoutputsize); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4 Decompressor"); #endif diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c index eb1a74f..f344f76 100644 --- a/lib/lz4/lz4hc_compress.c +++ b/lib/lz4/lz4hc_compress.c @@ -533,7 +533,7 @@ int lz4hc_compress(const unsigned char *src, size_t src_len, exit: return ret; } -EXPORT_SYMBOL_GPL(lz4hc_compress); +EXPORT_SYMBOL(lz4hc_compress); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4HC compressor"); -- cgit v1.1 From fab4f77d0ecc98293ec060bc9c77303db7dbdc46 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Wed, 11 Sep 2013 14:26:32 -0700 Subject: lz4: fix compression/decompression signedness mismatch LZ4 compression and decompression functions require different in signedness input/output parameters: unsigned char for compression and signed char for decompression. Change decompression API to require "(const) unsigned char *". Signed-off-by: Sergey Senozhatsky Cc: Kyungsik Lee Cc: Geert Uytterhoeven Cc: Yann Collet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/lz4.h | 8 ++++---- lib/lz4/lz4_decompress.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/lz4.h b/include/linux/lz4.h index d21c13f..4356686 100644 --- a/include/linux/lz4.h +++ b/include/linux/lz4.h @@ -67,8 +67,8 @@ int lz4hc_compress(const unsigned char *src, size_t src_len, * note : Destination buffer must be already allocated. * slightly faster than lz4_decompress_unknownoutputsize() */ -int lz4_decompress(const char *src, size_t *src_len, char *dest, - size_t actual_dest_len); +int lz4_decompress(const unsigned char *src, size_t *src_len, + unsigned char *dest, size_t actual_dest_len); /* * lz4_decompress_unknownoutputsize() @@ -82,6 +82,6 @@ int lz4_decompress(const char *src, size_t *src_len, char *dest, * Error if return (< 0) * note : Destination buffer must be already allocated. */ -int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, - char *dest, size_t *dest_len); +int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len, + unsigned char *dest, size_t *dest_len); #endif diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index 411be80..df6839e 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -283,8 +283,8 @@ _output_error: return (int) (-(((char *) ip) - source)); } -int lz4_decompress(const char *src, size_t *src_len, char *dest, - size_t actual_dest_len) +int lz4_decompress(const unsigned char *src, size_t *src_len, + unsigned char *dest, size_t actual_dest_len) { int ret = -1; int input_len = 0; @@ -302,8 +302,8 @@ exit_0: EXPORT_SYMBOL(lz4_decompress); #endif -int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, - char *dest, size_t *dest_len) +int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len, + unsigned char *dest, size_t *dest_len) { int ret = -1; int out_len = 0; -- cgit v1.1 From 69a113aae3d8c2753f68d0b64f0219d9cc6e493f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 20 Jun 2014 22:01:41 -0700 Subject: lz4: ensure length does not wrap Given some pathologically compressed data, lz4 could possibly decide to wrap a few internal variables, causing unknown things to happen. Catch this before the wrapping happens and abort the decompression. Reported-by: "Don A. Bailey" Cc: stable Signed-off-by: Greg Kroah-Hartman --- lib/lz4/lz4_decompress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index df6839e..99a03ac 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -72,6 +72,8 @@ static int lz4_uncompress(const char *source, char *dest, int osize) len = *ip++; for (; len == 255; length += 255) len = *ip++; + if (unlikely(length > (size_t)(length + len))) + goto _output_error; length += len; } -- cgit v1.1 From 4d43c9b5489f01cef6419af1935d97bd5b520c26 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Jun 2014 16:59:01 -0400 Subject: lz4: fix another possible overrun There is one other possible overrun in the lz4 code as implemented by Linux at this point in time (which differs from the upstream lz4 codebase, but will get synced at in a future kernel release.) As pointed out by Don, we also need to check the overflow in the data itself. While we are at it, replace the odd error return value with just a "simple" -1 value as the return value is never used for anything other than a basic "did this work or not" check. Reported-by: "Don A. Bailey" Reported-by: Willy Tarreau Cc: stable Signed-off-by: Greg Kroah-Hartman --- lib/lz4/lz4_decompress.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index 99a03ac..b74da44 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -108,6 +108,8 @@ static int lz4_uncompress(const char *source, char *dest, int osize) if (length == ML_MASK) { for (; *ip == 255; length += 255) ip++; + if (unlikely(length > (size_t)(length + *ip))) + goto _output_error; length += *ip++; } @@ -157,7 +159,7 @@ static int lz4_uncompress(const char *source, char *dest, int osize) /* write overflow error detected */ _output_error: - return (int) (-(((char *)ip) - source)); + return -1; } static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, -- cgit v1.1 From 85344953defa1132e1400dc4a26e51bd9b2c34f9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 3 Jul 2014 16:06:57 -0700 Subject: lz4: add overrun checks to lz4_uncompress_unknownoutputsize() Jan points out that I forgot to make the needed fixes to the lz4_uncompress_unknownoutputsize() function to mirror the changes done in lz4_decompress() with regards to potential pointer overflows. The only in-kernel user of this function is the zram code, which only takes data from a valid compressed buffer that it made itself, so it's not a big issue. But due to external kernel modules using this function, it's better to be safe here. Reported-by: Jan Beulich Cc: "Don A. Bailey" Cc: stable Signed-off-by: Greg Kroah-Hartman --- lib/lz4/lz4_decompress.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index b74da44..7a85967 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -192,6 +192,8 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, int s = 255; while ((ip < iend) && (s == 255)) { s = *ip++; + if (unlikely(length > (size_t)(length + s))) + goto _output_error; length += s; } } @@ -232,6 +234,8 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, if (length == ML_MASK) { while (ip < iend) { int s = *ip++; + if (unlikely(length > (size_t)(length + s))) + goto _output_error; length += s; if (s == 255) continue; @@ -284,7 +288,7 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, /* write overflow error detected */ _output_error: - return (int) (-(((char *) ip) - source)); + return -1; } int lz4_decompress(const unsigned char *src, size_t *src_len, -- cgit v1.1 From a51e43260ae2e9375014283e18fe2d35bdec675f Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Fri, 8 Jun 2012 15:39:25 +0900 Subject: staging: zsmalloc: zsmalloc: use unsigned long instead of void * We should use unsigned long as handle instead of void * to avoid any confusion. Without this, users may just treat zs_malloc return value as a pointer and try to deference it. This patch passed compile test(zram, zcache and ramster) and zram is tested on qemu. changelog * from v2 - remove hval pointed out by Nitin - based on next-20120607 * from v1 - change zcache's zv_create return value - baesd on next-20120604 Cc: Dan Magenheimer Acked-by: Seth Jennings Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Minchan Kim Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zram/zram_drv.c drivers/staging/zram/zram_drv.h drivers/staging/zsmalloc/zsmalloc-main.c drivers/staging/zsmalloc/zsmalloc.h Change-Id: I5b5adff5f31e3cf51cfd004df0f11e088d709d41 --- drivers/staging/zcache/zcache-main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index b698464..0d3fd04 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -693,14 +693,14 @@ static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5; static atomic_t zv_curr_dist_counts[NCHUNKS]; static atomic_t zv_cumul_dist_counts[NCHUNKS]; -static struct zv_hdr *zv_create(struct zs_pool *pool, uint32_t pool_id, +static unsigned long zv_create(struct zs_pool *pool, uint32_t pool_id, struct tmem_oid *oid, uint32_t index, void *cdata, unsigned clen) { struct zv_hdr *zv; u32 size = clen + sizeof(struct zv_hdr); int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT; - void *handle = NULL; + unsigned long handle = 0; BUG_ON(!irqs_disabled()); BUG_ON(chunks >= NCHUNKS); @@ -721,7 +721,7 @@ out: return handle; } -static void zv_free(struct zs_pool *pool, void *handle) +static void zv_free(struct zs_pool *pool, unsigned long handle) { unsigned long flags; struct zv_hdr *zv; @@ -743,7 +743,7 @@ static void zv_free(struct zs_pool *pool, void *handle) local_irq_restore(flags); } -static void zv_decompress(struct page *page, void *handle) +static void zv_decompress(struct page *page, unsigned long handle) { unsigned int clen = PAGE_SIZE; char *to_va; @@ -1247,7 +1247,7 @@ static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw, int ret = 0; BUG_ON(is_ephemeral(pool)); - zv_decompress((struct page *)(data), pampd); + zv_decompress((struct page *)(data), (unsigned long)pampd); return ret; } @@ -1282,7 +1282,7 @@ static void zcache_pampd_free(void *pampd, struct tmem_pool *pool, atomic_dec(&zcache_curr_eph_pampd_count); BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0); } else { - zv_free(cli->zspool, pampd); + zv_free(cli->zspool, (unsigned long)pampd); atomic_dec(&zcache_curr_pers_pampd_count); BUG_ON(atomic_read(&zcache_curr_pers_pampd_count) < 0); } -- cgit v1.1 From 1460992795a08a75595c5ac905f280c73c89ce12 Mon Sep 17 00:00:00 2001 From: Emerson Pinter Date: Sat, 4 Apr 2015 17:04:22 -0300 Subject: zcache: Fix zsmalloc include Change-Id: I8f0c873f92d8c75388aa59d670da755a4ded873d --- drivers/staging/zcache/zcache-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 0d3fd04..2532c97 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -33,7 +33,7 @@ #include #include "tmem.h" -#include "../zsmalloc/zsmalloc.h" +#include #if (!defined(CONFIG_CLEANCACHE) && !defined(CONFIG_FRONTSWAP)) #error "zcache is useless without CONFIG_CLEANCACHE or CONFIG_FRONTSWAP" -- cgit v1.1 From e587fc7e1422e54b05f8b92b601bc09ebe5ca22b Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Mon, 2 Jul 2012 16:15:52 -0500 Subject: staging: zsmalloc: add mapping modes This patch improves mapping performance in zsmalloc by getting usage information from the user in the form of a "mapping mode" and using it to avoid unnecessary copying for objects that span pages. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zram/zram_drv.c drivers/staging/zsmalloc/zsmalloc-main.c drivers/staging/zsmalloc/zsmalloc.h drivers/staging/zsmalloc/zsmalloc_int.h Change-Id: I0b7a97e21eb3b26270bd2949697ef6d14bf7ae27 --- drivers/staging/zcache/zcache-main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 2532c97..752390d 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -709,7 +709,7 @@ static unsigned long zv_create(struct zs_pool *pool, uint32_t pool_id, goto out; atomic_inc(&zv_curr_dist_counts[chunks]); atomic_inc(&zv_cumul_dist_counts[chunks]); - zv = zs_map_object(pool, handle); + zv = zs_map_object(pool, handle, ZS_MM_WO); zv->index = index; zv->oid = *oid; zv->pool_id = pool_id; @@ -728,7 +728,7 @@ static void zv_free(struct zs_pool *pool, unsigned long handle) uint16_t size; int chunks; - zv = zs_map_object(pool, handle); + zv = zs_map_object(pool, handle, ZS_MM_RW); ASSERT_SENTINEL(zv, ZVH); size = zv->size + sizeof(struct zv_hdr); INVERT_SENTINEL(zv, ZVH); @@ -750,7 +750,7 @@ static void zv_decompress(struct page *page, unsigned long handle) int ret; struct zv_hdr *zv; - zv = zs_map_object(zcache_host.zspool, handle); + zv = zs_map_object(zcache_host.zspool, handle, ZS_MM_RO); BUG_ON(zv->size == 0); ASSERT_SENTINEL(zv, ZVH); to_va = kmap_atomic(page, KM_USER0); -- cgit v1.1 From af358596dc922c13d1cf7319513e3371258bfe04 Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Mon, 8 Jul 2013 16:01:51 -0700 Subject: crypto: add lz4 Cryptographic API Add support for lz4 and lz4hc compression algorithm using the lib/lz4/* codebase. [akpm@linux-foundation.org: fix warnings] Signed-off-by: Chanho Min Cc: "Darrick J. Wong" Cc: Bob Pearson Cc: Richard Weinberger Cc: Herbert Xu Cc: Yann Collet Cc: Kyungsik Lee Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: crypto/Kconfig crypto/Makefile Change-Id: Ic3495647e3a902a12ae1b1951ff9bd89011696e0 --- crypto/Kconfig | 16 +++++++++ crypto/Makefile | 2 ++ crypto/lz4.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ crypto/lz4hc.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 crypto/lz4.c create mode 100644 crypto/lz4hc.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 0d12110..efd8816 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -865,6 +865,22 @@ config CRYPTO_LZO help This is the LZO algorithm. +config CRYPTO_LZ4 + tristate "LZ4 compression algorithm" + select CRYPTO_ALGAPI + select LZ4_COMPRESS + select LZ4_DECOMPRESS + help + This is the LZ4 algorithm. + +config CRYPTO_LZ4HC + tristate "LZ4HC compression algorithm" + select CRYPTO_ALGAPI + select LZ4HC_COMPRESS + select LZ4_DECOMPRESS + help + This is the LZ4 high compression mode algorithm. + comment "Random Number Generation" config CRYPTO_ANSI_CPRNG diff --git a/crypto/Makefile b/crypto/Makefile index 69c46ed..a6c3035 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -85,6 +85,8 @@ obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o +obj-$(CONFIG_CRYPTO_LZ4) += lz4.o +obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o obj-$(CONFIG_CRYPTO_RNG2) += rng.o obj-$(CONFIG_CRYPTO_RNG2) += krng.o obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o diff --git a/crypto/lz4.c b/crypto/lz4.c new file mode 100644 index 0000000..4586dd1 --- /dev/null +++ b/crypto/lz4.c @@ -0,0 +1,106 @@ +/* + * Cryptographic API. + * + * Copyright (c) 2013 Chanho Min + * + * 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 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 + * + */ + +#include +#include +#include +#include +#include + +struct lz4_ctx { + void *lz4_comp_mem; +}; + +static int lz4_init(struct crypto_tfm *tfm) +{ + struct lz4_ctx *ctx = crypto_tfm_ctx(tfm); + + ctx->lz4_comp_mem = vmalloc(LZ4_MEM_COMPRESS); + if (!ctx->lz4_comp_mem) + return -ENOMEM; + + return 0; +} + +static void lz4_exit(struct crypto_tfm *tfm) +{ + struct lz4_ctx *ctx = crypto_tfm_ctx(tfm); + vfree(ctx->lz4_comp_mem); +} + +static int lz4_compress_crypto(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen) +{ + struct lz4_ctx *ctx = crypto_tfm_ctx(tfm); + size_t tmp_len = *dlen; + int err; + + err = lz4_compress(src, slen, dst, &tmp_len, ctx->lz4_comp_mem); + + if (err < 0) + return -EINVAL; + + *dlen = tmp_len; + return 0; +} + +static int lz4_decompress_crypto(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen) +{ + int err; + size_t tmp_len = *dlen; + size_t __slen = slen; + + err = lz4_decompress(src, &__slen, dst, tmp_len); + if (err < 0) + return -EINVAL; + + *dlen = tmp_len; + return err; +} + +static struct crypto_alg alg_lz4 = { + .cra_name = "lz4", + .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, + .cra_ctxsize = sizeof(struct lz4_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(alg_lz4.cra_list), + .cra_init = lz4_init, + .cra_exit = lz4_exit, + .cra_u = { .compress = { + .coa_compress = lz4_compress_crypto, + .coa_decompress = lz4_decompress_crypto } } +}; + +static int __init lz4_mod_init(void) +{ + return crypto_register_alg(&alg_lz4); +} + +static void __exit lz4_mod_fini(void) +{ + crypto_unregister_alg(&alg_lz4); +} + +module_init(lz4_mod_init); +module_exit(lz4_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4 Compression Algorithm"); diff --git a/crypto/lz4hc.c b/crypto/lz4hc.c new file mode 100644 index 0000000..151ba31 --- /dev/null +++ b/crypto/lz4hc.c @@ -0,0 +1,106 @@ +/* + * Cryptographic API. + * + * Copyright (c) 2013 Chanho Min + * + * 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 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 + * + */ +#include +#include +#include +#include +#include + +struct lz4hc_ctx { + void *lz4hc_comp_mem; +}; + +static int lz4hc_init(struct crypto_tfm *tfm) +{ + struct lz4hc_ctx *ctx = crypto_tfm_ctx(tfm); + + ctx->lz4hc_comp_mem = vmalloc(LZ4HC_MEM_COMPRESS); + if (!ctx->lz4hc_comp_mem) + return -ENOMEM; + + return 0; +} + +static void lz4hc_exit(struct crypto_tfm *tfm) +{ + struct lz4hc_ctx *ctx = crypto_tfm_ctx(tfm); + + vfree(ctx->lz4hc_comp_mem); +} + +static int lz4hc_compress_crypto(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen) +{ + struct lz4hc_ctx *ctx = crypto_tfm_ctx(tfm); + size_t tmp_len = *dlen; + int err; + + err = lz4hc_compress(src, slen, dst, &tmp_len, ctx->lz4hc_comp_mem); + + if (err < 0) + return -EINVAL; + + *dlen = tmp_len; + return 0; +} + +static int lz4hc_decompress_crypto(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen) +{ + int err; + size_t tmp_len = *dlen; + size_t __slen = slen; + + err = lz4_decompress(src, &__slen, dst, tmp_len); + if (err < 0) + return -EINVAL; + + *dlen = tmp_len; + return err; +} + +static struct crypto_alg alg_lz4hc = { + .cra_name = "lz4hc", + .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, + .cra_ctxsize = sizeof(struct lz4hc_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(alg_lz4hc.cra_list), + .cra_init = lz4hc_init, + .cra_exit = lz4hc_exit, + .cra_u = { .compress = { + .coa_compress = lz4hc_compress_crypto, + .coa_decompress = lz4hc_decompress_crypto } } +}; + +static int __init lz4hc_mod_init(void) +{ + return crypto_register_alg(&alg_lz4hc); +} + +static void __exit lz4hc_mod_fini(void) +{ + crypto_unregister_alg(&alg_lz4hc); +} + +module_init(lz4hc_mod_init); +module_exit(lz4hc_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4HC Compression Algorithm"); -- cgit v1.1 From eb69dce0e5f1c67e6eeeab789683e7f5adc2d586 Mon Sep 17 00:00:00 2001 From: KOVACS Krisztian Date: Fri, 22 Aug 2014 10:44:35 +0200 Subject: crypto: lz4,lz4hc - fix decompression The lz4 library has two functions for decompression, with slightly different signatures and behaviour. The lz4_decompress_crypto() function seemed to be using the one that assumes that the decompressed length is known in advance. This patch switches to the other decompression function and makes sure that the length of the decompressed output is properly returned to the caller. The same issue was present in the lz4hc algorithm. Coincidentally, this change also makes very basic lz4 and lz4hc compression tests in testmgr pass. Signed-off-by: KOVACS Krisztian Signed-off-by: Herbert Xu --- crypto/lz4.c | 2 +- crypto/lz4hc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/lz4.c b/crypto/lz4.c index 4586dd1..34d072b 100644 --- a/crypto/lz4.c +++ b/crypto/lz4.c @@ -68,7 +68,7 @@ static int lz4_decompress_crypto(struct crypto_tfm *tfm, const u8 *src, size_t tmp_len = *dlen; size_t __slen = slen; - err = lz4_decompress(src, &__slen, dst, tmp_len); + err = lz4_decompress_unknownoutputsize(src, __slen, dst, &tmp_len); if (err < 0) return -EINVAL; diff --git a/crypto/lz4hc.c b/crypto/lz4hc.c index 151ba31..9218b3f 100644 --- a/crypto/lz4hc.c +++ b/crypto/lz4hc.c @@ -68,7 +68,7 @@ static int lz4hc_decompress_crypto(struct crypto_tfm *tfm, const u8 *src, size_t tmp_len = *dlen; size_t __slen = slen; - err = lz4_decompress(src, &__slen, dst, tmp_len); + err = lz4_decompress_unknownoutputsize(src, __slen, dst, &tmp_len); if (err < 0) return -EINVAL; -- cgit v1.1 From 2cff8e16465b5eb7b9f9dce7c7ee47c07b40a92b Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 9 Oct 2014 15:29:48 -0700 Subject: zsmalloc: move pages_allocated to zs_pool Currently, zram has no feature to limit memory so theoretically zram can deplete system memory. Users have asked for a limit several times as even without exhaustion zram makes it hard to control memory usage of the platform. This patchset adds the feature. Patch 1 makes zs_get_total_size_bytes faster because it would be used frequently in later patches for the new feature. Patch 2 changes zs_get_total_size_bytes's return unit from bytes to page so that zsmalloc doesn't need unnecessary operation(ie, << PAGE_SHIFT). Patch 3 adds new feature. I added the feature into zram layer, not zsmalloc because limiation is zram's requirement, not zsmalloc so any other user using zsmalloc(ie, zpool) shouldn't affected by unnecessary branch of zsmalloc. In future, if every users of zsmalloc want the feature, then, we could move the feature from client side to zsmalloc easily but vice versa would be painful. Patch 4 adds news facility to report maximum memory usage of zram so that this avoids user polling frequently via /sys/block/zram0/ mem_used_total and ensures transient max are not missed. This patch (of 4): pages_allocated has counted in size_class structure and when user of zsmalloc want to see total_size_bytes, it should gather all of count from each size_class to report the sum. It's not bad if user don't see the value often but if user start to see the value frequently, it would be not a good deal for performance pov. This patch moves the count from size_class to zs_pool so it could reduce memory footprint (from [255 * 8byte] to [sizeof(atomic_long_t)]). Signed-off-by: Minchan Kim Reviewed-by: Dan Streetman Cc: Sergey Senozhatsky Cc: Jerome Marchand Cc: Cc: Cc: Luigi Semenzato Cc: Nitin Gupta Cc: Seth Jennings Reviewed-by: David Horner Cc: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/zsmalloc.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 4d44c1e..e3b6909 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -199,9 +199,6 @@ struct size_class { spinlock_t lock; - /* stats */ - u64 pages_allocated; - struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS]; }; @@ -220,6 +217,7 @@ struct zs_pool { struct size_class size_class[ZS_SIZE_CLASSES]; gfp_t flags; /* allocation flags used when growing pool */ + atomic_long_t pages_allocated; }; /* @@ -933,8 +931,9 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) return 0; set_zspage_mapping(first_page, class->index, ZS_EMPTY); + atomic_long_add(class->pages_per_zspage, + &pool->pages_allocated); spin_lock(&class->lock); - class->pages_allocated += class->pages_per_zspage; } obj = (unsigned long)first_page->freelist; @@ -987,14 +986,13 @@ void zs_free(struct zs_pool *pool, unsigned long obj) first_page->inuse--; fullness = fix_fullness_group(pool, first_page); - - if (fullness == ZS_EMPTY) - class->pages_allocated -= class->pages_per_zspage; - spin_unlock(&class->lock); - if (fullness == ZS_EMPTY) + if (fullness == ZS_EMPTY) { + atomic_long_sub(class->pages_per_zspage, + &pool->pages_allocated); free_zspage(first_page); + } } EXPORT_SYMBOL_GPL(zs_free); @@ -1090,12 +1088,7 @@ EXPORT_SYMBOL_GPL(zs_unmap_object); u64 zs_get_total_size_bytes(struct zs_pool *pool) { - int i; - u64 npages = 0; - - for (i = 0; i < ZS_SIZE_CLASSES; i++) - npages += pool->size_class[i].pages_allocated; - + u64 npages = atomic_long_read(&pool->pages_allocated); return npages << PAGE_SHIFT; } EXPORT_SYMBOL_GPL(zs_get_total_size_bytes); -- cgit v1.1 From 032a8919a9dfc0043218efa04efccf58509e239f Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 9 Oct 2014 15:29:50 -0700 Subject: zsmalloc: change return value unit of zs_get_total_size_bytes zs_get_total_size_bytes returns a amount of memory zsmalloc consumed with *byte unit* but zsmalloc operates *page unit* rather than byte unit so let's change the API so benefit we could get is that reduce unnecessary overhead (ie, change page unit with byte unit) in zsmalloc. Since return type is pages, "zs_get_total_pages" is better than "zs_get_total_size_bytes". Signed-off-by: Minchan Kim Reviewed-by: Dan Streetman Cc: Sergey Senozhatsky Cc: Jerome Marchand Cc: Cc: Cc: Luigi Semenzato Cc: Nitin Gupta Cc: Seth Jennings Cc: David Horner Cc: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: mm/zsmalloc.c Change-Id: If5697d7b7f8ebaab3b58c1f9f84de747eb909ca3 --- drivers/block/zram/zram_drv.c | 4 ++-- include/linux/zsmalloc.h | 2 +- mm/zsmalloc.c | 8 +++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 2bb3f2c..803e7a2 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -111,10 +111,10 @@ static ssize_t mem_used_total_show(struct device *dev, down_read(&zram->init_lock); if (init_done(zram)) - val = zs_get_total_size_bytes(meta->mem_pool); + val = zs_get_total_pages(meta->mem_pool); up_read(&zram->init_lock); - return scnprintf(buf, PAGE_SIZE, "%llu\n", val); + return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT); } static ssize_t max_comp_streams_show(struct device *dev, diff --git a/include/linux/zsmalloc.h b/include/linux/zsmalloc.h index e44d634..05c2147 100644 --- a/include/linux/zsmalloc.h +++ b/include/linux/zsmalloc.h @@ -46,6 +46,6 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, enum zs_mapmode mm); void zs_unmap_object(struct zs_pool *pool, unsigned long handle); -u64 zs_get_total_size_bytes(struct zs_pool *pool); +unsigned long zs_get_total_pages(struct zs_pool *pool); #endif diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index e3b6909..8ac9230 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -239,7 +239,6 @@ struct mapping_area { enum zs_mapmode vm_mm; /* mapping mode */ }; - /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ static DEFINE_PER_CPU(struct mapping_area, zs_map_area); @@ -1086,12 +1085,11 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle) } EXPORT_SYMBOL_GPL(zs_unmap_object); -u64 zs_get_total_size_bytes(struct zs_pool *pool) +unsigned long zs_get_total_pages(struct zs_pool *pool) { - u64 npages = atomic_long_read(&pool->pages_allocated); - return npages << PAGE_SHIFT; + return atomic_long_read(&pool->pages_allocated); } -EXPORT_SYMBOL_GPL(zs_get_total_size_bytes); +EXPORT_SYMBOL_GPL(zs_get_total_pages); module_init(zs_init); module_exit(zs_exit); -- cgit v1.1 From cf8ced61d7f4bac6f8a6cfc8697e74f3fca8b013 Mon Sep 17 00:00:00 2001 From: nadlabak Date: Wed, 8 Apr 2015 23:17:43 +0200 Subject: zcache: Use zs_get_total_size_pages Change-Id: I800331317eda3ffa33b12314fc1641f3d2ca4db2 --- drivers/staging/zcache/zcache-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 752390d..d781bd0 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -1216,7 +1216,7 @@ static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph, } /* reject if mean compression is too poor */ if ((clen > zv_max_mean_zsize) && (curr_pers_pampd_count > 0)) { - total_zsize = zs_get_total_size_bytes(cli->zspool); + total_zsize = zs_get_total_pages(cli->zspool) << PAGE_SHIFT; zv_mean_zsize = div_u64(total_zsize, curr_pers_pampd_count); if (zv_mean_zsize > zv_max_mean_zsize) { -- cgit v1.1 From 86d495bfe8341eb50466fc6e7244287dd45be0c4 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 9 Oct 2014 15:30:01 -0700 Subject: zsmalloc: simplify init_zspage free obj linking Change zsmalloc init_zspage() logic to iterate through each object on each of its pages, checking the offset to verify the object is on the current page before linking it into the zspage. The current zsmalloc init_zspage free object linking code has logic that relies on there only being one page per zspage when PAGE_SIZE is a multiple of class->size. It calculates the number of objects for the current page, and iterates through all of them plus one, to account for the assumed partial object at the end of the page. While this currently works, the logic can be simplified to just link the object at each successive offset until the offset is larger than PAGE_SIZE, which does not rely on PAGE_SIZE being a multiple of class->size. Signed-off-by: Dan Streetman Acked-by: Minchan Kim Cc: Sergey Senozhatsky Cc: Nitin Gupta Cc: Seth Jennings Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/zsmalloc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 8ac9230..21fd44d 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -551,7 +551,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) while (page) { struct page *next_page; struct link_free *link; - unsigned int i, objs_on_page; + unsigned int i = 1; /* * page->index stores offset of first object starting @@ -564,14 +564,10 @@ static void init_zspage(struct page *first_page, struct size_class *class) link = (struct link_free *)kmap_atomic(page) + off / sizeof(*link); - objs_on_page = (PAGE_SIZE - off) / class->size; - for (i = 1; i <= objs_on_page; i++) { - off += class->size; - if (off < PAGE_SIZE) { - link->next = obj_location_to_handle(page, i); - link += class->size / sizeof(*link); - } + while ((off += class->size) < PAGE_SIZE) { + link->next = obj_location_to_handle(page, i++); + link += class->size / sizeof(*link); } /* @@ -583,7 +579,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) link->next = obj_location_to_handle(next_page, 0); kunmap_atomic(link); page = next_page; - off = (off + class->size) % PAGE_SIZE; + off %= PAGE_SIZE; } } -- cgit v1.1 From ea3467611518699b649b594dc8f3c1132be97d8b Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Fri, 12 Dec 2014 16:56:44 -0800 Subject: zsmalloc: merge size_class to reduce fragmentation zsmalloc has many size_classes to reduce fragmentation and they are in 16 bytes unit, for example, 16, 32, 48, etc., if PAGE_SIZE is 4096. And, zsmalloc has constraint that each zspage has 4 pages at maximum. In this situation, we can see interesting aspect. Let's think about size_class for 1488, 1472, ..., 1376. To prevent external fragmentation, they uses 4 pages per zspage and so all they can contain 11 objects at maximum. 16384 (4096 * 4) = 1488 * 11 + remains 16384 (4096 * 4) = 1472 * 11 + remains 16384 (4096 * 4) = ... 16384 (4096 * 4) = 1376 * 11 + remains It means that they have same characteristics and classification between them isn't needed. If we use one size_class for them, we can reduce fragementation and save some memory since both the 1488 and 1472 sized classes can only fit 11 objects into 4 pages, and an object that's 1472 bytes can fit into an object that's 1488 bytes, merging these classes to always use objects that are 1488 bytes will reduce the total number of size classes. And reducing the total number of size classes reduces overall fragmentation, because a wider range of compressed pages can fit into a single size class, leaving less unused objects in each size class. For this purpose, this patch implement size_class merging. If there is size_class that have same pages_per_zspage and same number of objects per zspage with previous size_class, we don't create new size_class. Instead, we use previous, same characteristic size_class. With this way, above example sizes (1488, 1472, ..., 1376) use just one size_class so we can get much more memory utilization. Below is result of my simple test. TEST ENV: EXT4 on zram, mount with discard option WORKLOAD: untar kernel source code, remove directory in descending order in size. (drivers arch fs sound include net Documentation firmware kernel tools) Each line represents orig_data_size, compr_data_size, mem_used_total, fragmentation overhead (mem_used - compr_data_size) and overhead ratio (overhead to compr_data_size), respectively, after untar and remove operation is executed. * untar-nomerge.out orig_size compr_size used_size overhead overhead_ratio 525.88MB 199.16MB 210.23MB 11.08MB 5.56% 288.32MB 97.43MB 105.63MB 8.20MB 8.41% 177.32MB 61.12MB 69.40MB 8.28MB 13.55% 146.47MB 47.32MB 56.10MB 8.78MB 18.55% 124.16MB 38.85MB 48.41MB 9.55MB 24.58% 103.93MB 31.68MB 40.93MB 9.25MB 29.21% 84.34MB 22.86MB 32.72MB 9.86MB 43.13% 66.87MB 14.83MB 23.83MB 9.00MB 60.70% 60.67MB 11.11MB 18.60MB 7.49MB 67.48% 55.86MB 8.83MB 16.61MB 7.77MB 88.03% 53.32MB 8.01MB 15.32MB 7.31MB 91.24% * untar-merge.out orig_size compr_size used_size overhead overhead_ratio 526.23MB 199.18MB 209.81MB 10.64MB 5.34% 288.68MB 97.45MB 104.08MB 6.63MB 6.80% 177.68MB 61.14MB 66.93MB 5.79MB 9.47% 146.83MB 47.34MB 52.79MB 5.45MB 11.51% 124.52MB 38.87MB 44.30MB 5.43MB 13.96% 104.29MB 31.70MB 36.83MB 5.13MB 16.19% 84.70MB 22.88MB 27.92MB 5.04MB 22.04% 67.11MB 14.83MB 19.26MB 4.43MB 29.86% 60.82MB 11.10MB 14.90MB 3.79MB 34.17% 55.90MB 8.82MB 12.61MB 3.79MB 42.97% 53.32MB 8.01MB 11.73MB 3.73MB 46.53% As you can see above result, merged one has better utilization (overhead ratio, 5th column) and uses less memory (mem_used_total, 3rd column). Signed-off-by: Joonsoo Kim Cc: Minchan Kim Cc: Nitin Gupta Cc: Jerome Marchand Cc: Sergey Senozhatsky Reviewed-by: Dan Streetman Cc: Luigi Semenzato Cc: Cc: "seungho1.park" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/zsmalloc.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 21fd44d..36c4efc 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -214,7 +214,7 @@ struct link_free { }; struct zs_pool { - struct size_class size_class[ZS_SIZE_CLASSES]; + struct size_class *size_class[ZS_SIZE_CLASSES]; gfp_t flags; /* allocation flags used when growing pool */ atomic_long_t pages_allocated; @@ -391,7 +391,7 @@ static enum fullness_group fix_fullness_group(struct zs_pool *pool, if (newfg == currfg) goto out; - class = &pool->size_class[class_idx]; + class = pool->size_class[class_idx]; remove_zspage(page, class, currfg); insert_zspage(page, class, newfg); set_zspage_mapping(page, class_idx, newfg); @@ -829,6 +829,23 @@ fail: return notifier_to_errno(ret); } +static unsigned int get_maxobj_per_zspage(int size, int pages_per_zspage) +{ + return pages_per_zspage * PAGE_SIZE / size; +} + +static bool can_merge(struct size_class *prev, int size, int pages_per_zspage) +{ + if (prev->pages_per_zspage != pages_per_zspage) + return false; + + if (get_maxobj_per_zspage(prev->size, prev->pages_per_zspage) + != get_maxobj_per_zspage(size, pages_per_zspage)) + return false; + + return true; +} + /** * zs_create_pool - Creates an allocation pool to work from. * @flags: allocation flags used to allocate pool metadata @@ -849,25 +866,56 @@ struct zs_pool *zs_create_pool(gfp_t flags) if (!pool) return NULL; - for (i = 0; i < ZS_SIZE_CLASSES; i++) { + /* + * Iterate reversly, because, size of size_class that we want to use + * for merging should be larger or equal to current size. + */ + for (i = ZS_SIZE_CLASSES - 1; i >= 0; i--) { int size; + int pages_per_zspage; struct size_class *class; + struct size_class *prev_class; size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; if (size > ZS_MAX_ALLOC_SIZE) size = ZS_MAX_ALLOC_SIZE; + pages_per_zspage = get_pages_per_zspage(size); + + /* + * size_class is used for normal zsmalloc operation such + * as alloc/free for that size. Although it is natural that we + * have one size_class for each size, there is a chance that we + * can get more memory utilization if we use one size_class for + * many different sizes whose size_class have same + * characteristics. So, we makes size_class point to + * previous size_class if possible. + */ + if (i < ZS_SIZE_CLASSES - 1) { + prev_class = pool->size_class[i + 1]; + if (can_merge(prev_class, size, pages_per_zspage)) { + pool->size_class[i] = prev_class; + continue; + } + } + + class = kzalloc(sizeof(struct size_class), GFP_KERNEL); + if (!class) + goto err; - class = &pool->size_class[i]; class->size = size; class->index = i; + class->pages_per_zspage = pages_per_zspage; spin_lock_init(&class->lock); - class->pages_per_zspage = get_pages_per_zspage(size); - + pool->size_class[i] = class; } pool->flags = flags; return pool; + +err: + zs_destroy_pool(pool); + return NULL; } EXPORT_SYMBOL_GPL(zs_create_pool); @@ -877,7 +925,13 @@ void zs_destroy_pool(struct zs_pool *pool) for (i = 0; i < ZS_SIZE_CLASSES; i++) { int fg; - struct size_class *class = &pool->size_class[i]; + struct size_class *class = pool->size_class[i]; + + if (!class) + continue; + + if (class->index != i) + continue; for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { if (class->fullness_list[fg]) { @@ -885,6 +939,7 @@ void zs_destroy_pool(struct zs_pool *pool) class->size, fg); } } + kfree(class); } kfree(pool); } @@ -903,7 +958,6 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) { unsigned long obj; struct link_free *link; - int class_idx; struct size_class *class; struct page *first_page, *m_page; @@ -912,9 +966,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) return 0; - class_idx = get_size_class_index(size); - class = &pool->size_class[class_idx]; - BUG_ON(class_idx != class->index); + class = pool->size_class[get_size_class_index(size)]; spin_lock(&class->lock); first_page = find_get_zspage(class); @@ -967,7 +1019,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj) first_page = get_first_page(f_page); get_zspage_mapping(first_page, &class_idx, &fullness); - class = &pool->size_class[class_idx]; + class = pool->size_class[class_idx]; f_offset = obj_idx_to_offset(f_page, f_objidx, class->size); spin_lock(&class->lock); @@ -1028,7 +1080,7 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, obj_handle_to_location(handle, &page, &obj_idx); get_zspage_mapping(get_first_page(page), &class_idx, &fg); - class = &pool->size_class[class_idx]; + class = pool->size_class[class_idx]; off = obj_idx_to_offset(page, obj_idx, class->size); area = &get_cpu_var(zs_map_area); @@ -1062,7 +1114,7 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle) obj_handle_to_location(handle, &page, &obj_idx); get_zspage_mapping(get_first_page(page), &class_idx, &fg); - class = &pool->size_class[class_idx]; + class = pool->size_class[class_idx]; off = obj_idx_to_offset(page, obj_idx, class->size); area = &__get_cpu_var(zs_map_area); -- cgit v1.1 From d15bf007b7b7507fe1a13ba449dd73a7b0ffee4c Mon Sep 17 00:00:00 2001 From: Shashank Shekhar Date: Fri, 12 Dec 2014 13:31:13 -0600 Subject: lib: lz4: Set ARM_EFFICIENT_UNALIGNED_ACCESS Set ARM_EFFICIENT_UNALIGNED_ACCESS to improve performance in lz4 compression and decompression. On msm8x26 cortex-a7, LZO LZ4 LZ4 w/ UA decompress (bs=4k) 121.21 115.52 148.7 LZO LZ4 LZ4 w/ UA compress (bs=4k) 37.5 34.5 44.8 Change-Id: I10dfea380f7558e29576d65f91c8cee13bf8e166 Signed-off-by: Chris Fries Reviewed-on: http://gerrit.mot.com/697567 Tested-by: Jira Key Reviewed-by: Shashank Shekhar Reviewed-by: Igor Kovalenko Submit-Approved: Jira Key (cherry picked from commit 0fbb0d508f7904f0741a174de83f7aa2a65fa1a0) --- lib/lz4/lz4defs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h index abcecdc..9b4182f 100644 --- a/lib/lz4/lz4defs.h +++ b/lib/lz4/lz4defs.h @@ -21,6 +21,7 @@ /* * Architecture-specific macros */ +#define ARM_EFFICIENT_UNALIGNED_ACCESS #define BYTE u8 typedef struct _U16_S { u16 v; } U16_S; typedef struct _U32_S { u32 v; } U32_S; -- cgit v1.1 From f2eb4e82d2aecc5f8e2b7fa654a090284a51c9f2 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Fri, 12 Dec 2014 16:56:58 -0800 Subject: zsmalloc: correct fragile [kmap|kunmap]_atomic use The kunmap_atomic should use virtual address getting by kmap_atomic. However, some pieces of code in zsmalloc uses modified address, not the one got by kmap_atomic for kunmap_atomic. It's okay for working because zsmalloc modifies the address inner PAGE_SIZE bounday so it works with current kmap_atomic's implementation. But it's still fragile with potential changing of kmap_atomic so let's correct it. I got a subtle bug when I implemented a new feature of zsmalloc (compaction) due to a link's mishandling (the link was over page boundary). Although it was totally my mistake, it took a while to find the cause because an unpredictable kmapped address was unmapped causing an almost random crash. Change-Id: Ic637e7d65e743a42df5833566054634c9a08f86d Signed-off-by: Minchan Kim Cc: Nitin Gupta Cc: Sergey Senozhatsky Cc: Dan Streetman Cc: Seth Jennings Cc: Jerome Marchand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/zsmalloc.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 36c4efc..7857a4a 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -552,6 +552,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) struct page *next_page; struct link_free *link; unsigned int i = 1; + void *vaddr; /* * page->index stores offset of first object starting @@ -562,8 +563,8 @@ static void init_zspage(struct page *first_page, struct size_class *class) if (page != first_page) page->index = off; - link = (struct link_free *)kmap_atomic(page) + - off / sizeof(*link); + vaddr = kmap_atomic(page); + link = (struct link_free *)vaddr + off / sizeof(*link); while ((off += class->size) < PAGE_SIZE) { link->next = obj_location_to_handle(page, i++); @@ -577,7 +578,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) */ next_page = get_next_page(page); link->next = obj_location_to_handle(next_page, 0); - kunmap_atomic(link); + kunmap_atomic(vaddr); page = next_page; off %= PAGE_SIZE; } @@ -959,6 +960,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) unsigned long obj; struct link_free *link; struct size_class *class; + void *vaddr; struct page *first_page, *m_page; unsigned long m_objidx, m_offset; @@ -987,11 +989,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) obj_handle_to_location(obj, &m_page, &m_objidx); m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); - link = (struct link_free *)kmap_atomic(m_page) + - m_offset / sizeof(*link); + vaddr = kmap_atomic(m_page); + link = (struct link_free *)vaddr + m_offset / sizeof(*link); first_page->freelist = link->next; memset(link, POISON_INUSE, sizeof(*link)); - kunmap_atomic(link); + kunmap_atomic(vaddr); first_page->inuse++; /* Now move the zspage to another fullness group, if required */ @@ -1007,6 +1009,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj) struct link_free *link; struct page *first_page, *f_page; unsigned long f_objidx, f_offset; + void *vaddr; int class_idx; struct size_class *class; @@ -1025,10 +1028,10 @@ void zs_free(struct zs_pool *pool, unsigned long obj) spin_lock(&class->lock); /* Insert this object in containing zspage's freelist */ - link = (struct link_free *)((unsigned char *)kmap_atomic(f_page) - + f_offset); + vaddr = kmap_atomic(f_page); + link = (struct link_free *)(vaddr + f_offset); link->next = first_page->freelist; - kunmap_atomic(link); + kunmap_atomic(vaddr); first_page->freelist = (void *)obj; first_page->inuse--; -- cgit v1.1 From d997b41417d94c22baaabd8d84a6a4f1e2e93fb9 Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Fri, 12 Dec 2014 16:57:10 -0800 Subject: mm/zsmalloc: allocate exactly size of struct zs_pool In zs_create_pool(), we allocate memory more then sizeof(struct zs_pool) ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); This patch allocate memory of exactly needed size. Change-Id: Id0ada356009c92bf791c94be073f4467f7bb1aac Signed-off-by: Ganesh Mahendran Acked-by: Minchan Kim Cc: Nitin Gupta Cc: Dan Streetman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/zsmalloc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 7857a4a..5e17643 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -859,11 +859,10 @@ static bool can_merge(struct size_class *prev, int size, int pages_per_zspage) */ struct zs_pool *zs_create_pool(gfp_t flags) { - int i, ovhd_size; + int i; struct zs_pool *pool; - ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); - pool = kzalloc(ovhd_size, GFP_KERNEL); + pool = kzalloc(sizeof(*pool), GFP_KERNEL); if (!pool) return NULL; -- cgit v1.1 From a6ce37fb73dce407da9d7d1d03408602be84177c Mon Sep 17 00:00:00 2001 From: Vinayak Menon Date: Wed, 25 Feb 2015 19:43:59 +0530 Subject: mm: swap: don't delay swap free for fast swap devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are couple of issues with swapcache usage when ZRAM is used as swap device. 1) Kernel does a swap readahead which can be around 6 to 8 pages depending on total ram, which is not required for zram since accesses are fast. 2) Kernel delays the freeing up of swapcache expecting a later hit, which again is useless in the case of zram. 3) This is not related to swapcache, but zram usage itself. As mentioned in (2) kernel delays freeing of swapcache, but along with that it delays zram compressed page free also. i.e. there can be 2 copies, though one is compressed. This patch addresses these issues using two new flags QUEUE_FLAG_FAST and SWP_FAST, to indicate that accesses to the device will be fast and cheap, and instructs the swap layer to free up swap space agressively, and not to do read ahead. Change-Id: I5d2d5176a5f9420300bb2f843f6ecbdb25ea80e4 Signed-off-by: Vinayak Menon Signed-off-by: D. Andrei Măceș Conflicts: include/linux/blkdev.h include/linux/swap.h mm/swap_state.c mm/swapfile.c Conflicts: include/linux/blkdev.h --- drivers/block/zram/zram_drv.c | 1 + include/linux/blkdev.h | 2 ++ include/linux/swap.h | 20 +++++++++++++++++--- mm/memory.c | 3 ++- mm/swapfile.c | 33 +++++++++++++++++++++++++++++---- mm/vmscan.c | 2 +- 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 803e7a2..7040165 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -936,6 +936,7 @@ static int create_device(struct zram *zram, int device_id) zram->disk->private_data = zram; snprintf(zram->disk->disk_name, 16, "zram%d", device_id); + __set_bit(QUEUE_FLAG_FAST, &zram->queue->queue_flags); /* Actual capacity set using syfs (/sys/block/zram/disksize */ set_capacity(zram->disk, 0); /* zram devices sort of resembles non-rotational disks */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 1b13021..af75d16 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -403,6 +403,7 @@ struct request_queue #define QUEUE_FLAG_NOXMERGES 15 /* No extended merges */ #define QUEUE_FLAG_ADD_RANDOM 16 /* Contributes to random pool */ #define QUEUE_FLAG_SECDISCARD 17 /* supports SECDISCARD */ +#define QUEUE_FLAG_FAST 20 /* fast block device (e.g. ram based) */ #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ (1 << QUEUE_FLAG_STACKABLE) | \ @@ -487,6 +488,7 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q) #define blk_queue_discard(q) test_bit(QUEUE_FLAG_DISCARD, &(q)->queue_flags) #define blk_queue_secdiscard(q) (blk_queue_discard(q) && \ test_bit(QUEUE_FLAG_SECDISCARD, &(q)->queue_flags)) +#define blk_queue_fast(q) test_bit(QUEUE_FLAG_FAST, &(q)->queue_flags) #define blk_noretry_request(rq) \ ((rq)->cmd_flags & (REQ_FAILFAST_DEV|REQ_FAILFAST_TRANSPORT| \ diff --git a/include/linux/swap.h b/include/linux/swap.h index e73799d..be5eecc 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -150,6 +150,7 @@ enum { SWP_BLKDEV = (1 << 6), /* its a block device */ /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ + SWP_FAST = (1 << 9), /* blkdev access is fast and cheap */ }; #define SWAP_CLUSTER_MAX 32 @@ -201,9 +202,6 @@ struct swap_list_t { int next; /* swapfile to be used next */ }; -/* Swap 50% full? Release swapcache more aggressively.. */ -#define vm_swap_full() (nr_swap_pages*2 < total_swap_pages) - /* linux/mm/page_alloc.c */ extern unsigned long totalram_pages; extern unsigned long totalreserve_pages; @@ -320,6 +318,21 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t, /* linux/mm/swapfile.c */ extern long nr_swap_pages; extern long total_swap_pages; +extern bool is_swap_fast(swp_entry_t entry); + +/* Swap 50% full? Release swapcache more aggressively.. */ +static inline bool vm_swap_full(struct swap_info_struct *si) +{ + /* + * If the swap device is fast, return true + * not to delay swap free. + */ + if (si->flags & SWP_FAST) + return true; + + return nr_swap_pages*2 < total_swap_pages; +} + extern void si_swapinfo(struct sysinfo *); extern swp_entry_t get_swap_page(void); extern swp_entry_t get_swap_page_of_type(int); @@ -379,6 +392,7 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) #define nr_swap_pages 0L #define total_swap_pages 0L #define total_swapcache_pages 0UL +#define vm_swap_full(si) 0 #define si_swapinfo(val) \ do { (val)->freeswap = (val)->totalswap = 0; } while (0) diff --git a/mm/memory.c b/mm/memory.c index 5331e67..067a4f3 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3140,7 +3140,8 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, mem_cgroup_commit_charge_swapin(page, ptr); swap_free(entry); - if (vm_swap_full() || (vma->vm_flags & VM_LOCKED) || PageMlocked(page)) + if ((PageSwapCache(page) && vm_swap_full(page_swap_info(page))) || + (vma->vm_flags & VM_LOCKED) || PageMlocked(page)) try_to_free_swap(page); unlock_page(page); if (swapcache) { diff --git a/mm/swapfile.c b/mm/swapfile.c index c8f4338..7197864 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -69,6 +69,26 @@ static inline unsigned char swap_count(unsigned char ent) return ent & ~SWAP_HAS_CACHE; /* may include SWAP_HAS_CONT flag */ } +bool is_swap_fast(swp_entry_t entry) +{ + struct swap_info_struct *p; + unsigned long type; + + if (non_swap_entry(entry)) + return false; + + type = swp_type(entry); + if (type >= nr_swapfiles) + return false; + + p = swap_info[type]; + + if (p->flags & SWP_FAST) + return true; + + return false; +} + /* returns 1 if swap entry is freed */ static int __try_to_reclaim_swap(struct swap_info_struct *si, unsigned long offset) @@ -289,7 +309,7 @@ checks: scan_base = offset = si->lowest_bit; /* reuse swap entry of cache-only swap if not busy. */ - if (vm_swap_full() && si->swap_map[offset] == SWAP_HAS_CACHE) { + if (vm_swap_full(si) && si->swap_map[offset] == SWAP_HAS_CACHE) { int swap_was_freed; spin_unlock(&swap_lock); swap_was_freed = __try_to_reclaim_swap(si, offset); @@ -378,7 +398,8 @@ scan: spin_lock(&swap_lock); goto checks; } - if (vm_swap_full() && si->swap_map[offset] == SWAP_HAS_CACHE) { + if (vm_swap_full(si) && + si->swap_map[offset] == SWAP_HAS_CACHE) { spin_lock(&swap_lock); goto checks; } @@ -393,7 +414,8 @@ scan: spin_lock(&swap_lock); goto checks; } - if (vm_swap_full() && si->swap_map[offset] == SWAP_HAS_CACHE) { + if (vm_swap_full(si) && + si->swap_map[offset] == SWAP_HAS_CACHE) { spin_lock(&swap_lock); goto checks; } @@ -708,7 +730,8 @@ int free_swap_and_cache(swp_entry_t entry) * Also recheck PageSwapCache now page is locked (above). */ if (PageSwapCache(page) && !PageWriteback(page) && - (!page_mapped(page) || vm_swap_full())) { + (!page_mapped(page) || + vm_swap_full(page_swap_info(page)))) { delete_from_swap_cache(page); SetPageDirty(page); } @@ -2114,6 +2137,8 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) } if (discard_swap(p) == 0 && (swap_flags & SWAP_FLAG_DISCARD)) p->flags |= SWP_DISCARDABLE; + if (blk_queue_fast(bdev_get_queue(p->bdev))) + p->flags |= SWP_FAST; } mutex_lock(&swapon_mutex); diff --git a/mm/vmscan.c b/mm/vmscan.c index 08f11e2..9b72c26 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -985,7 +985,7 @@ cull_mlocked: activate_locked: /* Not a candidate for swapping, so reclaim swap space. */ - if (PageSwapCache(page) && vm_swap_full()) + if (PageSwapCache(page) && vm_swap_full(page_swap_info(page))) try_to_free_swap(page); VM_BUG_ON(PageActive(page)); SetPageActive(page); -- cgit v1.1 From 4698b1b9e737365e206f1ef55a864d8e205fe8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=2E=20Andrei=20M=C4=83ce=C8=99?= Date: Wed, 30 Sep 2015 15:18:37 -0400 Subject: mm: Need page_swap_info() helper method from upstream Stolen from commit f981c5950fa85916ba49bea5d9a7a5078f47e569: "mm: methods for teaching filesystems about PG_swapcache pages" Change-Id: I6673913f9c825d3a6de88a652e99bcaf04eb1dd6 --- include/linux/swap.h | 1 + mm/swapfile.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/linux/swap.h b/include/linux/swap.h index be5eecc..bd6e937 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -348,6 +348,7 @@ extern int swap_type_of(dev_t, sector_t, struct block_device **); extern unsigned int count_swap_pages(int, int); extern sector_t map_swap_page(struct page *, struct block_device **); extern sector_t swapdev_block(int, pgoff_t); +extern struct swap_info_struct *page_swap_info(struct page *); extern int reuse_swap_page(struct page *); extern int try_to_free_swap(struct page *); struct backing_dev_info; diff --git a/mm/swapfile.c b/mm/swapfile.c index 7197864..4add436 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2321,6 +2321,13 @@ int swapcache_prepare(swp_entry_t entry) return __swap_duplicate(entry, SWAP_HAS_CACHE); } +struct swap_info_struct *page_swap_info(struct page *page) +{ + swp_entry_t swap = { .val = page_private(page) }; + BUG_ON(!PageSwapCache(page)); + return swap_info[swp_type(swap)]; +} + /* * swap_lock prevents swap_map being freed. Don't grab an extra * reference on the swaphandle, it doesn't matter if it becomes unused. -- cgit v1.1 From 25925d66273ddbaa96a46bd35a817c8742f41309 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 13 Aug 2012 17:24:24 +0200 Subject: lib/lzo: Rename lzo1x_decompress.c to lzo1x_decompress_safe.c Rename the source file to match the function name and thereby also make room for a possible future even slightly faster "non-safe" decompressor version. Change-Id: I482ee415d43c5aaa77d34946ae1fa0af5465b007 Signed-off-by: Markus F.X.J. Oberhumer --- lib/lzo/Makefile | 2 +- lib/lzo/lzo1x_decompress.c | 255 ---------------------------------------- lib/lzo/lzo1x_decompress_safe.c | 255 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+), 256 deletions(-) delete mode 100644 lib/lzo/lzo1x_decompress.c create mode 100644 lib/lzo/lzo1x_decompress_safe.c diff --git a/lib/lzo/Makefile b/lib/lzo/Makefile index e764116..f0f7d7c 100644 --- a/lib/lzo/Makefile +++ b/lib/lzo/Makefile @@ -1,5 +1,5 @@ lzo_compress-objs := lzo1x_compress.o -lzo_decompress-objs := lzo1x_decompress.o +lzo_decompress-objs := lzo1x_decompress_safe.o obj-$(CONFIG_LZO_COMPRESS) += lzo_compress.o obj-$(CONFIG_LZO_DECOMPRESS) += lzo_decompress.o diff --git a/lib/lzo/lzo1x_decompress.c b/lib/lzo/lzo1x_decompress.c deleted file mode 100644 index f2fd098..0000000 --- a/lib/lzo/lzo1x_decompress.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * LZO1X Decompressor from MiniLZO - * - * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer - * - * The full LZO package can be found at: - * http://www.oberhumer.com/opensource/lzo/ - * - * Changed for kernel use by: - * Nitin Gupta - * Richard Purdie - */ - -#ifndef STATIC -#include -#include -#endif - -#include -#include -#include "lzodefs.h" - -#define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x)) -#define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x)) -#define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op) - -#define COPY4(dst, src) \ - put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst)) - -int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, - unsigned char *out, size_t *out_len) -{ - const unsigned char * const ip_end = in + in_len; - unsigned char * const op_end = out + *out_len; - const unsigned char *ip = in, *m_pos; - unsigned char *op = out; - size_t t; - - *out_len = 0; - - if (*ip > 17) { - t = *ip++ - 17; - if (t < 4) - goto match_next; - if (HAVE_OP(t, op_end, op)) - goto output_overrun; - if (HAVE_IP(t + 1, ip_end, ip)) - goto input_overrun; - do { - *op++ = *ip++; - } while (--t > 0); - goto first_literal_run; - } - - while ((ip < ip_end)) { - t = *ip++; - if (t >= 16) - goto match; - if (t == 0) { - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - while (*ip == 0) { - t += 255; - ip++; - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - } - t += 15 + *ip++; - } - if (HAVE_OP(t + 3, op_end, op)) - goto output_overrun; - if (HAVE_IP(t + 4, ip_end, ip)) - goto input_overrun; - - COPY4(op, ip); - op += 4; - ip += 4; - if (--t > 0) { - if (t >= 4) { - do { - COPY4(op, ip); - op += 4; - ip += 4; - t -= 4; - } while (t >= 4); - if (t > 0) { - do { - *op++ = *ip++; - } while (--t > 0); - } - } else { - do { - *op++ = *ip++; - } while (--t > 0); - } - } - -first_literal_run: - t = *ip++; - if (t >= 16) - goto match; - m_pos = op - (1 + M2_MAX_OFFSET); - m_pos -= t >> 2; - m_pos -= *ip++ << 2; - - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - - if (HAVE_OP(3, op_end, op)) - goto output_overrun; - *op++ = *m_pos++; - *op++ = *m_pos++; - *op++ = *m_pos; - - goto match_done; - - do { -match: - if (t >= 64) { - m_pos = op - 1; - m_pos -= (t >> 2) & 7; - m_pos -= *ip++ << 3; - t = (t >> 5) - 1; - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - if (HAVE_OP(t + 3 - 1, op_end, op)) - goto output_overrun; - goto copy_match; - } else if (t >= 32) { - t &= 31; - if (t == 0) { - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - while (*ip == 0) { - t += 255; - ip++; - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - } - t += 31 + *ip++; - } - m_pos = op - 1; - m_pos -= get_unaligned_le16(ip) >> 2; - ip += 2; - } else if (t >= 16) { - m_pos = op; - m_pos -= (t & 8) << 11; - - t &= 7; - if (t == 0) { - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - while (*ip == 0) { - t += 255; - ip++; - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - } - t += 7 + *ip++; - } - m_pos -= get_unaligned_le16(ip) >> 2; - ip += 2; - if (m_pos == op) - goto eof_found; - m_pos -= 0x4000; - } else { - m_pos = op - 1; - m_pos -= t >> 2; - m_pos -= *ip++ << 2; - - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - if (HAVE_OP(2, op_end, op)) - goto output_overrun; - - *op++ = *m_pos++; - *op++ = *m_pos; - goto match_done; - } - - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - if (HAVE_OP(t + 3 - 1, op_end, op)) - goto output_overrun; - - if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { - COPY4(op, m_pos); - op += 4; - m_pos += 4; - t -= 4 - (3 - 1); - do { - COPY4(op, m_pos); - op += 4; - m_pos += 4; - t -= 4; - } while (t >= 4); - if (t > 0) - do { - *op++ = *m_pos++; - } while (--t > 0); - } else { -copy_match: - *op++ = *m_pos++; - *op++ = *m_pos++; - do { - *op++ = *m_pos++; - } while (--t > 0); - } -match_done: - t = ip[-2] & 3; - if (t == 0) - break; -match_next: - if (HAVE_OP(t, op_end, op)) - goto output_overrun; - if (HAVE_IP(t + 1, ip_end, ip)) - goto input_overrun; - - *op++ = *ip++; - if (t > 1) { - *op++ = *ip++; - if (t > 2) - *op++ = *ip++; - } - - t = *ip++; - } while (ip < ip_end); - } - - *out_len = op - out; - return LZO_E_EOF_NOT_FOUND; - -eof_found: - *out_len = op - out; - return (ip == ip_end ? LZO_E_OK : - (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); -input_overrun: - *out_len = op - out; - return LZO_E_INPUT_OVERRUN; - -output_overrun: - *out_len = op - out; - return LZO_E_OUTPUT_OVERRUN; - -lookbehind_overrun: - *out_len = op - out; - return LZO_E_LOOKBEHIND_OVERRUN; -} -#ifndef STATIC -EXPORT_SYMBOL_GPL(lzo1x_decompress_safe); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("LZO1X Decompressor"); - -#endif diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c new file mode 100644 index 0000000..f2fd098 --- /dev/null +++ b/lib/lzo/lzo1x_decompress_safe.c @@ -0,0 +1,255 @@ +/* + * LZO1X Decompressor from MiniLZO + * + * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer + * + * The full LZO package can be found at: + * http://www.oberhumer.com/opensource/lzo/ + * + * Changed for kernel use by: + * Nitin Gupta + * Richard Purdie + */ + +#ifndef STATIC +#include +#include +#endif + +#include +#include +#include "lzodefs.h" + +#define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x)) +#define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x)) +#define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op) + +#define COPY4(dst, src) \ + put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst)) + +int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, + unsigned char *out, size_t *out_len) +{ + const unsigned char * const ip_end = in + in_len; + unsigned char * const op_end = out + *out_len; + const unsigned char *ip = in, *m_pos; + unsigned char *op = out; + size_t t; + + *out_len = 0; + + if (*ip > 17) { + t = *ip++ - 17; + if (t < 4) + goto match_next; + if (HAVE_OP(t, op_end, op)) + goto output_overrun; + if (HAVE_IP(t + 1, ip_end, ip)) + goto input_overrun; + do { + *op++ = *ip++; + } while (--t > 0); + goto first_literal_run; + } + + while ((ip < ip_end)) { + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) { + if (HAVE_IP(1, ip_end, ip)) + goto input_overrun; + while (*ip == 0) { + t += 255; + ip++; + if (HAVE_IP(1, ip_end, ip)) + goto input_overrun; + } + t += 15 + *ip++; + } + if (HAVE_OP(t + 3, op_end, op)) + goto output_overrun; + if (HAVE_IP(t + 4, ip_end, ip)) + goto input_overrun; + + COPY4(op, ip); + op += 4; + ip += 4; + if (--t > 0) { + if (t >= 4) { + do { + COPY4(op, ip); + op += 4; + ip += 4; + t -= 4; + } while (t >= 4); + if (t > 0) { + do { + *op++ = *ip++; + } while (--t > 0); + } + } else { + do { + *op++ = *ip++; + } while (--t > 0); + } + } + +first_literal_run: + t = *ip++; + if (t >= 16) + goto match; + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + + if (HAVE_LB(m_pos, out, op)) + goto lookbehind_overrun; + + if (HAVE_OP(3, op_end, op)) + goto output_overrun; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos; + + goto match_done; + + do { +match: + if (t >= 64) { + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; + if (HAVE_LB(m_pos, out, op)) + goto lookbehind_overrun; + if (HAVE_OP(t + 3 - 1, op_end, op)) + goto output_overrun; + goto copy_match; + } else if (t >= 32) { + t &= 31; + if (t == 0) { + if (HAVE_IP(1, ip_end, ip)) + goto input_overrun; + while (*ip == 0) { + t += 255; + ip++; + if (HAVE_IP(1, ip_end, ip)) + goto input_overrun; + } + t += 31 + *ip++; + } + m_pos = op - 1; + m_pos -= get_unaligned_le16(ip) >> 2; + ip += 2; + } else if (t >= 16) { + m_pos = op; + m_pos -= (t & 8) << 11; + + t &= 7; + if (t == 0) { + if (HAVE_IP(1, ip_end, ip)) + goto input_overrun; + while (*ip == 0) { + t += 255; + ip++; + if (HAVE_IP(1, ip_end, ip)) + goto input_overrun; + } + t += 7 + *ip++; + } + m_pos -= get_unaligned_le16(ip) >> 2; + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } else { + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + + if (HAVE_LB(m_pos, out, op)) + goto lookbehind_overrun; + if (HAVE_OP(2, op_end, op)) + goto output_overrun; + + *op++ = *m_pos++; + *op++ = *m_pos; + goto match_done; + } + + if (HAVE_LB(m_pos, out, op)) + goto lookbehind_overrun; + if (HAVE_OP(t + 3 - 1, op_end, op)) + goto output_overrun; + + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { + COPY4(op, m_pos); + op += 4; + m_pos += 4; + t -= 4 - (3 - 1); + do { + COPY4(op, m_pos); + op += 4; + m_pos += 4; + t -= 4; + } while (t >= 4); + if (t > 0) + do { + *op++ = *m_pos++; + } while (--t > 0); + } else { +copy_match: + *op++ = *m_pos++; + *op++ = *m_pos++; + do { + *op++ = *m_pos++; + } while (--t > 0); + } +match_done: + t = ip[-2] & 3; + if (t == 0) + break; +match_next: + if (HAVE_OP(t, op_end, op)) + goto output_overrun; + if (HAVE_IP(t + 1, ip_end, ip)) + goto input_overrun; + + *op++ = *ip++; + if (t > 1) { + *op++ = *ip++; + if (t > 2) + *op++ = *ip++; + } + + t = *ip++; + } while (ip < ip_end); + } + + *out_len = op - out; + return LZO_E_EOF_NOT_FOUND; + +eof_found: + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); +input_overrun: + *out_len = op - out; + return LZO_E_INPUT_OVERRUN; + +output_overrun: + *out_len = op - out; + return LZO_E_OUTPUT_OVERRUN; + +lookbehind_overrun: + *out_len = op - out; + return LZO_E_LOOKBEHIND_OVERRUN; +} +#ifndef STATIC +EXPORT_SYMBOL_GPL(lzo1x_decompress_safe); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZO1X Decompressor"); + +#endif -- cgit v1.1 From 39a4e2420fe3fec2019dc3ad9ebfc6c4ae7869c3 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 13 Aug 2012 17:25:44 +0200 Subject: lib/lzo: Update LZO compression to current upstream version This commit updates the kernel LZO code to the current upsteam version which features a significant speed improvement - benchmarking the Calgary and Silesia test corpora typically shows a doubled performance in both compression and decompression on modern i386/x86_64/powerpc machines. Change-Id: Ifcde45460c856644097bdeb612f0d68c6429f03c Signed-off-by: Markus F.X.J. Oberhumer --- lib/lzo/lzo1x_compress.c | 335 ++++++++++++++++++++++---------------- lib/lzo/lzo1x_decompress_safe.c | 350 +++++++++++++++++++--------------------- lib/lzo/lzodefs.h | 38 +++-- 3 files changed, 387 insertions(+), 336 deletions(-) diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c index a604099..236eb21 100644 --- a/lib/lzo/lzo1x_compress.c +++ b/lib/lzo/lzo1x_compress.c @@ -1,194 +1,243 @@ /* - * LZO1X Compressor from MiniLZO + * LZO1X Compressor from LZO * - * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer + * Copyright (C) 1996-2012 Markus F.X.J. Oberhumer * * The full LZO package can be found at: * http://www.oberhumer.com/opensource/lzo/ * - * Changed for kernel use by: + * Changed for Linux kernel use by: * Nitin Gupta * Richard Purdie */ #include #include -#include #include +#include #include "lzodefs.h" static noinline size_t -_lzo1x_1_do_compress(const unsigned char *in, size_t in_len, - unsigned char *out, size_t *out_len, void *wrkmem) +lzo1x_1_do_compress(const unsigned char *in, size_t in_len, + unsigned char *out, size_t *out_len, + size_t ti, void *wrkmem) { + const unsigned char *ip; + unsigned char *op; const unsigned char * const in_end = in + in_len; - const unsigned char * const ip_end = in + in_len - M2_MAX_LEN - 5; - const unsigned char ** const dict = wrkmem; - const unsigned char *ip = in, *ii = ip; - const unsigned char *end, *m, *m_pos; - size_t m_off, m_len, dindex; - unsigned char *op = out; + const unsigned char * const ip_end = in + in_len - 20; + const unsigned char *ii; + lzo_dict_t * const dict = (lzo_dict_t *) wrkmem; - ip += 4; + op = out; + ip = in; + ii = ip; + ip += ti < 4 ? 4 - ti : 0; for (;;) { - dindex = ((size_t)(0x21 * DX3(ip, 5, 5, 6)) >> 5) & D_MASK; - m_pos = dict[dindex]; - - if (m_pos < in) - goto literal; - - if (ip == m_pos || ((size_t)(ip - m_pos) > M4_MAX_OFFSET)) - goto literal; - - m_off = ip - m_pos; - if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) - goto try_match; - - dindex = (dindex & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f); - m_pos = dict[dindex]; - - if (m_pos < in) - goto literal; - - if (ip == m_pos || ((size_t)(ip - m_pos) > M4_MAX_OFFSET)) - goto literal; - - m_off = ip - m_pos; - if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) - goto try_match; - - goto literal; - -try_match: - if (get_unaligned((const unsigned short *)m_pos) - == get_unaligned((const unsigned short *)ip)) { - if (likely(m_pos[2] == ip[2])) - goto match; - } - + const unsigned char *m_pos; + size_t t, m_len, m_off; + u32 dv; literal: - dict[dindex] = ip; - ++ip; + ip += 1 + ((ip - ii) >> 5); +next: if (unlikely(ip >= ip_end)) break; - continue; - -match: - dict[dindex] = ip; - if (ip != ii) { - size_t t = ip - ii; + dv = get_unaligned_le32(ip); + t = ((dv * 0x1824429d) >> (32 - D_BITS)) & D_MASK; + m_pos = in + dict[t]; + dict[t] = (lzo_dict_t) (ip - in); + if (unlikely(dv != get_unaligned_le32(m_pos))) + goto literal; + ii -= ti; + ti = 0; + t = ip - ii; + if (t != 0) { if (t <= 3) { op[-2] |= t; - } else if (t <= 18) { + COPY4(op, ii); + op += t; + } else if (t <= 16) { *op++ = (t - 3); + COPY8(op, ii); + COPY8(op + 8, ii + 8); + op += t; } else { - size_t tt = t - 18; - - *op++ = 0; - while (tt > 255) { - tt -= 255; + if (t <= 18) { + *op++ = (t - 3); + } else { + size_t tt = t - 18; *op++ = 0; + while (unlikely(tt > 255)) { + tt -= 255; + *op++ = 0; + } + *op++ = tt; } - *op++ = tt; + do { + COPY8(op, ii); + COPY8(op + 8, ii + 8); + op += 16; + ii += 16; + t -= 16; + } while (t >= 16); + if (t > 0) do { + *op++ = *ii++; + } while (--t > 0); } - do { - *op++ = *ii++; - } while (--t > 0); } - ip += 3; - if (m_pos[3] != *ip++ || m_pos[4] != *ip++ - || m_pos[5] != *ip++ || m_pos[6] != *ip++ - || m_pos[7] != *ip++ || m_pos[8] != *ip++) { - --ip; - m_len = ip - ii; + m_len = 4; + { +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ64) + u64 v; + v = get_unaligned((const u64 *) (ip + m_len)) ^ + get_unaligned((const u64 *) (m_pos + m_len)); + if (unlikely(v == 0)) { + do { + m_len += 8; + v = get_unaligned((const u64 *) (ip + m_len)) ^ + get_unaligned((const u64 *) (m_pos + m_len)); + if (unlikely(ip + m_len >= ip_end)) + goto m_len_done; + } while (v == 0); + } +# if defined(__LITTLE_ENDIAN) + m_len += (unsigned) __builtin_ctzll(v) / 8; +# elif defined(__BIG_ENDIAN) + m_len += (unsigned) __builtin_clzll(v) / 8; +# else +# error "missing endian definition" +# endif +#elif defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ32) + u32 v; + v = get_unaligned((const u32 *) (ip + m_len)) ^ + get_unaligned((const u32 *) (m_pos + m_len)); + if (unlikely(v == 0)) { + do { + m_len += 4; + v = get_unaligned((const u32 *) (ip + m_len)) ^ + get_unaligned((const u32 *) (m_pos + m_len)); + if (v != 0) + break; + m_len += 4; + v = get_unaligned((const u32 *) (ip + m_len)) ^ + get_unaligned((const u32 *) (m_pos + m_len)); + if (unlikely(ip + m_len >= ip_end)) + goto m_len_done; + } while (v == 0); + } +# if defined(__LITTLE_ENDIAN) + m_len += (unsigned) __builtin_ctz(v) / 8; +# elif defined(__BIG_ENDIAN) + m_len += (unsigned) __builtin_clz(v) / 8; +# else +# error "missing endian definition" +# endif +#else + if (unlikely(ip[m_len] == m_pos[m_len])) { + do { + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (ip[m_len] != m_pos[m_len]) + break; + m_len += 1; + if (unlikely(ip + m_len >= ip_end)) + goto m_len_done; + } while (ip[m_len] == m_pos[m_len]); + } +#endif + } +m_len_done: - if (m_off <= M2_MAX_OFFSET) { - m_off -= 1; - *op++ = (((m_len - 1) << 5) - | ((m_off & 7) << 2)); - *op++ = (m_off >> 3); - } else if (m_off <= M3_MAX_OFFSET) { - m_off -= 1; + m_off = ip - m_pos; + ip += m_len; + ii = ip; + if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) { + m_off -= 1; + *op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = (m_off >> 3); + } else if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= M3_MAX_LEN) *op++ = (M3_MARKER | (m_len - 2)); - goto m3_m4_offset; - } else { - m_off -= 0x4000; - - *op++ = (M4_MARKER | ((m_off & 0x4000) >> 11) - | (m_len - 2)); - goto m3_m4_offset; + else { + m_len -= M3_MAX_LEN; + *op++ = M3_MARKER | 0; + while (unlikely(m_len > 255)) { + m_len -= 255; + *op++ = 0; + } + *op++ = (m_len); } + *op++ = (m_off << 2); + *op++ = (m_off >> 6); } else { - end = in_end; - m = m_pos + M2_MAX_LEN + 1; - - while (ip < end && *m == *ip) { - m++; - ip++; - } - m_len = ip - ii; - - if (m_off <= M3_MAX_OFFSET) { - m_off -= 1; - if (m_len <= 33) { - *op++ = (M3_MARKER | (m_len - 2)); - } else { - m_len -= 33; - *op++ = M3_MARKER | 0; - goto m3_m4_len; - } - } else { - m_off -= 0x4000; - if (m_len <= M4_MAX_LEN) { - *op++ = (M4_MARKER - | ((m_off & 0x4000) >> 11) + m_off -= 0x4000; + if (m_len <= M4_MAX_LEN) + *op++ = (M4_MARKER | ((m_off >> 11) & 8) | (m_len - 2)); - } else { - m_len -= M4_MAX_LEN; - *op++ = (M4_MARKER - | ((m_off & 0x4000) >> 11)); -m3_m4_len: - while (m_len > 255) { - m_len -= 255; - *op++ = 0; - } - - *op++ = (m_len); + else { + m_len -= M4_MAX_LEN; + *op++ = (M4_MARKER | ((m_off >> 11) & 8)); + while (unlikely(m_len > 255)) { + m_len -= 255; + *op++ = 0; } + *op++ = (m_len); } -m3_m4_offset: - *op++ = ((m_off & 63) << 2); + *op++ = (m_off << 2); *op++ = (m_off >> 6); } - - ii = ip; - if (unlikely(ip >= ip_end)) - break; + goto next; } - *out_len = op - out; - return in_end - ii; + return in_end - (ii - ti); } -int lzo1x_1_compress(const unsigned char *in, size_t in_len, unsigned char *out, - size_t *out_len, void *wrkmem) +int lzo1x_1_compress(const unsigned char *in, size_t in_len, + unsigned char *out, size_t *out_len, + void *wrkmem) { - const unsigned char *ii; + const unsigned char *ip = in; unsigned char *op = out; - size_t t; + size_t l = in_len; + size_t t = 0; - if (unlikely(in_len <= M2_MAX_LEN + 5)) { - t = in_len; - } else { - t = _lzo1x_1_do_compress(in, in_len, op, out_len, wrkmem); + while (l > 20) { + size_t ll = l <= (M4_MAX_OFFSET + 1) ? l : (M4_MAX_OFFSET + 1); + uintptr_t ll_end = (uintptr_t) ip + ll; + if ((ll_end + ((t + ll) >> 5)) <= ll_end) + break; + BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS); + memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t)); + t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem); + ip += ll; op += *out_len; + l -= ll; } + t += l; if (t > 0) { - ii = in + in_len - t; + const unsigned char *ii = in + in_len - t; if (op == out && t <= 238) { *op++ = (17 + t); @@ -198,16 +247,21 @@ int lzo1x_1_compress(const unsigned char *in, size_t in_len, unsigned char *out, *op++ = (t - 3); } else { size_t tt = t - 18; - *op++ = 0; while (tt > 255) { tt -= 255; *op++ = 0; } - *op++ = tt; } - do { + if (t >= 16) do { + COPY8(op, ii); + COPY8(op + 8, ii + 8); + op += 16; + ii += 16; + t -= 16; + } while (t >= 16); + if (t > 0) do { *op++ = *ii++; } while (--t > 0); } @@ -223,4 +277,3 @@ EXPORT_SYMBOL_GPL(lzo1x_1_compress); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("LZO1X-1 Compressor"); - diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c index f2fd098..569985d 100644 --- a/lib/lzo/lzo1x_decompress_safe.c +++ b/lib/lzo/lzo1x_decompress_safe.c @@ -1,12 +1,12 @@ /* - * LZO1X Decompressor from MiniLZO + * LZO1X Decompressor from LZO * - * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer + * Copyright (C) 1996-2012 Markus F.X.J. Oberhumer * * The full LZO package can be found at: * http://www.oberhumer.com/opensource/lzo/ * - * Changed for kernel use by: + * Changed for Linux kernel use by: * Nitin Gupta * Richard Purdie */ @@ -15,225 +15,207 @@ #include #include #endif - #include #include #include "lzodefs.h" -#define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x)) -#define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x)) -#define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op) - -#define COPY4(dst, src) \ - put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst)) +#define HAVE_IP(x) ((size_t)(ip_end - ip) >= (size_t)(x)) +#define HAVE_OP(x) ((size_t)(op_end - op) >= (size_t)(x)) +#define NEED_IP(x) if (!HAVE_IP(x)) goto input_overrun +#define NEED_OP(x) if (!HAVE_OP(x)) goto output_overrun +#define TEST_LB(m_pos) if ((m_pos) < out) goto lookbehind_overrun int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, - unsigned char *out, size_t *out_len) + unsigned char *out, size_t *out_len) { + unsigned char *op; + const unsigned char *ip; + size_t t, next; + size_t state = 0; + const unsigned char *m_pos; const unsigned char * const ip_end = in + in_len; unsigned char * const op_end = out + *out_len; - const unsigned char *ip = in, *m_pos; - unsigned char *op = out; - size_t t; - *out_len = 0; + op = out; + ip = in; + if (unlikely(in_len < 3)) + goto input_overrun; if (*ip > 17) { t = *ip++ - 17; - if (t < 4) + if (t < 4) { + next = t; goto match_next; - if (HAVE_OP(t, op_end, op)) - goto output_overrun; - if (HAVE_IP(t + 1, ip_end, ip)) - goto input_overrun; - do { - *op++ = *ip++; - } while (--t > 0); - goto first_literal_run; - } - - while ((ip < ip_end)) { - t = *ip++; - if (t >= 16) - goto match; - if (t == 0) { - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - while (*ip == 0) { - t += 255; - ip++; - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - } - t += 15 + *ip++; - } - if (HAVE_OP(t + 3, op_end, op)) - goto output_overrun; - if (HAVE_IP(t + 4, ip_end, ip)) - goto input_overrun; - - COPY4(op, ip); - op += 4; - ip += 4; - if (--t > 0) { - if (t >= 4) { - do { - COPY4(op, ip); - op += 4; - ip += 4; - t -= 4; - } while (t >= 4); - if (t > 0) { - do { - *op++ = *ip++; - } while (--t > 0); - } - } else { - do { - *op++ = *ip++; - } while (--t > 0); - } } + goto copy_literal_run; + } -first_literal_run: + for (;;) { t = *ip++; - if (t >= 16) - goto match; - m_pos = op - (1 + M2_MAX_OFFSET); - m_pos -= t >> 2; - m_pos -= *ip++ << 2; - - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - - if (HAVE_OP(3, op_end, op)) - goto output_overrun; - *op++ = *m_pos++; - *op++ = *m_pos++; - *op++ = *m_pos; - - goto match_done; - - do { -match: - if (t >= 64) { - m_pos = op - 1; - m_pos -= (t >> 2) & 7; - m_pos -= *ip++ << 3; - t = (t >> 5) - 1; - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - if (HAVE_OP(t + 3 - 1, op_end, op)) - goto output_overrun; - goto copy_match; - } else if (t >= 32) { - t &= 31; - if (t == 0) { - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - while (*ip == 0) { + if (t < 16) { + if (likely(state == 0)) { + if (unlikely(t == 0)) { + while (unlikely(*ip == 0)) { t += 255; ip++; - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; + NEED_IP(1); } - t += 31 + *ip++; + t += 15 + *ip++; } - m_pos = op - 1; - m_pos -= get_unaligned_le16(ip) >> 2; - ip += 2; - } else if (t >= 16) { - m_pos = op; - m_pos -= (t & 8) << 11; - - t &= 7; - if (t == 0) { - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - while (*ip == 0) { - t += 255; - ip++; - if (HAVE_IP(1, ip_end, ip)) - goto input_overrun; - } - t += 7 + *ip++; + t += 3; +copy_literal_run: +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) { + const unsigned char *ie = ip + t; + unsigned char *oe = op + t; + do { + COPY8(op, ip); + op += 8; + ip += 8; + COPY8(op, ip); + op += 8; + ip += 8; + } while (ip < ie); + ip = ie; + op = oe; + } else +#endif + { + NEED_OP(t); + NEED_IP(t + 3); + do { + *op++ = *ip++; + } while (--t > 0); } - m_pos -= get_unaligned_le16(ip) >> 2; - ip += 2; - if (m_pos == op) - goto eof_found; - m_pos -= 0x4000; - } else { + state = 4; + continue; + } else if (state != 4) { + next = t & 3; m_pos = op - 1; m_pos -= t >> 2; m_pos -= *ip++ << 2; - - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - if (HAVE_OP(2, op_end, op)) - goto output_overrun; - - *op++ = *m_pos++; - *op++ = *m_pos; - goto match_done; + TEST_LB(m_pos); + NEED_OP(2); + op[0] = m_pos[0]; + op[1] = m_pos[1]; + op += 2; + goto match_next; + } else { + next = t & 3; + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + t = 3; } - - if (HAVE_LB(m_pos, out, op)) - goto lookbehind_overrun; - if (HAVE_OP(t + 3 - 1, op_end, op)) - goto output_overrun; - - if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { - COPY4(op, m_pos); - op += 4; - m_pos += 4; - t -= 4 - (3 - 1); + } else if (t >= 64) { + next = t & 3; + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1 + (3 - 1); + } else if (t >= 32) { + t = (t & 31) + (3 - 1); + if (unlikely(t == 2)) { + while (unlikely(*ip == 0)) { + t += 255; + ip++; + NEED_IP(1); + } + t += 31 + *ip++; + NEED_IP(2); + } + m_pos = op - 1; + next = get_unaligned_le16(ip); + ip += 2; + m_pos -= next >> 2; + next &= 3; + } else { + m_pos = op; + m_pos -= (t & 8) << 11; + t = (t & 7) + (3 - 1); + if (unlikely(t == 2)) { + while (unlikely(*ip == 0)) { + t += 255; + ip++; + NEED_IP(1); + } + t += 7 + *ip++; + NEED_IP(2); + } + next = get_unaligned_le16(ip); + ip += 2; + m_pos -= next >> 2; + next &= 3; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + TEST_LB(m_pos); +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + if (op - m_pos >= 8) { + unsigned char *oe = op + t; + if (likely(HAVE_OP(t + 15))) { do { - COPY4(op, m_pos); - op += 4; - m_pos += 4; - t -= 4; - } while (t >= 4); - if (t > 0) - do { - *op++ = *m_pos++; - } while (--t > 0); + COPY8(op, m_pos); + op += 8; + m_pos += 8; + COPY8(op, m_pos); + op += 8; + m_pos += 8; + } while (op < oe); + op = oe; + if (HAVE_IP(6)) { + state = next; + COPY4(op, ip); + op += next; + ip += next; + continue; + } } else { -copy_match: - *op++ = *m_pos++; - *op++ = *m_pos++; + NEED_OP(t); do { *op++ = *m_pos++; - } while (--t > 0); + } while (op < oe); } -match_done: - t = ip[-2] & 3; - if (t == 0) - break; + } else +#endif + { + unsigned char *oe = op + t; + NEED_OP(t); + op[0] = m_pos[0]; + op[1] = m_pos[1]; + op += 2; + m_pos += 2; + do { + *op++ = *m_pos++; + } while (op < oe); + } match_next: - if (HAVE_OP(t, op_end, op)) - goto output_overrun; - if (HAVE_IP(t + 1, ip_end, ip)) - goto input_overrun; - - *op++ = *ip++; - if (t > 1) { + state = next; + t = next; +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + if (likely(HAVE_IP(6) && HAVE_OP(4))) { + COPY4(op, ip); + op += t; + ip += t; + } else +#endif + { + NEED_IP(t + 3); + NEED_OP(t); + while (t > 0) { *op++ = *ip++; - if (t > 2) - *op++ = *ip++; + t--; } - - t = *ip++; - } while (ip < ip_end); + } } - *out_len = op - out; - return LZO_E_EOF_NOT_FOUND; - eof_found: *out_len = op - out; - return (ip == ip_end ? LZO_E_OK : - (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + return (t != 3 ? LZO_E_ERROR : + ip == ip_end ? LZO_E_OK : + ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN); + input_overrun: *out_len = op - out; return LZO_E_INPUT_OVERRUN; diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h index b6d482c..6710b83 100644 --- a/lib/lzo/lzodefs.h +++ b/lib/lzo/lzodefs.h @@ -1,19 +1,37 @@ /* * lzodefs.h -- architecture, OS and compiler specific defines * - * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer + * Copyright (C) 1996-2012 Markus F.X.J. Oberhumer * * The full LZO package can be found at: * http://www.oberhumer.com/opensource/lzo/ * - * Changed for kernel use by: + * Changed for Linux kernel use by: * Nitin Gupta * Richard Purdie */ -#define LZO_VERSION 0x2020 -#define LZO_VERSION_STRING "2.02" -#define LZO_VERSION_DATE "Oct 17 2005" + +#define COPY4(dst, src) \ + put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst)) +#if defined(__x86_64__) +#define COPY8(dst, src) \ + put_unaligned(get_unaligned((const u64 *)(src)), (u64 *)(dst)) +#else +#define COPY8(dst, src) \ + COPY4(dst, src); COPY4((dst) + 4, (src) + 4) +#endif + +#if defined(__BIG_ENDIAN) && defined(__LITTLE_ENDIAN) +#error "conflicting endian definitions" +#elif defined(__x86_64__) +#define LZO_USE_CTZ64 1 +#define LZO_USE_CTZ32 1 +#elif defined(__i386__) || defined(__powerpc__) +#define LZO_USE_CTZ32 1 +#elif defined(__arm__) && (__LINUX_ARM_ARCH__ >= 5) +#define LZO_USE_CTZ32 1 +#endif #define M1_MAX_OFFSET 0x0400 #define M2_MAX_OFFSET 0x0800 @@ -34,10 +52,8 @@ #define M3_MARKER 32 #define M4_MARKER 16 -#define D_BITS 14 -#define D_MASK ((1u << D_BITS) - 1) +#define lzo_dict_t unsigned short +#define D_BITS 13 +#define D_SIZE (1u << D_BITS) +#define D_MASK (D_SIZE - 1) #define D_HIGH ((D_MASK >> 1) + 1) - -#define DX2(p, s1, s2) (((((size_t)((p)[2]) << (s2)) ^ (p)[1]) \ - << (s1)) ^ (p)[0]) -#define DX3(p, s1, s2, s3) ((DX2((p)+1, s2, s3) << (s1)) ^ (p)[0]) -- cgit v1.1 From 579b11bb139ae8ab650f2bb6d6c0fc71bec529cf Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 4 Feb 2013 02:26:14 +0100 Subject: lib/lzo: huge LZO decompression speedup on ARM by using unaligned access Change-Id: I4dc172b2822504c3f2db1913ed9404e031861d55 Signed-off-by: Markus F.X.J. Oberhumer --- lib/lzo/lzo1x_decompress_safe.c | 4 ++++ lib/lzo/lzodefs.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c index 569985d..e3edc5f 100644 --- a/lib/lzo/lzo1x_decompress_safe.c +++ b/lib/lzo/lzo1x_decompress_safe.c @@ -72,9 +72,11 @@ copy_literal_run: COPY8(op, ip); op += 8; ip += 8; +# if !defined(__arm__) COPY8(op, ip); op += 8; ip += 8; +# endif } while (ip < ie); ip = ie; op = oe; @@ -159,9 +161,11 @@ copy_literal_run: COPY8(op, m_pos); op += 8; m_pos += 8; +# if !defined(__arm__) COPY8(op, m_pos); op += 8; m_pos += 8; +# endif } while (op < oe); op = oe; if (HAVE_IP(6)) { diff --git a/lib/lzo/lzodefs.h b/lib/lzo/lzodefs.h index 6710b83..db756cc 100644 --- a/lib/lzo/lzodefs.h +++ b/lib/lzo/lzodefs.h @@ -12,8 +12,14 @@ */ +#if 1 && defined(__arm__) && ((__LINUX_ARM_ARCH__ >= 6) || defined(__ARM_FEATURE_UNALIGNED)) +#define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 +#define COPY4(dst, src) \ + * (u32 *) (void *) (dst) = * (const u32 *) (const void *) (src) +#else #define COPY4(dst, src) \ put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst)) +#endif #if defined(__x86_64__) #define COPY8(dst, src) \ put_unaligned(get_unaligned((const u64 *)(src)), (u64 *)(dst)) -- cgit v1.1 From 912b213927c79feb8254549b13eba41870d56081 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 20 Jun 2014 22:00:53 -0700 Subject: lzo: properly check for overruns commit 206a81c18401c0cde6e579164f752c4b147324ce upstream. The lzo decompressor can, if given some really crazy data, possibly overrun some variable types. Modify the checking logic to properly detect overruns before they happen. Change-Id: I13c97fd70481d4d272beb8ba495a8be3c4b48cf2 Reported-by: "Don A. Bailey" Tested-by: "Don A. Bailey" Signed-off-by: Greg Kroah-Hartman --- lib/lzo/lzo1x_decompress_safe.c | 62 +++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c index e3edc5f..a5f3d0e 100644 --- a/lib/lzo/lzo1x_decompress_safe.c +++ b/lib/lzo/lzo1x_decompress_safe.c @@ -19,11 +19,31 @@ #include #include "lzodefs.h" -#define HAVE_IP(x) ((size_t)(ip_end - ip) >= (size_t)(x)) -#define HAVE_OP(x) ((size_t)(op_end - op) >= (size_t)(x)) -#define NEED_IP(x) if (!HAVE_IP(x)) goto input_overrun -#define NEED_OP(x) if (!HAVE_OP(x)) goto output_overrun -#define TEST_LB(m_pos) if ((m_pos) < out) goto lookbehind_overrun +#define HAVE_IP(t, x) \ + (((size_t)(ip_end - ip) >= (size_t)(t + x)) && \ + (((t + x) >= t) && ((t + x) >= x))) + +#define HAVE_OP(t, x) \ + (((size_t)(op_end - op) >= (size_t)(t + x)) && \ + (((t + x) >= t) && ((t + x) >= x))) + +#define NEED_IP(t, x) \ + do { \ + if (!HAVE_IP(t, x)) \ + goto input_overrun; \ + } while (0) + +#define NEED_OP(t, x) \ + do { \ + if (!HAVE_OP(t, x)) \ + goto output_overrun; \ + } while (0) + +#define TEST_LB(m_pos) \ + do { \ + if ((m_pos) < out) \ + goto lookbehind_overrun; \ + } while (0) int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, unsigned char *out, size_t *out_len) @@ -58,14 +78,14 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, while (unlikely(*ip == 0)) { t += 255; ip++; - NEED_IP(1); + NEED_IP(1, 0); } t += 15 + *ip++; } t += 3; copy_literal_run: #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) - if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) { + if (likely(HAVE_IP(t, 15) && HAVE_OP(t, 15))) { const unsigned char *ie = ip + t; unsigned char *oe = op + t; do { @@ -83,8 +103,8 @@ copy_literal_run: } else #endif { - NEED_OP(t); - NEED_IP(t + 3); + NEED_OP(t, 0); + NEED_IP(t, 3); do { *op++ = *ip++; } while (--t > 0); @@ -97,7 +117,7 @@ copy_literal_run: m_pos -= t >> 2; m_pos -= *ip++ << 2; TEST_LB(m_pos); - NEED_OP(2); + NEED_OP(2, 0); op[0] = m_pos[0]; op[1] = m_pos[1]; op += 2; @@ -121,10 +141,10 @@ copy_literal_run: while (unlikely(*ip == 0)) { t += 255; ip++; - NEED_IP(1); + NEED_IP(1, 0); } t += 31 + *ip++; - NEED_IP(2); + NEED_IP(2, 0); } m_pos = op - 1; next = get_unaligned_le16(ip); @@ -139,10 +159,10 @@ copy_literal_run: while (unlikely(*ip == 0)) { t += 255; ip++; - NEED_IP(1); + NEED_IP(1, 0); } t += 7 + *ip++; - NEED_IP(2); + NEED_IP(2, 0); } next = get_unaligned_le16(ip); ip += 2; @@ -156,7 +176,7 @@ copy_literal_run: #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) if (op - m_pos >= 8) { unsigned char *oe = op + t; - if (likely(HAVE_OP(t + 15))) { + if (likely(HAVE_OP(t, 15))) { do { COPY8(op, m_pos); op += 8; @@ -168,7 +188,7 @@ copy_literal_run: # endif } while (op < oe); op = oe; - if (HAVE_IP(6)) { + if (HAVE_IP(6, 0)) { state = next; COPY4(op, ip); op += next; @@ -176,7 +196,7 @@ copy_literal_run: continue; } } else { - NEED_OP(t); + NEED_OP(t, 0); do { *op++ = *m_pos++; } while (op < oe); @@ -185,7 +205,7 @@ copy_literal_run: #endif { unsigned char *oe = op + t; - NEED_OP(t); + NEED_OP(t, 0); op[0] = m_pos[0]; op[1] = m_pos[1]; op += 2; @@ -198,15 +218,15 @@ match_next: state = next; t = next; #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) - if (likely(HAVE_IP(6) && HAVE_OP(4))) { + if (likely(HAVE_IP(6, 0) && HAVE_OP(4, 0))) { COPY4(op, ip); op += t; ip += t; } else #endif { - NEED_IP(t + 3); - NEED_OP(t); + NEED_IP(t, 3); + NEED_OP(t, 0); while (t > 0) { *op++ = *ip++; t--; -- cgit v1.1 From 128faec612bd65b7c7b5318c47d35f4cabbc3060 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Wed, 29 Aug 2012 16:58:45 -0500 Subject: staging: zcache: fix cleancache race condition with shrinker commit 6d7d9798ad5c97ee4e911dd070dc12dc5ae55bd0 upstream. This patch fixes a race condition that results in memory corruption when using cleancache. The race exists between the zcache shrinker handler, shrink_zcache_memory() and cleancache_get_page(). In most cases, the shrinker will both evict a zbpg from its buddy list and flush it from tmem before a cleancache_get_page() occurs on that page. A subsequent cleancache_get_page() will fail in the tmem layer. In the rare case that two occur together and the cleancache_get_page() path gets through the tmem layer before the shrinker path can flush tmem, zbud_decompress() does a check to see if the zbpg is a "zombie", i.e. not on a buddy list, which means the shrinker is in the process of reclaiming it. If the zbpg is a zombie, zbud_decompress() returns -EINVAL. However, this return code is being ignored by the caller, zcache_pampd_get_data_and_free(), which results in the caller of cleancache_get_page() thinking that the page has been properly retrieved when it has not. This patch modifies zcache_pampd_get_data_and_free() to convey the failure up the stack so that the caller of cleancache_get_page() knows the page retrieval failed. This needs to be applied to stable trees as well. zcache-main.c was named zcache.c before v3.1, so I'm not sure how you want to handle trees earlier than that. Change-Id: I618a2488d788c15b3e8d74d2831cc5d83ca71abc Signed-off-by: Seth Jennings Reviewed-by: Konrad Rzeszutek Wilk Reviewed-by: Minchan Kim Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zcache/zcache-main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index d781bd0..61cce6b 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -1259,13 +1259,12 @@ static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw, void *pampd, struct tmem_pool *pool, struct tmem_oid *oid, uint32_t index) { - int ret = 0; - BUG_ON(!is_ephemeral(pool)); - zbud_decompress((struct page *)(data), pampd); + if (zbud_decompress((struct page *)(data), pampd) < 0) + return -EINVAL; zbud_free_and_delist((struct zbud_hdr *)pampd); atomic_dec(&zcache_curr_eph_pampd_count); - return ret; + return 0; } /* -- cgit v1.1 From c72e09b31537cd8c558f70fd00d40c755e670114 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 25 Sep 2013 18:57:43 -0500 Subject: mm: remove compressed copy from zram in-memory Swap subsystem does lazy swap slot free with expecting the page would be swapped out again so we can avoid unnecessary write. But the problem in in-memory swap(ex, zram) is that it consumes memory space until vm_swap_full(ie, used half of all of swap device) condition meet. It could be bad if we use multiple swap device, small in-memory swap and big storage swap or in-memory swap alone. This patch makes swap subsystem free swap slot as soon as swap-read is completed and make the swapcache page dirty so the page should be written out the swap device to reclaim it. It means we never lose it. I tested this patch with kernel compile workload. 1. before compile time : 9882.42 zram max wasted space by fragmentation: 13471881 byte memory space consumed by zram: 174227456 byte the number of slot free notify: 206684 2. after compile time : 9653.90 zram max wasted space by fragmentation: 11805932 byte memory space consumed by zram: 154001408 byte the number of slot free notify: 426972 [akpm@linux-foundation.org: tweak comment text] [artem.savkov@gmail.com: fix BUG due to non-swapcache pages in end_swap_bio_read()] [akpm@linux-foundation.org: invert unlikely() test, augment comment, 80-col cleanup] Signed-off-by: Dan Magenheimer Signed-off-by: Minchan Kim Signed-off-by: Artem Savkov Cc: Hugh Dickins Cc: Seth Jennings Cc: Nitin Gupta Cc: Konrad Rzeszutek Wilk Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Change-Id: I8f11fb7f0391954435002e7f290352f311c907d4 Signed-off-by: Paul Reioux Conflicts: mm/page_io.c --- mm/page_io.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/mm/page_io.c b/mm/page_io.c index dc76b4d..f57da45 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include static struct bio *get_swap_bio(gfp_t gfp_flags, @@ -78,9 +80,49 @@ void end_swap_bio_read(struct bio *bio, int err) imajor(bio->bi_bdev->bd_inode), iminor(bio->bi_bdev->bd_inode), (unsigned long long)bio->bi_sector); - } else { - SetPageUptodate(page); + goto out; + } + + SetPageUptodate(page); + + /* + * There is no guarantee that the page is in swap cache - the software + * suspend code (at least) uses end_swap_bio_read() against a non- + * swapcache page. So we must check PG_swapcache before proceeding with + * this optimization. + */ + if (likely(PageSwapCache(page))) { + /* + * The swap subsystem performs lazy swap slot freeing, + * expecting that the page will be swapped out again. + * So we can avoid an unnecessary write if the page + * isn't redirtied. + * This is good for real swap storage because we can + * reduce unnecessary I/O and enhance wear-leveling + * if an SSD is used as the as swap device. + * But if in-memory swap device (eg zram) is used, + * this causes a duplicated copy between uncompressed + * data in VM-owned memory and compressed data in + * zram-owned memory. So let's free zram-owned memory + * and make the VM-owned decompressed page *dirty*, + * so the page should be swapped out somewhere again if + * we again wish to reclaim it. + */ + struct gendisk *disk = bio->bi_bdev->bd_disk; + if (disk->fops->swap_slot_free_notify) { + swp_entry_t entry; + unsigned long offset; + + entry.val = page_private(page); + offset = swp_offset(entry); + + SetPageDirty(page); + disk->fops->swap_slot_free_notify(bio->bi_bdev, + offset); + } } + +out: unlock_page(page); bio_put(bio); } -- cgit v1.1 From 1e291c9a70e255278ddb451097f0d65126556b61 Mon Sep 17 00:00:00 2001 From: Dan Magenheimer Date: Wed, 25 Jan 2012 16:58:46 -0800 Subject: mm: implement WasActive page flag (for improving cleancache) (Feedback welcome if there is a different/better way to do this without using a page flag!) Since about 2.6.27, the page replacement algorithm maintains an "active" bit to help decide which pages are most eligible to reclaim, see http://linux-mm.org/PageReplacementDesign This "active' information is also useful to cleancache but is lost by the time that cleancache has the opportunity to preserve the pageful of data. This patch adds a new page flag "WasActive" to retain the state. The flag may possibly be useful elsewhere. It is up to each cleancache backend to utilize the bit as it desires. The matching patch for zcache is included here for clarification/discussion purposes, though it will need to go through GregKH and the staging tree. The patch resolves issues reported with cleancache which occur especially during streaming workloads on older processors, see https://lkml.org/lkml/2011/8/17/351 Signed-off-by: Dan Magenheimer Conflicts: include/linux/page-flags.h Change-Id: I0fcb2302a7b9c5e66db005229f679baee90f262f Conflicts: include/linux/page-flags.h --- drivers/staging/zcache/zcache-main.c | 4 ++++ include/linux/page-flags.h | 10 ++++++++++ mm/vmscan.c | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 61cce6b..eb65043 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -1748,6 +1748,8 @@ static void zcache_cleancache_put_page(int pool_id, u32 ind = (u32) index; struct tmem_oid oid = *(struct tmem_oid *)&key; + if (!PageWasActive(page)) + return; if (likely(ind == index)) (void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index, page); } @@ -1762,6 +1764,8 @@ static int zcache_cleancache_get_page(int pool_id, if (likely(ind == index)) ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index, page); + if (ret == 0) + SetPageWasActive(page); return ret; } diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 87a0009..657ba2c 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -51,6 +51,9 @@ * PG_hwpoison indicates that a page got corrupted in hardware and contains * data with incorrect ECC bits that triggered a machine check. Accessing is * not safe since it may cause another machine check. Don't touch! + * + * PG_wasactive reflects that a page previously was promoted to active status. + * Such pages should be considered higher priority for cleancache backends. */ /* @@ -107,6 +110,9 @@ enum pageflags { #ifdef CONFIG_TRANSPARENT_HUGEPAGE PG_compound_lock, #endif +#ifdef CONFIG_CLEANCACHE + PG_was_active, +#endif __NR_PAGEFLAGS, /* Filesystems */ @@ -264,6 +270,10 @@ __PAGEFLAG(SlobFree, slob_free) __PAGEFLAG(SlubFrozen, slub_frozen) +#ifdef CONFIG_CLEANCACHE +PAGEFLAG(WasActive, was_active) +#endif + /* * Private page markings that may be used by the filesystem that owns the page * for its own purposes. diff --git a/mm/vmscan.c b/mm/vmscan.c index 9b72c26..c11955c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -624,6 +624,8 @@ void putback_lru_page(struct page *page) int was_unevictable = PageUnevictable(page); VM_BUG_ON(PageLRU(page)); + if (active) + SetPageWasActive(page); redo: ClearPageUnevictable(page); @@ -1289,6 +1291,7 @@ unsigned long clear_active_flags(struct list_head *page_list, if (PageActive(page)) { lru += LRU_ACTIVE; ClearPageActive(page); + SetPageWasActive(page); nr_active += numpages; } if (count) @@ -1710,6 +1713,7 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone, } ClearPageActive(page); /* we are de-activating */ + SetPageWasActive(page); list_add(&page->lru, &l_inactive); } -- cgit v1.1 From d6cc98c629a36ddd0c44cac326082034e73a09c0 Mon Sep 17 00:00:00 2001 From: Paul Reioux Date: Thu, 26 Jan 2012 14:57:57 -0800 Subject: mm/vmscan: Add #ifdef around "WasActive page flag" also fix a merge error @ putback_lru_page --- mm/vmscan.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/vmscan.c b/mm/vmscan.c index c11955c..0d1eb7e 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -624,8 +624,10 @@ void putback_lru_page(struct page *page) int was_unevictable = PageUnevictable(page); VM_BUG_ON(PageLRU(page)); +#ifdef CONFIG_CLEANCACHE if (active) SetPageWasActive(page); +#endif redo: ClearPageUnevictable(page); @@ -1291,7 +1293,9 @@ unsigned long clear_active_flags(struct list_head *page_list, if (PageActive(page)) { lru += LRU_ACTIVE; ClearPageActive(page); +#ifdef CONFIG_CLEANCACHE SetPageWasActive(page); +#endif nr_active += numpages; } if (count) @@ -1713,7 +1717,9 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone, } ClearPageActive(page); /* we are de-activating */ +#ifdef CONFIG_CLEANCACHE SetPageWasActive(page); +#endif list_add(&page->lru, &l_inactive); } -- cgit v1.1 From 9d2e392cc54a3504b378dde116195ee0273a917b Mon Sep 17 00:00:00 2001 From: Ziyan Date: Fri, 8 Jan 2016 12:29:44 +0100 Subject: zram: default to LZ4 compression if LZ4 is enabled Change-Id: I4ac454ceb60f960c30c9719d0aa63e4bda7dfbd5 --- drivers/block/zram/zram_drv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 7040165..a4eb55d 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -40,7 +40,11 @@ /* Globals */ static int zram_major; static struct zram *zram_devices; +#ifdef CONFIG_ZRAM_LZ4_COMPRESS +static const char *default_compressor = "lz4"; +#else static const char *default_compressor = "lzo"; +#endif /* * We don't need to see memory allocation errors more than once every 1 -- cgit v1.1 From 27173842294abf228b58ccc011025cacb7639c8d Mon Sep 17 00:00:00 2001 From: Ziyan Date: Fri, 8 Jan 2016 20:47:42 +0100 Subject: i9300: enable LZ4-backed zRAM Change-Id: I35a2c7fc74173986ef1b5bb92066e926cf8b8a3f --- arch/arm/configs/cyanogenmod_i9300_defconfig | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 3d793b4..015d300 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -108,6 +108,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_CC_CHECK_WARNING_STRICTLY is not set CONFIG_SYSCTL=y @@ -691,6 +692,8 @@ CONFIG_VIRT_TO_BUS=y CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y @@ -1243,6 +1246,9 @@ CONFIG_CMA_AREAS=7 # CONFIG_MTD is not set # CONFIG_PARPORT is not set CONFIG_BLK_DEV=y +CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y +# CONFIG_ZRAM_DEBUG is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -1326,10 +1332,13 @@ CONFIG_SEC_MODEM=y CONFIG_UMTS_MODEM_XMM6262=y # CONFIG_CDMA_MODEM_CBP71 is not set # CONFIG_CDMA_MODEM_CBP72 is not set +# CONFIG_CDMA_MODEM_CBP82 is not set # CONFIG_LTE_MODEM_CMC221 is not set +# CONFIG_UMTS_MODEM_SS222 is not set # CONFIG_CDMA_MODEM_MDM6600 is not set # CONFIG_TDSCDMA_MODEM_SPRD8803 is not set # CONFIG_GSM_MODEM_ESC6270 is not set +# CONFIG_CDMA_MODEM_QSC6085 is not set # CONFIG_LINK_DEVICE_MIPI is not set # CONFIG_LINK_DEVICE_DPRAM is not set # CONFIG_LINK_DEVICE_PLD is not set @@ -1337,6 +1346,7 @@ CONFIG_UMTS_MODEM_XMM6262=y CONFIG_LINK_DEVICE_HSIC=y # CONFIG_LINK_DEVICE_C2C is not set # CONFIG_LINK_DEVICE_SPI is not set +# CONFIG_BOOT_DEVICE_SPI is not set # CONFIG_WORKQUEUE_FRONT is not set # CONFIG_IPC_CMC22x_OLD_RFS is not set # CONFIG_SIPC_VER_5 is not set @@ -2872,9 +2882,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_USB_SERIAL_QUATECH_USB2 is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -CONFIG_XVMALLOC=y -CONFIG_ZRAM=y -# CONFIG_ZRAM_DEBUG is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -3336,7 +3343,9 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y # CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set -# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_LZ4=y +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation @@ -3364,6 +3373,8 @@ CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y CONFIG_LZO_COMPRESS=y CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_XZ_DEC is not set # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y -- cgit v1.1 From fa24b26d017dc88a9b1956dab2ed898e4d4f17ad Mon Sep 17 00:00:00 2001 From: Marco Navarra Date: Thu, 22 Dec 2011 13:28:23 +0100 Subject: Staging: android: fixed 80 characters warnings in lowmemorykiller.c This patch fixes some 80 chatacters limit warnings in the lowmemorykiller.c file Signed-off-by: Marco Navarra Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index d412581..fa3f521 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -7,10 +7,10 @@ * files take a comma separated list of numbers in ascending order. * * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and - * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes - * with a oom_adj value of 8 or higher when the free memory drops below 4096 pages - * and kill processes with a oom_adj value of 0 or higher when the free memory - * drops below 1024 pages. + * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill + * processes with a oom_adj value of 8 or higher when the free memory drops + * below 4096 pages and kill processes with a oom_adj value of 0 or higher + * when the free memory drops below 1024 pages. * * The driver considers memory used for caches to be free, but if a large * percentage of the cached memory is locked this can be very inaccurate @@ -159,8 +159,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) } if (sc->nr_to_scan > 0) lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n", - sc->nr_to_scan, sc->gfp_mask, other_free, other_file, - min_adj); + sc->nr_to_scan, sc->gfp_mask, other_free, + other_file, min_adj); rem = global_page_state(NR_ACTIVE_ANON) + global_page_state(NR_ACTIVE_FILE) + global_page_state(NR_INACTIVE_ANON) + -- cgit v1.1 From e6edd4723129b31402ad2da3d579abfaf5286f4d Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 6 Feb 2012 20:29:41 +0400 Subject: staging: android/lowmemorykiller: Don't grab tasklist_lock Grabbing tasklist_lock has its disadvantages, i.e. it blocks process creation and destruction. If there are lots of processes, blocking doesn't sound as a great idea. For LMK, it is sufficient to surround tasks list traverse with rcu_read_{,un}lock(). >From now on using force_sig() is not safe, as it can race with an already exiting task, so we use send_sig() now. As a downside, it won't kill PID namespace init processes, but that's not what we want anyway. Suggested-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Reviewed-by: Oleg Nesterov Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/android/lowmemorykiller.c --- drivers/staging/android/lowmemorykiller.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index fa3f521..d091c80 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #define ENHANCED_LMK_ROUTINE @@ -178,7 +179,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected_oom_adj = min_adj; #endif - read_lock(&tasklist_lock); + rcu_read_lock(); for_each_process(p) { struct mm_struct *mm; struct signal_struct *sig; @@ -273,13 +274,13 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected_oom_adj, selected_tasksize); lowmem_deathpending = selected; lowmem_deathpending_timeout = jiffies + HZ; - force_sig(SIGKILL, selected); + send_sig(SIGKILL, selected, 0); rem -= selected_tasksize; } #endif lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", sc->nr_to_scan, sc->gfp_mask, rem); - read_unlock(&tasklist_lock); + rcu_read_unlock(); return rem; } -- cgit v1.1 From 02f83a692461160e7652c4ecaaa632f2339e0f53 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 6 Feb 2012 20:29:47 +0400 Subject: staging: android/lowmemorykiller: Better mm handling LMK should not directly check for task->mm. The reason is that the process' threads may exit or detach its mm via use_mm(), but other threads may still have a valid mm. To catch this we use find_lock_task_mm(), which walks up all threads and returns an appropriate task (with lock held). Suggested-by: Oleg Nesterov Reviewed-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Acked-by: KOSAKI Motohiro Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index d091c80..f3219f4 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -99,7 +99,7 @@ task_notify_func(struct notifier_block *self, unsigned long val, void *data) static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { - struct task_struct *p; + struct task_struct *tsk; #ifdef ENHANCED_LMK_ROUTINE struct task_struct *selected[LOWMEM_DEATHPENDING_DEPTH] = {NULL,}; #else @@ -180,17 +180,19 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #endif rcu_read_lock(); - for_each_process(p) { - struct mm_struct *mm; + for_each_process(tsk) { + struct task_struct *p; struct signal_struct *sig; int oom_adj; #ifdef ENHANCED_LMK_ROUTINE int is_exist_oom_task = 0; #endif - task_lock(p); - mm = p->mm; + p = find_lock_task_mm(tsk); + if (!p) + continue; + sig = p->signal; - if (!mm || !sig) { + if (!sig) { task_unlock(p); continue; } @@ -199,7 +201,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) task_unlock(p); continue; } - tasksize = get_mm_rss(mm); + tasksize = get_mm_rss(p->mm); task_unlock(p); if (tasksize <= 0) continue; -- cgit v1.1 From 5ff795b6e17acb2130c14ac245ba5afae4098ac7 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 6 Feb 2012 20:29:54 +0400 Subject: staging: android/lowmemorykiller: No need for task->signal check task->signal == NULL is not possible, so no need for these checks. Suggested-by: Oleg Nesterov Reviewed-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Acked-by: KOSAKI Motohiro Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index f3219f4..15553bf 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -182,7 +182,6 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) rcu_read_lock(); for_each_process(tsk) { struct task_struct *p; - struct signal_struct *sig; int oom_adj; #ifdef ENHANCED_LMK_ROUTINE int is_exist_oom_task = 0; @@ -191,12 +190,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (!p) continue; - sig = p->signal; - if (!sig) { - task_unlock(p); - continue; - } - oom_adj = sig->oom_adj; + oom_adj = p->signal->oom_adj; if (oom_adj < min_adj) { task_unlock(p); continue; -- cgit v1.1 From 69de935e50cda9c5f06e6d129c3980f6e11f2f19 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 6 Feb 2012 20:30:01 +0400 Subject: staging: android/lowmemorykiller: Do not kill kernel threads LMK should not try killing kernel threads. Suggested-by: Oleg Nesterov Reviewed-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Acked-by: KOSAKI Motohiro Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 15553bf..7c98eac 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -186,6 +186,9 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #ifdef ENHANCED_LMK_ROUTINE int is_exist_oom_task = 0; #endif + if (tsk->flags & PF_KTHREAD) + continue; + p = find_lock_task_mm(tsk); if (!p) continue; -- cgit v1.1 From f5db7298d33b188805e4dec3145c7758ce647b24 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Mon, 13 Feb 2012 19:28:49 -0800 Subject: staging: android, lowmemorykiller: convert to use oom_score_adj /proc/pid/oom_adj is deprecated and will be removed in August 2012 according to Documentation/feature-removal-schedule.txt. Convert its usage in the lowmemorykiller to use the new interface, oom_score_adj, instead. Signed-off-by: David Rientjes Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 7c98eac..6a1e445 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -1,16 +1,17 @@ /* drivers/misc/lowmemorykiller.c * * The lowmemorykiller driver lets user-space specify a set of memory thresholds - * where processes with a range of oom_adj values will get killed. Specify the - * minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the - * number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both - * files take a comma separated list of numbers in ascending order. + * where processes with a range of oom_score_adj values will get killed. Specify + * the minimum oom_score_adj values in + * /sys/module/lowmemorykiller/parameters/adj and the number of free pages in + * /sys/module/lowmemorykiller/parameters/minfree. Both files take a comma + * separated list of numbers in ascending order. * * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill - * processes with a oom_adj value of 8 or higher when the free memory drops - * below 4096 pages and kill processes with a oom_adj value of 0 or higher - * when the free memory drops below 1024 pages. + * processes with a oom_score_adj value of 8 or higher when the free memory + * drops below 4096 pages and kill processes with a oom_score_adj value of 0 or + * higher when the free memory drops below 1024 pages. * * The driver considers memory used for caches to be free, but if a large * percentage of the cached memory is locked this can be very inaccurate @@ -108,7 +109,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) int rem = 0; int tasksize; int i; - int min_adj = OOM_ADJUST_MAX + 1; + int min_score_adj = OOM_SCORE_ADJ_MAX + 1; #ifdef ENHANCED_LMK_ROUTINE int selected_tasksize[LOWMEM_DEATHPENDING_DEPTH] = {0,}; int selected_oom_adj[LOWMEM_DEATHPENDING_DEPTH] = {OOM_ADJUST_MAX,}; @@ -116,7 +117,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) int max_selected_oom_idx = 0; #else int selected_tasksize = 0; - int selected_oom_adj; + int selected_oom_score_adj; #endif int array_size = ARRAY_SIZE(lowmem_adj); #ifndef CONFIG_DMA_CMA @@ -154,19 +155,19 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) for (i = 0; i < array_size; i++) { if (other_free < lowmem_minfree[i] && other_file < lowmem_minfree[i]) { - min_adj = lowmem_adj[i]; + min_score_adj = lowmem_adj[i]; break; } } if (sc->nr_to_scan > 0) lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n", sc->nr_to_scan, sc->gfp_mask, other_free, - other_file, min_adj); + other_file, min_score_adj); rem = global_page_state(NR_ACTIVE_ANON) + global_page_state(NR_ACTIVE_FILE) + global_page_state(NR_INACTIVE_ANON) + global_page_state(NR_INACTIVE_FILE); - if (sc->nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { + if (sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) { lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n", sc->nr_to_scan, sc->gfp_mask, rem); return rem; @@ -176,13 +177,12 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) selected_oom_adj[i] = min_adj; #else - selected_oom_adj = min_adj; + selected_oom_score_adj = min_score_adj; #endif - rcu_read_lock(); for_each_process(tsk) { struct task_struct *p; - int oom_adj; + int oom_score_adj; #ifdef ENHANCED_LMK_ROUTINE int is_exist_oom_task = 0; #endif @@ -193,8 +193,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (!p) continue; - oom_adj = p->signal->oom_adj; - if (oom_adj < min_adj) { + oom_score_adj = p->signal->oom_score_adj; + if (oom_score_adj < min_score_adj) { task_unlock(p); continue; } @@ -241,17 +241,17 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) } #else if (selected) { - if (oom_adj < selected_oom_adj) + if (oom_score_adj < selected_oom_score_adj) continue; - if (oom_adj == selected_oom_adj && + if (oom_score_adj == selected_oom_score_adj && tasksize <= selected_tasksize) continue; } selected = p; selected_tasksize = tasksize; - selected_oom_adj = oom_adj; + selected_oom_score_adj = oom_score_adj; lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", - p->pid, p->comm, oom_adj, tasksize); + p->pid, p->comm, oom_score_adj, tasksize); #endif } #ifdef ENHANCED_LMK_ROUTINE @@ -270,7 +270,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (selected) { lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", selected->pid, selected->comm, - selected_oom_adj, selected_tasksize); + selected_oom_score_adj, selected_tasksize); lowmem_deathpending = selected; lowmem_deathpending_timeout = jiffies + HZ; send_sig(SIGKILL, selected, 0); -- cgit v1.1 From e5ed23ac6fa9b95dc2ab4bcbaeb41e06b7bdc87a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 7 Mar 2012 13:21:23 -0800 Subject: Staging: android: lowmemorykiller.c Fix compiler warning about the type of the module parameter. Cc: San Mehat Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 6a1e445..7e2c95c 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -51,7 +51,7 @@ static int lowmem_adj[6] = { 12, }; static int lowmem_adj_size = 4; -static size_t lowmem_minfree[6] = { +static int lowmem_minfree[6] = { 3 * 512, /* 6MB */ 2 * 1024, /* 8MB */ 4 * 1024, /* 16MB */ -- cgit v1.1 From f749a76e69e403c0c29456e4c810fc86ebe2eed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 25 Sep 2012 17:37:14 -0700 Subject: staging: android: lowmemorykiller: Add config option to support oom_adj values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversion to use oom_score_adj instead of the deprecated oom_adj values breaks existing user-space code. Add a config option to convert oom_adj values written to oom_score_adj values if they appear to be valid oom_adj values. Change-Id: I68308125059b802ee2991feefb07e9703bc48549 Signed-off-by: Arve Hjønnevåg Conflicts: drivers/staging/android/Kconfig --- drivers/staging/android/Kconfig | 9 ++++ drivers/staging/android/lowmemorykiller.c | 85 +++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 2471949..4aa1494 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -90,6 +90,15 @@ 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. + endif # if ANDROID endmenu diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 7e2c95c..c6594f8 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -301,9 +301,94 @@ static void __exit lowmem_exit(void) task_free_unregister(&task_nb); } +#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); -- cgit v1.1 From 495686d879b1f543c401a7137059cb147f367c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 26 Sep 2012 18:01:17 -0700 Subject: staging: android: lowmemorykiller: Don't count reserved free memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The amount of reserved memory varies between devices. Subtract it here to reduce the amount of devices specific tuning needed for the minfree values. Change-Id: I466ae8b18f5972f6f6d8b5a7d8c4ae69660de53a Signed-off-by: Arve Hjønnevåg Conflicts: drivers/staging/android/lowmemorykiller.c --- drivers/staging/android/lowmemorykiller.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index c6594f8..184bc27 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #define ENHANCED_LMK_ROUTINE @@ -121,7 +122,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #endif int array_size = ARRAY_SIZE(lowmem_adj); #ifndef CONFIG_DMA_CMA - int other_free = global_page_state(NR_FREE_PAGES); + int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; #else int other_free = global_page_state(NR_FREE_PAGES) - global_page_state(NR_FREE_CMA_PAGES); -- cgit v1.1 From fd6a3ac352f2be6e0894c8d8cba2b6646b53cf9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 26 Sep 2012 19:52:59 -0700 Subject: staging: android: lowmemorykiller: Change default debug_level to 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The select...to kill messages are not very useful when not debugging the lowmemorykiller itself. After the change to check TIF_MEMDIE instead of using a task notifer this message can also get very noisy. Change-Id: Ice171c25801d6faa454b885a23b24b002423b754 Signed-off-by: Arve Hjønnevåg --- drivers/staging/android/lowmemorykiller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 184bc27..a7cce4f 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -44,7 +44,7 @@ #define LOWMEM_DEATHPENDING_DEPTH 3 #endif -static uint32_t lowmem_debug_level = 2; +static uint32_t lowmem_debug_level = 1; static int lowmem_adj[6] = { 0, 1, -- cgit v1.1 From 60a864d62b964dbde426df2f53b053c13cfbe50f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 3 May 2013 14:57:29 -0700 Subject: lowmemorykiller: make default lowmemorykiller debug message useful lowmemorykiller debug messages are inscrutable and mostly useful for debugging the lowmemorykiller, not explaining why a process was killed. Make the messages more useful by prefixing them with "lowmemorykiller: " and explaining in more readable terms what was killed, who it was killed for, and why it was killed. The messages now look like: [ 76.997631] lowmemorykiller: Killing 'droid.gallery3d' (2172), adj 1000, [ 76.997635] to free 27436kB on behalf of 'kswapd0' (29) because [ 76.997638] cache 122624kB is below limit 122880kB for oom_score_adj 1000 [ 76.997641] Free memory is -53356kB above reserved A negative number for free memory above reserved means some of the reserved memory has been used and is being regenerated by kswapd, which is likely what called the shrinkers. Change-Id: I1fe983381e73e124b90aa5d91cb66e55eaca390f Signed-off-by: Colin Cross Conflicts: drivers/staging/android/lowmemorykiller.c --- drivers/staging/android/lowmemorykiller.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index a7cce4f..76fad2b 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -30,6 +30,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -70,7 +72,7 @@ static unsigned long lowmem_deathpending_timeout; #define lowmem_print(level, x...) \ do { \ if (lowmem_debug_level >= (level)) \ - printk(x); \ + pr_info(x); \ } while (0) static int @@ -111,6 +113,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) int tasksize; int i; int min_score_adj = OOM_SCORE_ADJ_MAX + 1; + int minfree = 0; #ifdef ENHANCED_LMK_ROUTINE int selected_tasksize[LOWMEM_DEATHPENDING_DEPTH] = {0,}; int selected_oom_adj[LOWMEM_DEATHPENDING_DEPTH] = {OOM_ADJUST_MAX,}; @@ -154,8 +157,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (lowmem_minfree_size < array_size) array_size = lowmem_minfree_size; for (i = 0; i < array_size; i++) { - if (other_free < lowmem_minfree[i] && - other_file < lowmem_minfree[i]) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { min_score_adj = lowmem_adj[i]; break; } @@ -251,8 +254,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected = p; selected_tasksize = tasksize; selected_oom_score_adj = oom_score_adj; - lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", - p->pid, p->comm, oom_score_adj, tasksize); + lowmem_print(2, "select '%s' (%d), adj %d, size %d, to kill\n", + p->comm, p->pid, oom_score_adj, tasksize); #endif } #ifdef ENHANCED_LMK_ROUTINE @@ -269,9 +272,18 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) } #else if (selected) { - lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", - selected->pid, selected->comm, - selected_oom_score_adj, selected_tasksize); + lowmem_print(1, "Killing '%s' (%d), adj %d,\n" \ + " to free %ldkB on behalf of '%s' (%d) because\n" \ + " cache %ldkB is below limit %ldkB for oom_score_adj %d\n" \ + " Free memory is %ldkB above reserved\n", + selected->comm, selected->pid, + selected_oom_score_adj, + selected_tasksize * (long)(PAGE_SIZE / 1024), + current->comm, current->pid, + other_file * (long)(PAGE_SIZE / 1024), + minfree * (long)(PAGE_SIZE / 1024), + min_score_adj, + other_free * (long)(PAGE_SIZE / 1024)); lowmem_deathpending = selected; lowmem_deathpending_timeout = jiffies + HZ; send_sig(SIGKILL, selected, 0); -- cgit v1.1 From aecd4580a1ffb38b53a49b482f286f6d04304e5d Mon Sep 17 00:00:00 2001 From: Ziyann Date: Sat, 29 Nov 2014 16:23:12 +0100 Subject: staging: android: lowmemorykiller: fix build breakage on kernel 3.0 --- drivers/staging/android/lowmemorykiller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 76fad2b..5645e29 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -396,7 +396,7 @@ module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); __module_param_call(MODULE_PARAM_PREFIX, adj, &lowmem_adj_array_ops, .arr = &__param_arr_adj, - S_IRUGO | S_IWUSR, -1); + S_IRUGO | S_IWUSR, 0664); __MODULE_PARM_TYPE(adj, "array of int"); #else module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, -- cgit v1.1 From 51ec83132ab250883b0a1393dd73cbfdb92226cb Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Wed, 3 Jun 2015 14:28:48 -0700 Subject: kernel: avoid adding non-thread-group task to LMK rbtree To maintain the task adj RB tree, we add a task to the RB tree when fork, and delete it when exit. The place is exactly the same as the linear p->tasks list, only when the task is thread_group_leader. But to handle the oom_score_adj change case, which did not check the thread_group_leader, we may del/add a non-leader task to the RB tree. Finally leave the task in the RB tree, since we would not really delete a non-leader task from the tree. The orphan task would finally be freed, and cause later use-after-free panic when accessing RB tree. Solution: Move the rbtree adj_node to signal_struct, which is shared between task and all threads. This can make sure we only add one node for a thread group. Change-Id: I1e8dfe490656408863b3726c7bc9e4ee6dc5abc1 Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.mot.com/754224 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Zhi-Ming Yuan Reviewed-by: Yi-Wei Zhao Submit-Approved: Jira Key (cherry picked from commit b3f12a2465542888ec5c868c38022e0e5f7631ca) Signed-off-by: Abdul Salam Reviewed-on: http://gerrit.mot.com/766108 Reviewed-by: Sudharsan Yettapu Reviewed-by: Ravikumar Vembu (cherry picked from commit 558ef1fceae5d4c8509cb2a40d98c841525f7ea3) Reviewed-on: http://gerrit.mot.com/768300 Conflicts: kernel/fork.c --- include/linux/sched.h | 10 ++++++---- kernel/fork.c | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index ad2f17f..6d2e888 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -645,11 +645,13 @@ struct signal_struct { struct rw_semaphore threadgroup_fork_lock; #endif - int oom_adj; /* OOM kill score adjustment (bit shift) */ - int oom_score_adj; /* OOM kill score adjustment */ - int oom_score_adj_min; /* OOM kill score adjustment minimum value. + int oom_adj; /* OOM kill score adjustment (bit shift) */ + short oom_score_adj; /* OOM kill score adjustment */ + short oom_score_adj_min;/* OOM kill score adjustment min value. * Only settable by CAP_SYS_RESOURCE. */ - +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE + struct rb_node adj_node; +#endif struct mutex cred_guard_mutex; /* guard against foreign influences on * credential calculations * (notably. ptrace) */ diff --git a/kernel/fork.c b/kernel/fork.c index 158ca4f..0400fdf 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1009,6 +1009,10 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->oom_score_adj = current->signal->oom_score_adj; sig->oom_score_adj_min = current->signal->oom_score_adj_min; +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE + RB_CLEAR_NODE(&sig->adj_node); +#endif + mutex_init(&sig->cred_guard_mutex); return 0; -- cgit v1.1 From 8f5f33a7fd66e85d8d8502b5ee9c1c731ff0aeb5 Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Mon, 1 Jun 2015 18:58:42 -0700 Subject: fs: avoid adding non-thread-group task to LMK rbtree To maintain the task adj RB tree, we add a task to the RB tree when fork, and delete it when exit. The place is exactly the same as the linear p->tasks list, say, nly when the task is thread_group_leader. When task group_leader is changing, we make sure to add the new leader into RB tree after its leader flag is set, task->exit_signal. Cherry-picked from (CR): http://gerrit.mot.com/753419/ Change-Id: I8da47998510e531188feb067b491e92306be9414 Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.mot.com/753419 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Zhi-Ming Yuan Reviewed-by: Yi-Wei Zhao Submit-Approved: Jira Key Reviewed-on: http://gerrit.mot.com/766106 Reviewed-by: Sudharsan Yettapu Reviewed-by: Ravikumar Vembu (cherry picked from commit e9e92d64142625981490dd5c323aa08467d349e8) Reviewed-on: http://gerrit.mot.com/768301 Conflicts: fs/exec.c --- fs/exec.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/exec.c b/fs/exec.c index 807400f..f0d744a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -963,6 +963,15 @@ static int de_thread(struct task_struct *tsk) leader->group_leader = tsk; tsk->exit_signal = SIGCHLD; + /* + * need to delete leader from adj tree, because it will not be + * group leader (exit_signal = -1) soon. release_task(leader) + * can't delete it. + */ + spin_lock_irq(lock); + delete_from_adj_tree(leader); + add_2_adj_tree(tsk); + spin_unlock_irq(lock); BUG_ON(leader->exit_state != EXIT_ZOMBIE); leader->exit_state = EXIT_DEAD; -- cgit v1.1 From 6f04da23b1e3aa60626bfef868ee89a77cebd637 Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Fri, 28 Jun 2013 19:26:38 +0800 Subject: staging: android: lowmemorykiller: implement task's adj rbtree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the current LMK implementation, LMK has to scan all processes to select the correct task to kill during low memory. The basic idea for the optimization is to : queue all tasks with oom_score_adj priority, and then LMK just selects the proper task from the queue(rbtree) to kill. performance improvement: the current implementation: average time to find a task to kill : 1004us the optimized implementation: average time to find a task to kill: 43us Change-Id: I4dbbdd5673314dbbdabb71c3eff0dc229ce4ea91 Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.pcs.mot.com/548917 SLT-Approved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yi-Wei Zhao Submit-Approved: Jira Key Signed-off-by: D. Andrei Măceș Conflicts: drivers/staging/android/Kconfig drivers/staging/android/lowmemorykiller.c fs/proc/base.c mm/oom_kill.c Conflicts: drivers/staging/android/lowmemorykiller.c mm/oom_kill.c Conflicts: mm/oom_kill.c Conflicts: drivers/staging/android/lowmemorykiller.c mm/oom_kill.c --- drivers/staging/android/Kconfig | 8 +++ drivers/staging/android/lowmemorykiller.c | 100 ++++++++++++++++++++++++++++++ fs/exec.c | 2 + fs/proc/base.c | 4 ++ include/linux/sched.h | 10 +++ kernel/exit.c | 1 + kernel/fork.c | 1 + mm/oom_kill.c | 22 +++++++ 8 files changed, 148 insertions(+) diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 4aa1494..30ad4d2 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -99,6 +99,14 @@ config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES /sys/module/lowmemorykiller/parameters/adj and convert them to oom_score_adj values. +config ANDROID_LMK_ADJ_RBTREE + bool "Use RBTREE for Android Low Memory Killer" + depends on ANDROID_LOW_MEMORY_KILLER + default y + ---help--- + Use oom_score_adj rbtree to select the best proecss to kill + when system in low memory status. + endif # if ANDROID endmenu diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 5645e29..754057c 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -101,6 +101,12 @@ task_notify_func(struct notifier_block *self, unsigned long val, void *data) return NOTIFY_OK; } +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE +static struct task_struct *pick_next_from_adj_tree(struct task_struct *task); +static struct task_struct *pick_first_task(void); +static struct task_struct *pick_last_task(void); +#endif + static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { struct task_struct *tsk; @@ -184,7 +190,14 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected_oom_score_adj = min_score_adj; #endif rcu_read_lock(); + +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE + for (tsk = pick_first_task(); + tsk != pick_last_task(); + tsk = pick_next_from_adj_tree(tsk)) { +#else for_each_process(tsk) { +#endif struct task_struct *p; int oom_score_adj; #ifdef ENHANCED_LMK_ROUTINE @@ -200,7 +213,11 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) oom_score_adj = p->signal->oom_score_adj; if (oom_score_adj < min_score_adj) { task_unlock(p); +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE + break; +#else continue; +#endif } tasksize = get_mm_rss(p->mm); task_unlock(p); @@ -246,7 +263,11 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #else if (selected) { if (oom_score_adj < selected_oom_score_adj) +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE + break; +#else continue; +#endif if (oom_score_adj == selected_oom_score_adj && tasksize <= selected_tasksize) continue; @@ -391,6 +412,85 @@ static const struct kparam_array __param_arr_adj = { }; #endif +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE +DEFINE_SPINLOCK(lmk_lock); +struct rb_root tasks_scoreadj = RB_ROOT; +void add_2_adj_tree(struct task_struct *task) +{ + struct rb_node **link = &tasks_scoreadj.rb_node; + struct rb_node *parent = NULL; + struct task_struct *task_entry; + s64 key = task->signal->oom_score_adj; + /* + * Find the right place in the rbtree: + */ + spin_lock(&lmk_lock); + while (*link) { + parent = *link; + task_entry = rb_entry(parent, struct task_struct, adj_node); + + if (key < task_entry->signal->oom_score_adj) + link = &parent->rb_right; + else + link = &parent->rb_left; + } + + rb_link_node(&task->adj_node, parent, link); + rb_insert_color(&task->adj_node, &tasks_scoreadj); + spin_unlock(&lmk_lock); +} + +void delete_from_adj_tree(struct task_struct *task) +{ + spin_lock(&lmk_lock); + rb_erase(&task->adj_node, &tasks_scoreadj); + spin_unlock(&lmk_lock); +} + + +static struct task_struct *pick_next_from_adj_tree(struct task_struct *task) +{ + struct rb_node *next; + + spin_lock(&lmk_lock); + next = rb_next(&task->adj_node); + spin_unlock(&lmk_lock); + + if (!next) + return NULL; + + return rb_entry(next, struct task_struct, adj_node); +} + +static struct task_struct *pick_first_task(void) +{ + struct rb_node *left; + + spin_lock(&lmk_lock); + left = rb_first(&tasks_scoreadj); + spin_unlock(&lmk_lock); + + if (!left) + return NULL; + + return rb_entry(left, struct task_struct, adj_node); +} + +static struct task_struct *pick_last_task(void) +{ + struct rb_node *right; + + spin_lock(&lmk_lock); + right = rb_last(&tasks_scoreadj); + spin_unlock(&lmk_lock); + + if (!right) + return NULL; + + return rb_entry(right, struct task_struct, adj_node); +} +#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, diff --git a/fs/exec.c b/fs/exec.c index f0d744a..8c75690 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -957,6 +957,8 @@ static int de_thread(struct task_struct *tsk) transfer_pid(leader, tsk, PIDTYPE_SID); list_replace_rcu(&leader->tasks, &tsk->tasks); + delete_from_adj_tree(leader); + add_2_adj_tree(tsk); list_replace_init(&leader->sibling, &tsk->sibling); tsk->group_leader = tsk; diff --git a/fs/proc/base.c b/fs/proc/base.c index 1a140ca..56d23e1 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1063,6 +1063,8 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf, else task->signal->oom_score_adj = (oom_adjust * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; + delete_from_adj_tree(task); + add_2_adj_tree(task); err_sighand: unlock_task_sighand(task, &flags); err_task_lock: @@ -1187,6 +1189,8 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf, atomic_dec(&task->mm->oom_disable_count); } task->signal->oom_score_adj = oom_score_adj; + delete_from_adj_tree(task); + add_2_adj_tree(task); if (has_capability_noaudit(current, CAP_SYS_RESOURCE)) task->signal->oom_score_adj_min = oom_score_adj; /* diff --git a/include/linux/sched.h b/include/linux/sched.h index 6d2e888..6b030a5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1284,6 +1284,9 @@ struct task_struct { #endif struct list_head tasks; +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE + struct rb_node adj_node; +#endif #ifdef CONFIG_SMP struct plist_node pushable_tasks; #endif @@ -1623,6 +1626,13 @@ static inline struct pid *task_tgid(struct task_struct *task) return task->group_leader->pids[PIDTYPE_PID].pid; } +#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE +extern void add_2_adj_tree(struct task_struct *task); +extern void delete_from_adj_tree(struct task_struct *task); +#else +static inline void add_2_adj_tree(struct task_struct *task) { } +static inline void delete_from_adj_tree(struct task_struct *task) { } +#endif /* * Without tasklist or rcu lock it is not safe to dereference * the result of task_pgrp/task_session even if task == current, diff --git a/kernel/exit.c b/kernel/exit.c index 97dd317..6b8a7af 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -68,6 +68,7 @@ static void __unhash_process(struct task_struct *p, bool group_dead) detach_pid(p, PIDTYPE_SID); list_del_rcu(&p->tasks); + delete_from_adj_tree(p); list_del_init(&p->sibling); __this_cpu_dec(process_counts); } diff --git a/kernel/fork.c b/kernel/fork.c index 0400fdf..c1760e6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1379,6 +1379,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, attach_pid(p, PIDTYPE_SID, task_session(current)); list_add_tail(&p->sibling, &p->real_parent->children); list_add_tail_rcu(&p->tasks, &init_task.tasks); + add_2_adj_tree(p); __this_cpu_inc(process_counts); } attach_pid(p, PIDTYPE_PID, pid); diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 7c72487..678cf2b 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -38,6 +38,28 @@ int sysctl_oom_kill_allocating_task; int sysctl_oom_dump_tasks = 1; static DEFINE_SPINLOCK(zone_scan_lock); +/* + * compare_swap_oom_score_adj() - compare and swap current's oom_score_adj + * @old_val: old oom_score_adj for compare + * @new_val: new oom_score_adj for swap + * + * Sets the oom_score_adj value for current to @new_val iff its present value is + * @old_val. Usually used to reinstate a previous value to prevent racing with + * userspacing tuning the value in the interim. + */ +void compare_swap_oom_score_adj(int old_val, int new_val) +{ + struct sighand_struct *sighand = current->sighand; + + spin_lock_irq(&sighand->siglock); + if (current->signal->oom_score_adj == old_val) { + current->signal->oom_score_adj = new_val; + delete_from_adj_tree(current); + add_2_adj_tree(current); + } + spin_unlock_irq(&sighand->siglock); +} + /** * test_set_oom_score_adj() - set current's oom_score_adj and return old value * @new_val: new oom_score_adj value -- cgit v1.1 From eeaf4e63e771b8084bd2fecd58ec4430383e2ef5 Mon Sep 17 00:00:00 2001 From: Yi-wei Zhao Date: Fri, 14 Dec 2012 16:34:53 -0600 Subject: staging: android: lowmemorykiller: select a new task to kill Under certain circumstances, a process may take time to handle a SIGKILL. When lowmemkiller is called again shortly after, it would pick the same process to kill over and over, so that we cann't get free memory for long time. Solution is to check fatal_signal_pending() on the selected task, and if it's already pending, select a new task to kill. Cherry-pick 5e3358093351e5d48e21250e31896b855542f22c Reviewed-on: http://gerrit.pcs.mot.com/479831 Change-Id: I53445114451ffaba293f3c7174fb0b01ed0d34b6 Signed-off-by: Tianshui Shi Reviewed-on: http://gerrit.pcs.mot.com/505410 Tested-by: Jira Key Reviewed-by: Yi-Wei Zhao Reviewed-by: Jason Hrycay Reviewed-by: Jeffrey Carlyle (cherry picked from commit da093001caf06ed2296b4f79c84cc48ce713eac6) --- drivers/staging/android/lowmemorykiller.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 754057c..a0731af 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -219,6 +219,11 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) continue; #endif } + if (fatal_signal_pending(p)) { + lowmem_print(2, "skip slow dying process %d\n", p->pid); + task_unlock(p); + continue; + } tasksize = get_mm_rss(p->mm); task_unlock(p); if (tasksize <= 0) -- cgit v1.1 From 2ed1b46030d9062ac9dc12ffef3beb3ece44d1a0 Mon Sep 17 00:00:00 2001 From: Yi-wei Zhao Date: Fri, 13 Feb 2015 17:33:04 -0800 Subject: staging:android:lmk: read rb tree root with spinlock there is racing condition: after reading rb tree root, it might be changed by other tasks before adding new node. it can lead to rb tree corruption. This patch is to avoid this race condition. Change-Id: Id86bfd133488ad4ee12cd83c9bf1d1c12ef5598f Signed-off-by: Yi-wei Zhao Reviewed-on: http://gerrit.mot.com/715645 Tested-by: Jira Key Reviewed-by: Sheng-Zhe Zhao SLTApproved: Christopher Fries Submit-Approved: Jira Key --- drivers/staging/android/lowmemorykiller.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index a0731af..caa7d3d 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -422,14 +422,16 @@ DEFINE_SPINLOCK(lmk_lock); struct rb_root tasks_scoreadj = RB_ROOT; void add_2_adj_tree(struct task_struct *task) { - struct rb_node **link = &tasks_scoreadj.rb_node; + struct rb_node **link; struct rb_node *parent = NULL; struct task_struct *task_entry; s64 key = task->signal->oom_score_adj; + /* * Find the right place in the rbtree: */ spin_lock(&lmk_lock); + link = &tasks_scoreadj.rb_node; while (*link) { parent = *link; task_entry = rb_entry(parent, struct task_struct, adj_node); -- cgit v1.1 From ca7c9fad094f4413dcb2ce65d6d4983201295536 Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Thu, 12 Mar 2015 19:14:26 -0700 Subject: drivers:lmk: Fix double delete issue someone may change a process's oom_score_adj by proc fs, even though the process has exited. In that case, the task was deleted from the rb tree already, and the redundant deleting would trigger rb_erase panic finally. In this patch, we make sure to clear the node after deteting and check its empty status before rb_erase. Change-Id: I7628c7d21011099e796b7d366cbc142f96bb8aab Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.mot.com/725306 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Sheng-Zhe Zhao Submit-Approved: Jira Key --- drivers/staging/android/lowmemorykiller.c | 5 ++++- kernel/fork.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index caa7d3d..239d32d 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -450,7 +450,10 @@ void add_2_adj_tree(struct task_struct *task) void delete_from_adj_tree(struct task_struct *task) { spin_lock(&lmk_lock); - rb_erase(&task->adj_node, &tasks_scoreadj); + if (!RB_EMPTY_NODE(&task->adj_node)) { + rb_erase(&task->adj_node, &tasks_scoreadj); + RB_CLEAR_NODE(&task->adj_node); + } spin_unlock(&lmk_lock); } diff --git a/kernel/fork.c b/kernel/fork.c index c1760e6..ba7e2bc 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -317,6 +317,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) account_kernel_stack(ti, 1); + RB_CLEAR_NODE(&tsk->adj_node); + return tsk; out: -- cgit v1.1 From f8be24cb48429f5d532876b57d5083dd4e178159 Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Fri, 27 Mar 2015 14:15:25 -0700 Subject: drivers:lmk: Fix null pointer issue On some race, the tsk that lmk is using may be deleted from the RB tree by other thread, and rb_next would return a NULL if we use this tsk to get next. For this case, we need to skip this round of shrink and wait for the next turn. Otherwise, tsk would trigger NULL pointer panic. Change-Id: I37f4bd2827f8a0a28f29192dd71532d1c252f986 Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.mot.com/729556 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Yi-Wei Zhao Submit-Approved: Jira Key --- drivers/staging/android/lowmemorykiller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 239d32d..c6ee61c 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -193,7 +193,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE for (tsk = pick_first_task(); - tsk != pick_last_task(); + tsk != pick_last_task() && tsk != NULL; tsk = pick_next_from_adj_tree(tsk)) { #else for_each_process(tsk) { -- cgit v1.1 From 9b0e84ac672845689861120bbb58b33788f7f6a3 Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Wed, 3 Jun 2015 14:33:43 -0700 Subject: lowmemorykiller: maintain LMK rbtree with signal->adj_node Currently, we maintain LMK rbtree with task->adj_node. However, when handling oom_score_adj change case, we may del/add a non-leader task to the RB tree, which is not as expected. This patch we maintain the LMK rbtree with task->signal->adj_node. Since signal_struct is shared between main task and threads, we can avoid non-leader thread adding to tree. Change-Id: I3ba9e740e03ab04c25497a1cc2c870f051bd5b07 Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.mot.com/754225 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhi-Ming Yuan Reviewed-by: Yi-Wei Zhao Submit-Approved: Jira Key (cherry picked from commit b40634023f9152c6232de9acb80108e0af7e4075) Signed-off-by: Abdul Salam Reviewed-on: http://gerrit.mot.com/766107 Reviewed-by: Sudharsan Yettapu Reviewed-by: Ravikumar Vembu (cherry picked from commit f3abd37ce3b4d36ae05cfc1c5cd10e5a3f584e7f) Reviewed-on: http://gerrit.mot.com/768302 --- drivers/staging/android/lowmemorykiller.c | 36 ++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index c6ee61c..f68459a 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -420,11 +420,14 @@ static const struct kparam_array __param_arr_adj = { #ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE DEFINE_SPINLOCK(lmk_lock); struct rb_root tasks_scoreadj = RB_ROOT; +/* + * Makesure to invoke the function with holding sighand->siglock + */ void add_2_adj_tree(struct task_struct *task) { struct rb_node **link; struct rb_node *parent = NULL; - struct task_struct *task_entry; + struct signal_struct *sig_entry; s64 key = task->signal->oom_score_adj; /* @@ -434,25 +437,28 @@ void add_2_adj_tree(struct task_struct *task) link = &tasks_scoreadj.rb_node; while (*link) { parent = *link; - task_entry = rb_entry(parent, struct task_struct, adj_node); + sig_entry = rb_entry(parent, struct signal_struct, adj_node); - if (key < task_entry->signal->oom_score_adj) + if (key < sig_entry->oom_score_adj) link = &parent->rb_right; else link = &parent->rb_left; } - rb_link_node(&task->adj_node, parent, link); - rb_insert_color(&task->adj_node, &tasks_scoreadj); + rb_link_node(&task->signal->adj_node, parent, link); + rb_insert_color(&task->signal->adj_node, &tasks_scoreadj); spin_unlock(&lmk_lock); } +/* + * Makesure to invoke the function with holding sighand->siglock + */ void delete_from_adj_tree(struct task_struct *task) { spin_lock(&lmk_lock); - if (!RB_EMPTY_NODE(&task->adj_node)) { - rb_erase(&task->adj_node, &tasks_scoreadj); - RB_CLEAR_NODE(&task->adj_node); + if (!RB_EMPTY_NODE(&task->signal->adj_node)) { + rb_erase(&task->signal->adj_node, &tasks_scoreadj); + RB_CLEAR_NODE(&task->signal->adj_node); } spin_unlock(&lmk_lock); } @@ -461,20 +467,23 @@ void delete_from_adj_tree(struct task_struct *task) static struct task_struct *pick_next_from_adj_tree(struct task_struct *task) { struct rb_node *next; + struct signal_struct *next_tsk_sig; spin_lock(&lmk_lock); - next = rb_next(&task->adj_node); + next = rb_next(&task->signal->adj_node); spin_unlock(&lmk_lock); if (!next) return NULL; - return rb_entry(next, struct task_struct, adj_node); + next_tsk_sig = rb_entry(next, struct signal_struct, adj_node); + return next_tsk_sig->curr_target->group_leader; } static struct task_struct *pick_first_task(void) { struct rb_node *left; + struct signal_struct *first_tsk_sig; spin_lock(&lmk_lock); left = rb_first(&tasks_scoreadj); @@ -483,12 +492,14 @@ static struct task_struct *pick_first_task(void) if (!left) return NULL; - return rb_entry(left, struct task_struct, adj_node); + first_tsk_sig = rb_entry(left, struct signal_struct, adj_node); + return first_tsk_sig->curr_target->group_leader; } static struct task_struct *pick_last_task(void) { struct rb_node *right; + struct signal_struct *last_tsk_sig; spin_lock(&lmk_lock); right = rb_last(&tasks_scoreadj); @@ -497,7 +508,8 @@ static struct task_struct *pick_last_task(void) if (!right) return NULL; - return rb_entry(right, struct task_struct, adj_node); + last_tsk_sig = rb_entry(right, struct signal_struct, adj_node); + return last_tsk_sig->curr_target->group_leader; } #endif -- cgit v1.1 From c12b9dadba69989e42d2065bd25c73301cc8729f Mon Sep 17 00:00:00 2001 From: Emanuele Date: Fri, 25 Mar 2016 13:25:44 +0100 Subject: lowmemorykiller: fixes for new oom_score_adj Change-Id: I34c547039d02366649206395fe3fb3f363fc900e Signed-off-by: Emanuele Scarlata --- drivers/staging/android/lowmemorykiller.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index f68459a..2c1d092 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -185,7 +185,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #ifdef ENHANCED_LMK_ROUTINE for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) - selected_oom_adj[i] = min_adj; + selected_oom_adj[i] = min_score_adj; #else selected_oom_score_adj = min_score_adj; #endif @@ -238,8 +238,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) break; } } - } else if (selected_oom_adj[max_selected_oom_idx] < oom_adj || - (selected_oom_adj[max_selected_oom_idx] == oom_adj && + } else if (selected_oom_adj[max_selected_oom_idx] < oom_score_adj || + (selected_oom_adj[max_selected_oom_idx] == oom_score_adj && selected_tasksize[max_selected_oom_idx] < tasksize)) { is_exist_oom_task = 1; } @@ -247,7 +247,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (is_exist_oom_task) { selected[max_selected_oom_idx] = p; selected_tasksize[max_selected_oom_idx] = tasksize; - selected_oom_adj[max_selected_oom_idx] = oom_adj; + selected_oom_adj[max_selected_oom_idx] = oom_score_adj; if (all_selected_oom < LOWMEM_DEATHPENDING_DEPTH) all_selected_oom++; @@ -263,7 +263,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) } lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", - p->pid, p->comm, oom_adj, tasksize); + p->pid, p->comm, oom_score_adj, tasksize); } #else if (selected) { -- cgit v1.1 From 3053323261efed3e9000998136e86f007b52ba73 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Sun, 12 Jun 2016 11:56:46 +0200 Subject: Remove ENHANCED_LMK_ROUTINE added by Samsung Change-Id: I2e26fbcd06541536258313f4f5753ca87ab46d9c --- drivers/staging/android/lowmemorykiller.c | 98 ------------------------------- 1 file changed, 98 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 2c1d092..5a9835b 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -40,11 +40,6 @@ #include #include #include -#define ENHANCED_LMK_ROUTINE - -#ifdef ENHANCED_LMK_ROUTINE -#define LOWMEM_DEATHPENDING_DEPTH 3 -#endif static uint32_t lowmem_debug_level = 1; static int lowmem_adj[6] = { @@ -61,12 +56,7 @@ static int lowmem_minfree[6] = { 16 * 1024, /* 64MB */ }; static int lowmem_minfree_size = 4; - -#ifdef ENHANCED_LMK_ROUTINE -static struct task_struct *lowmem_deathpending[LOWMEM_DEATHPENDING_DEPTH] = {NULL,}; -#else static struct task_struct *lowmem_deathpending; -#endif static unsigned long lowmem_deathpending_timeout; #define lowmem_print(level, x...) \ @@ -87,17 +77,8 @@ task_notify_func(struct notifier_block *self, unsigned long val, void *data) { struct task_struct *task = data; -#ifdef ENHANCED_LMK_ROUTINE - int i = 0; - for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) - if (task == lowmem_deathpending[i]) { - lowmem_deathpending[i] = NULL; - break; - } -#else if (task == lowmem_deathpending) lowmem_deathpending = NULL; -#endif return NOTIFY_OK; } @@ -110,25 +91,14 @@ static struct task_struct *pick_last_task(void); static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { struct task_struct *tsk; -#ifdef ENHANCED_LMK_ROUTINE - struct task_struct *selected[LOWMEM_DEATHPENDING_DEPTH] = {NULL,}; -#else struct task_struct *selected = NULL; -#endif int rem = 0; int tasksize; int i; int min_score_adj = OOM_SCORE_ADJ_MAX + 1; int minfree = 0; -#ifdef ENHANCED_LMK_ROUTINE - int selected_tasksize[LOWMEM_DEATHPENDING_DEPTH] = {0,}; - int selected_oom_adj[LOWMEM_DEATHPENDING_DEPTH] = {OOM_ADJUST_MAX,}; - int all_selected_oom = 0; - int max_selected_oom_idx = 0; -#else int selected_tasksize = 0; int selected_oom_score_adj; -#endif int array_size = ARRAY_SIZE(lowmem_adj); #ifndef CONFIG_DMA_CMA int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; @@ -146,17 +116,9 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) * this pass. * */ -#ifdef ENHANCED_LMK_ROUTINE - for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) { - if (lowmem_deathpending[i] && - time_before_eq(jiffies, lowmem_deathpending_timeout)) - return 0; - } -#else if (lowmem_deathpending && time_before_eq(jiffies, lowmem_deathpending_timeout)) return 0; -#endif if (lowmem_adj_size < array_size) array_size = lowmem_adj_size; @@ -183,12 +145,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) return rem; } -#ifdef ENHANCED_LMK_ROUTINE - for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) - selected_oom_adj[i] = min_score_adj; -#else selected_oom_score_adj = min_score_adj; -#endif rcu_read_lock(); #ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE @@ -200,9 +157,6 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) #endif struct task_struct *p; int oom_score_adj; -#ifdef ENHANCED_LMK_ROUTINE - int is_exist_oom_task = 0; -#endif if (tsk->flags & PF_KTHREAD) continue; @@ -229,43 +183,6 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (tasksize <= 0) continue; -#ifdef ENHANCED_LMK_ROUTINE - if (all_selected_oom < LOWMEM_DEATHPENDING_DEPTH) { - for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) { - if (!selected[i]) { - is_exist_oom_task = 1; - max_selected_oom_idx = i; - break; - } - } - } else if (selected_oom_adj[max_selected_oom_idx] < oom_score_adj || - (selected_oom_adj[max_selected_oom_idx] == oom_score_adj && - selected_tasksize[max_selected_oom_idx] < tasksize)) { - is_exist_oom_task = 1; - } - - if (is_exist_oom_task) { - selected[max_selected_oom_idx] = p; - selected_tasksize[max_selected_oom_idx] = tasksize; - selected_oom_adj[max_selected_oom_idx] = oom_score_adj; - - if (all_selected_oom < LOWMEM_DEATHPENDING_DEPTH) - all_selected_oom++; - - if (all_selected_oom == LOWMEM_DEATHPENDING_DEPTH) { - for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) { - if (selected_oom_adj[i] < selected_oom_adj[max_selected_oom_idx]) - max_selected_oom_idx = i; - else if (selected_oom_adj[i] == selected_oom_adj[max_selected_oom_idx] && - selected_tasksize[i] < selected_tasksize[max_selected_oom_idx]) - max_selected_oom_idx = i; - } - } - - lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", - p->pid, p->comm, oom_score_adj, tasksize); - } -#else if (selected) { if (oom_score_adj < selected_oom_score_adj) #ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE @@ -282,21 +199,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected_oom_score_adj = oom_score_adj; lowmem_print(2, "select '%s' (%d), adj %d, size %d, to kill\n", p->comm, p->pid, oom_score_adj, tasksize); -#endif - } -#ifdef ENHANCED_LMK_ROUTINE - for (i = 0; i < LOWMEM_DEATHPENDING_DEPTH; i++) { - if (selected[i]) { - lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", - selected[i]->pid, selected[i]->comm, - selected_oom_adj[i], selected_tasksize[i]); - lowmem_deathpending[i] = selected[i]; - lowmem_deathpending_timeout = jiffies + HZ; - force_sig(SIGKILL, selected[i]); - rem -= selected_tasksize[i]; - } } -#else if (selected) { lowmem_print(1, "Killing '%s' (%d), adj %d,\n" \ " to free %ldkB on behalf of '%s' (%d) because\n" \ @@ -315,7 +218,6 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) send_sig(SIGKILL, selected, 0); rem -= selected_tasksize; } -#endif lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", sc->nr_to_scan, sc->gfp_mask, rem); rcu_read_unlock(); -- cgit v1.1 From 6726c46cea51720ece973296c30b2b57e209a14b Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Thu, 17 Sep 2015 16:01:40 -0700 Subject: zram: fix possible use after free in zcomp_create() zcomp_create() verifies the success of zcomp_strm_{multi,single}_create() through comp->stream, which can potentially be pointing to memory that was freed if these functions returned an error. While at it, replace a 'ERR_PTR(-ENOMEM)' by a more generic 'ERR_PTR(error)' as in the future zcomp_strm_{multi,siggle}_create() could return other error codes. Function documentation updated accordingly. Change-Id: I84334ce1929c8212aa70387781ef0a6b0af50fa5 Fixes: beca3ec71fe5 ("zram: add multi stream functionality") Signed-off-by: Luis Henriques Acked-by: Sergey Senozhatsky Acked-by: Minchan Kim Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/zram/zcomp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index f1ff39a..54d946a 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -325,12 +325,14 @@ void zcomp_destroy(struct zcomp *comp) * allocate new zcomp and initialize it. return compressing * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in - * case of allocation error. + * case of allocation error, or any other error potentially + * returned by functions zcomp_strm_{multi,single}_create. */ struct zcomp *zcomp_create(const char *compress, int max_strm) { struct zcomp *comp; struct zcomp_backend *backend; + int error; backend = find_backend(compress); if (!backend) @@ -342,12 +344,12 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) comp->backend = backend; if (max_strm > 1) - zcomp_strm_multi_create(comp, max_strm); + error = zcomp_strm_multi_create(comp, max_strm); else - zcomp_strm_single_create(comp); - if (!comp->stream) { + error = zcomp_strm_single_create(comp); + if (error) { kfree(comp); - return ERR_PTR(-ENOMEM); + return ERR_PTR(error); } return comp; } -- cgit v1.1 From 631bc3fecbe703ac4afcfcf197b03e5e4e911240 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 13 Jun 2016 14:48:08 +1000 Subject: i9100: update+regen defconfig for zram changes * don't enable lz4 yet, but enable zsmalloc in order to continue using zram. Change-Id: I78ca1bb9a75c19750e65d76862a65f44986de6ac --- arch/arm/configs/cyanogenmod_i9100_defconfig | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index cfacb41..8edecfd 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -105,6 +105,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set CONFIG_INITRAMFS_COMPRESSION_NONE=y # CONFIG_INITRAMFS_COMPRESSION_GZIP is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set @@ -675,6 +676,8 @@ CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y @@ -731,6 +734,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND_FLEXRATE_MAX_DURATION=100 CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +# CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST is not set # CONFIG_CPU_FREQ_GOV_SLP is not set # CONFIG_CPU_FREQ_DVFS_MONITOR is not set CONFIG_CPU_IDLE=y @@ -1218,6 +1222,9 @@ CONFIG_SW_SYNC_USER=y # CONFIG_MTD is not set # CONFIG_PARPORT is not set CONFIG_BLK_DEV=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_LZ4_COMPRESS is not set +# CONFIG_ZRAM_DEBUG is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -1298,10 +1305,13 @@ CONFIG_UMTS_MODEM_XMM6260=y # CONFIG_UMTS_MODEM_XMM6262 is not set # CONFIG_CDMA_MODEM_CBP71 is not set # CONFIG_CDMA_MODEM_CBP72 is not set +# CONFIG_CDMA_MODEM_CBP82 is not set # CONFIG_LTE_MODEM_CMC221 is not set +# CONFIG_UMTS_MODEM_SS222 is not set # CONFIG_CDMA_MODEM_MDM6600 is not set # CONFIG_TDSCDMA_MODEM_SPRD8803 is not set # CONFIG_GSM_MODEM_ESC6270 is not set +# CONFIG_CDMA_MODEM_QSC6085 is not set # CONFIG_LINK_DEVICE_MIPI is not set # CONFIG_LINK_DEVICE_DPRAM is not set # CONFIG_LINK_DEVICE_PLD is not set @@ -1309,6 +1319,7 @@ CONFIG_UMTS_MODEM_XMM6260=y CONFIG_LINK_DEVICE_HSIC=y # CONFIG_LINK_DEVICE_C2C is not set # CONFIG_LINK_DEVICE_SPI is not set +# CONFIG_BOOT_DEVICE_SPI is not set # CONFIG_WORKQUEUE_FRONT is not set # CONFIG_IPC_CMC22x_OLD_RFS is not set # CONFIG_SIPC_VER_5 is not set @@ -2690,10 +2701,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_LINE6_USB is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -CONFIG_XVMALLOC=y -CONFIG_ZRAM=y -CONFIG_ZCACHE=y -# CONFIG_ZRAM_DEBUG is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -3179,6 +3186,8 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set # CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation -- cgit v1.1 From df78ca6d63311df32fcb6594d4ddebee953919f7 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 13 Jun 2016 15:11:22 +1000 Subject: i9100: enable LZ compression for zram Change-Id: I6c406d1c1d97ee3a3846c04c92dd625ab621a020 --- arch/arm/configs/cyanogenmod_i9100_defconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index 8edecfd..a568077 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -3185,8 +3185,8 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y # CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set -# CONFIG_CRYPTO_LZO is not set -# CONFIG_CRYPTO_LZ4 is not set +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_LZ4=y # CONFIG_CRYPTO_LZ4HC is not set # @@ -3215,6 +3215,8 @@ CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y CONFIG_LZO_COMPRESS=y CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_XZ_DEC is not set # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y -- cgit v1.1 From 75356944c2b5f4a80dadcd28807321c5049776ed Mon Sep 17 00:00:00 2001 From: RGIB Date: Wed, 22 Jun 2016 12:35:12 +0200 Subject: smdk4412 : qcom modem for n5120 Change-Id: I802127b600b35f03e864cc1603f7d42e144cca21 --- arch/arm/configs/cyanogenmod_n5120_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index 896bf0d..6360eacf 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -578,6 +578,7 @@ CONFIG_BT_MGMT=y # Qualcomm Modem Feature # CONFIG_QC_MODEM=y +CONFIG_SIM_DETECT=y CONFIG_MSM_RMNET_USB=y CONFIG_SENSORS_HALL=y CONFIG_MSM_SUBSYSTEM_RESTART=y @@ -699,7 +700,7 @@ CONFIG_MIGRATION=y CONFIG_ZONE_DMA_FLAG=0 CONFIG_BOUNCE=y CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set CONFIG_CMA=y -- cgit v1.1 From 00cd10b11d1123bd4aee1f8845a11e7d0b662bcc Mon Sep 17 00:00:00 2001 From: RGIB Date: Wed, 22 Jun 2016 15:01:26 +0200 Subject: n5120 : selinux enforcing Change-Id: I8c5401059cdec1b0298a162ea26030ef8471671a --- arch/arm/configs/cyanogenmod_n5120_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index 6360eacf..bc698f1 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -722,7 +722,7 @@ CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y # CONFIG_USE_OF is not set CONFIG_ZBOOT_ROM_TEXT=0 CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12 androidboot.selinux=permissive" +CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12" # CONFIG_CMDLINE_FROM_BOOTLOADER is not set CONFIG_CMDLINE_EXTEND=y # CONFIG_CMDLINE_FORCE is not set -- cgit v1.1 From 6f6a51da4fe6f5b6c11eae5e86d59d214feb538b Mon Sep 17 00:00:00 2001 From: Roberto Gibellini Date: Sat, 25 Jun 2016 15:13:34 -0700 Subject: Revert "n5120 : selinux enforcing" This reverts commit 00cd10b11d1123bd4aee1f8845a11e7d0b662bcc. Change-Id: I07285ec12e8a2ebf3f7925ea8ce67c51d1d903cf --- arch/arm/configs/cyanogenmod_n5120_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index bc698f1..6360eacf 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -722,7 +722,7 @@ CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y # CONFIG_USE_OF is not set CONFIG_ZBOOT_ROM_TEXT=0 CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12" +CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12 androidboot.selinux=permissive" # CONFIG_CMDLINE_FROM_BOOTLOADER is not set CONFIG_CMDLINE_EXTEND=y # CONFIG_CMDLINE_FORCE is not set -- cgit v1.1 From 4f8c6f7356c599f2620c2b5baeb92be6dd532876 Mon Sep 17 00:00:00 2001 From: Roberto Gibellini Date: Sun, 26 Jun 2016 00:29:10 -0700 Subject: Revert "Revert "n5120 : selinux enforcing"" This reverts commit 6f6a51da4fe6f5b6c11eae5e86d59d214feb538b. Change-Id: I907a5858269642e72b93fc0ebd138660e4c79b9c --- arch/arm/configs/cyanogenmod_n5120_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index 6360eacf..bc698f1 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -722,7 +722,7 @@ CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y # CONFIG_USE_OF is not set CONFIG_ZBOOT_ROM_TEXT=0 CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12 androidboot.selinux=permissive" +CONFIG_CMDLINE="console=ttySAC2,115200 consoleblank=0 androidboot.hardware=smdk4x12" # CONFIG_CMDLINE_FROM_BOOTLOADER is not set CONFIG_CMDLINE_EXTEND=y # CONFIG_CMDLINE_FORCE is not set -- cgit v1.1 From 53094f33e12e0564161fc7ebf38c222d9270e118 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 13 Jun 2016 20:44:36 +1000 Subject: ARM: EXYNOS: silence watchdog log spam Change-Id: I6bee287c05b3f5170b8d1e2c1c6738d3c8cc88e9 --- arch/arm/mach-exynos/sec_watchdog.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos/sec_watchdog.c b/arch/arm/mach-exynos/sec_watchdog.c index b903b80..4ee118a 100644 --- a/arch/arm/mach-exynos/sec_watchdog.c +++ b/arch/arm/mach-exynos/sec_watchdog.c @@ -69,7 +69,7 @@ static spinlock_t wdt_lock; #if defined(PET_BY_WORKQUEUE) static void watchdog_workfunc(struct work_struct *work) { - pr_info("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT)); + pr_debug("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT)); writel(watchdog_reset * TPS, S3C2410_WTCNT); queue_delayed_work_on(0, watchdog_wq, &watchdog_work, watchdog_pet * HZ); @@ -77,7 +77,7 @@ static void watchdog_workfunc(struct work_struct *work) #elif defined(PET_BY_DIRECT_TIMER) static void pet_watchdog_timer_fn(unsigned long data) { - pr_info("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT)); + pr_debug("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT)); writel(watchdog_reset * TPS, S3C2410_WTCNT); pet_watchdog_timer.expires += watchdog_pet * HZ; add_timer_on(&pet_watchdog_timer, 0); @@ -85,7 +85,7 @@ static void pet_watchdog_timer_fn(unsigned long data) #else static enum hrtimer_restart watchdog_timerfunc(struct hrtimer *timer) { - pr_info("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT)); + pr_debug("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT)); writel(watchdog_reset * TPS, S3C2410_WTCNT); hrtimer_start(&watchdog_timer, ktime_set(watchdog_pet, 0), HRTIMER_MODE_REL); -- cgit v1.1 From 2533a2b72074aefdbf2766857d06c39836d3e65e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 7 Jan 2015 14:28:26 -0600 Subject: mnt: Fail collect_mounts when applied to unmounted mounts The only users of collect_mounts are in audit_tree.c In audit_trim_trees and audit_add_tree_rule the path passed into collect_mounts is generated from kern_path passed an audit_tree pathname which is guaranteed to be an absolute path. In those cases collect_mounts is obviously intended to work on mounted paths and if a race results in paths that are unmounted when collect_mounts it is reasonable to fail early. The paths passed into audit_tag_tree don't have the absolute path check. But are used to play with fsnotify and otherwise interact with the audit_trees, so again operating only on mounted paths appears reasonable. Avoid having to worry about what happens when we try and audit unmounted filesystems by restricting collect_mounts to mounts that appear in the mount tree. Change-Id: I2edfee6d6951a2179ce8f53785b65ddb1eb95629 Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index 900812f..8b34a97 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1477,8 +1477,14 @@ struct vfsmount *collect_mounts(struct path *path) { struct vfsmount *tree; down_write(&namespace_sem); - tree = copy_tree(path->mnt, path->dentry, CL_COPY_ALL | CL_PRIVATE); + if (!check_mnt(path->mnt)) + tree = ERR_PTR(-EINVAL); + else + tree = copy_tree(path->mnt, path->dentry, + CL_COPY_ALL | CL_PRIVATE); up_write(&namespace_sem); + if (IS_ERR(tree)) + return NULL; return tree; } -- cgit v1.1 From 28ea59dc1c4781c819996a65e60aa773f1da6765 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 26 May 2016 06:45:59 -0500 Subject: KEYS: potential uninitialized variable If __key_link_begin() failed then "edit" would be uninitialized. I've added a check to fix that. Change-Id: I0e28bdba07f645437db2b08daf67ca27f16c6f5c Fixes: f70e2e06196a ('KEYS: Do preallocation for __key_link()') Signed-off-by: Dan Carpenter --- security/keys/key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/key.c b/security/keys/key.c index e1704f6..d2765e4 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -580,7 +580,7 @@ int key_reject_and_link(struct key *key, mutex_unlock(&key_construction_mutex); - if (keyring) + if (keyring && link_ret == 0) __key_link_end(keyring, key->type, prealloc); /* wake up anyone waiting for a key to be constructed */ -- cgit v1.1 From 219a2d78a8bc79df201c45a78a5ece714c002704 Mon Sep 17 00:00:00 2001 From: Scott Bauer Date: Thu, 23 Jun 2016 08:59:47 -0600 Subject: HID: hiddev: validate num_values for HIDIOCGUSAGES, HIDIOCSUSAGES commands This patch validates the num_values parameter from userland during the HIDIOCGUSAGES and HIDIOCSUSAGES commands. Previously, if the report id was set to HID_REPORT_ID_UNKNOWN, we would fail to validate the num_values parameter leading to a heap overflow. Change-Id: I10866ee01c7ba430eab2b5cc3356c9519c7f9730 Cc: stable@vger.kernel.org Signed-off-by: Scott Bauer Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hiddev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 7c1188b..fd78f2b 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -515,13 +515,13 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, goto inval; } else if (uref->usage_index >= field->report_count) goto inval; - - else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && - (uref_multi->num_values > HID_MAX_MULTI_USAGES || - uref->usage_index + uref_multi->num_values > field->report_count)) - goto inval; } + if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && + (uref_multi->num_values > HID_MAX_MULTI_USAGES || + uref->usage_index + uref_multi->num_values > field->report_count)) + goto inval; + switch (cmd) { case HIDIOCGUSAGE: uref->value = field->value[uref->usage_index]; -- cgit v1.1 From 29bb56e2cc91e8a9a1d44cd14fe04c48f77cce66 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 20 Mar 2015 17:41:43 +0000 Subject: net: validate the range we feed to iov_iter_init() in sys_sendto/sys_recvfrom Change-Id: Ida19e5102b7faca17c685a261c20fbbf5c9614f9 Cc: stable@vger.kernel.org # v3.19 Signed-off-by: Al Viro Signed-off-by: David S. Miller --- net/socket.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/socket.c b/net/socket.c index e4c7f90..53882f7 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1683,6 +1683,8 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, if (len > INT_MAX) len = INT_MAX; + if (unlikely(!access_ok(VERIFY_READ, buff, len))) + return -EFAULT; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1742,6 +1744,8 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size, if (size > INT_MAX) size = INT_MAX; + if (unlikely(!access_ok(VERIFY_WRITE, ubuf, size))) + return -EFAULT; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; -- cgit v1.1 From 9f710c241217d368d716288fb4eb6305ea88692e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 22 Mar 2016 18:02:50 +0100 Subject: netfilter: x_tables: make sure e->next_offset covers remaining blob size Otherwise this function may read data beyond the ruleset blob. Change-Id: I22f514057d3e0403d1af61f4d9555403ab9f72ea Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/arp_tables.c | 6 ++++-- net/ipv4/netfilter/ip_tables.c | 6 ++++-- net/ipv6/netfilter/ip6_tables.c | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 1d6f562..42536c6 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -563,7 +563,8 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, unsigned int h; if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 || - (unsigned char *)e + sizeof(struct arpt_entry) >= limit) { + (unsigned char *)e + sizeof(struct arpt_entry) >= limit || + (unsigned char *)e + e->next_offset > limit) { duprintf("Bad offset %p\n", e); return -EINVAL; } @@ -1217,7 +1218,8 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, duprintf("check_compat_entry_size_and_hooks %p\n", e); if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 || - (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) { + (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit || + (unsigned char *)e + e->next_offset > limit) { duprintf("Bad offset %p, limit = %p\n", e, limit); return -EINVAL; } diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 795af4e..2f5ef5b 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -726,7 +726,8 @@ check_entry_size_and_hooks(struct ipt_entry *e, unsigned int h; if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 || - (unsigned char *)e + sizeof(struct ipt_entry) >= limit) { + (unsigned char *)e + sizeof(struct ipt_entry) >= limit || + (unsigned char *)e + e->next_offset > limit) { duprintf("Bad offset %p\n", e); return -EINVAL; } @@ -1483,7 +1484,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e, duprintf("check_compat_entry_size_and_hooks %p\n", e); if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 || - (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) { + (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit || + (unsigned char *)e + e->next_offset > limit) { duprintf("Bad offset %p, limit = %p\n", e, limit); return -EINVAL; } diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 959cc3f..387fc82 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -749,7 +749,8 @@ check_entry_size_and_hooks(struct ip6t_entry *e, unsigned int h; if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 || - (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) { + (unsigned char *)e + sizeof(struct ip6t_entry) >= limit || + (unsigned char *)e + e->next_offset > limit) { duprintf("Bad offset %p\n", e); return -EINVAL; } @@ -1507,7 +1508,8 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e, duprintf("check_compat_entry_size_and_hooks %p\n", e); if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 || - (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) { + (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit || + (unsigned char *)e + e->next_offset > limit) { duprintf("Bad offset %p, limit = %p\n", e, limit); return -EINVAL; } -- cgit v1.1 From 98bce590f5d092d1436a80b43f6dbedd174cd9e0 Mon Sep 17 00:00:00 2001 From: Aaron Knight Date: Sun, 24 Jul 2016 16:21:19 -0400 Subject: t0ltecdma: Disable USB Device FS -Appears to fix frequently occuring, annoying random reboots with no other observed regressions. I went from having up to 2-3 crashes an hour related to USB, to none all day -This option is deprecated and has been removed in later kernels versions anyways. Special Thanks: Simon Shields, for helping me interpet the crash dumps and pointing me in the right direction on this. Change-Id: Ieda0eb6e0dfb3fec4cfbe89540a587eaa6de7995 --- arch/arm/configs/cyanogenmod_t0ltecdma_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig index 1dd7240..4a914ff 100755 --- a/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig +++ b/arch/arm/configs/cyanogenmod_t0ltecdma_defconfig @@ -2436,7 +2436,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y -- cgit v1.1 From 9271694bc414bfacdc0854164b08d9686dc50b9b Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 1 Aug 2016 20:27:36 +1000 Subject: motor: max77693: expose min/max/default/threshold in sysfs based off a similar patch for klte by Kevin Haggerty Change-Id: If2b4f1f2c0310fc0a6c3fe49fd680973dce28ef5 --- drivers/motor/max77693_haptic.c | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/motor/max77693_haptic.c b/drivers/motor/max77693_haptic.c index beb7f2a..9c83c25 100644 --- a/drivers/motor/max77693_haptic.c +++ b/drivers/motor/max77693_haptic.c @@ -24,6 +24,11 @@ #include #include +#define PWM_MIN 0 +#define PWM_DEFAULT 50 +#define PWM_THRESH 75 +#define PWM_MAX 100 + static unsigned long pwm_val = 50; /* duty in percent */ static int pwm_duty = 27787; /* duty value, 37050=100%, 27787=50%, 18525=0% */ @@ -301,6 +306,42 @@ ssize_t pwm_value_store(struct device *dev, static DEVICE_ATTR(pwm_value, S_IRUGO | S_IWUSR, pwm_value_show, pwm_value_store); +static ssize_t pwm_default_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", PWM_DEFAULT); +} + +static DEVICE_ATTR(pwm_default, S_IRUGO, + pwm_default_show, NULL); + +static ssize_t pwm_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", PWM_MAX); +} + +static DEVICE_ATTR(pwm_max, S_IRUGO, + pwm_max_show, NULL); + +static ssize_t pwm_min_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", PWM_MIN); +} + +static DEVICE_ATTR(pwm_min, S_IRUGO, + pwm_min_show, NULL); + +static ssize_t pwm_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", PWM_THRESH); +} + +static DEVICE_ATTR(pwm_threshold, S_IRUGO, + pwm_threshold_show, NULL); + static int max77693_haptic_probe(struct platform_device *pdev) { int error = 0; @@ -378,6 +419,23 @@ static int max77693_haptic_probe(struct platform_device *pdev) if (error < 0) { pr_err("[VIB] create sysfs fail: pwm_value\n"); } + error = device_create_file(hap_data->tout_dev.dev, &dev_attr_pwm_max); + if (error < 0) { + pr_err("[VIB] create sysfs fail: pwm_max\n"); + } + error = device_create_file(hap_data->tout_dev.dev, &dev_attr_pwm_min); + if (error < 0) { + pr_err("[VIB] create sysfs fail: pwm_min\n"); + } + error = device_create_file(hap_data->tout_dev.dev, &dev_attr_pwm_default); + if (error < 0) { + pr_err("[VIB] create sysfs fail: pwm_default\n"); + } + error = device_create_file(hap_data->tout_dev.dev, &dev_attr_pwm_threshold); + if (error < 0) { + pr_err("[VIB] create sysfs fail: pwm_threshold\n"); + } + #endif pr_debug("[VIB] -- %s\n", __func__); -- cgit v1.1 From 6112c4d4e3408cf2aea5a626a3e39ba3922094c5 Mon Sep 17 00:00:00 2001 From: Zhao Wei Liew Date: Wed, 17 Aug 2016 08:26:53 +0100 Subject: power: max17042_battery: Set type to UNKNOWN This is a fuelgauge driver, not an actual battery driver. Setting its type to 'Battery' will confuse healthd, causing healthd to pick this driver instead of the actual battery driver for reading battery stats. Issue-Id: NIGHTLIES-3279 Change-Id: Ia45e74599d391a90cb526aa07a2525b64c3eec96 --- drivers/power/max17042_fuelgauge_u1.c | 2 +- include/linux/power_supply.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/max17042_fuelgauge_u1.c b/drivers/power/max17042_fuelgauge_u1.c index 39b931c..378dafe 100644 --- a/drivers/power/max17042_fuelgauge_u1.c +++ b/drivers/power/max17042_fuelgauge_u1.c @@ -884,7 +884,7 @@ static int __devinit max17042_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); chip->battery.name = "fuelgauge"; - chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; + chip->battery.type = POWER_SUPPLY_TYPE_UNKNOWN; chip->battery.get_property = max17042_get_property; chip->battery.set_property = max17042_set_property; chip->battery.properties = max17042_battery_props; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 8289fca..35b7ed6 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -146,6 +146,7 @@ enum power_supply_type { POWER_SUPPLY_TYPE_WIRELESS, POWER_SUPPLY_TYPE_UARTOFF, POWER_SUPPLY_TYPE_OTG, + POWER_SUPPLY_TYPE_UNKNOWN, }; enum { -- cgit v1.1 From 8aa6fa75955db416644e6c9367b350c25cb6745c Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 5 Oct 2015 16:13:04 +0200 Subject: mm: add a field to store names for private anonymous memory Userspace processes often have multiple allocators that each do anonymous mmaps to get memory. When examining memory usage of individual processes or systems as a whole, it is useful to be able to break down the various heaps that were allocated by each layer and examine their size, RSS, and physical memory usage. This patch adds a user pointer to the shared union in vm_area_struct that points to a null terminated string inside the user process containing a name for the vma. vmas that point to the same address will be merged, but vmas that point to equivalent strings at different addresses will not be merged. Userspace can set the name for a region of memory by calling prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, (unsigned long)name); Setting the name to NULL clears it. The names of named anonymous vmas are shown in /proc/pid/maps as [anon:] and in /proc/pid/smaps in a new "Name" field that is only present for named vmas. If the userspace pointer is no longer valid all or part of the name will be replaced with "". The idea to store a userspace pointer to reduce the complexity within mm (at the expense of the complexity of reading /proc/pid/mem) came from Dave Hansen. This results in no runtime overhead in the mm subsystem other than comparing the anon_name pointers when considering vma merging. The pointer is stored in a union with fieds that are only used on file-backed mappings, so it does not increase memory usage. Change-Id: I53b093d98dc24f41377824f34e076edced4a6f07 --- Documentation/filesystems/proc.txt | 6 ++ fs/proc/task_mmu.c | 63 ++++++++++++++++ include/linux/mm.h | 2 +- include/linux/mm_types.h | 15 ++++ include/linux/prctl.h | 3 + kernel/sys.c | 146 +++++++++++++++++++++++++++++++++++++ mm/madvise.c | 3 +- mm/mempolicy.c | 5 +- mm/mlock.c | 3 +- mm/mmap.c | 44 ++++++----- mm/mprotect.c | 3 +- 11 files changed, 269 insertions(+), 24 deletions(-) diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index db3b1ab..6b0d098 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -354,6 +354,8 @@ is not associated with a file: [stack] = the stack of the main process [vdso] = the "virtual dynamic shared object", the kernel system call handler + [anon:] = an anonymous mapping that has been + named by userspace or if empty, the mapping is anonymous. @@ -376,6 +378,7 @@ Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 374 kB +Name: name from userspace The first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping @@ -391,6 +394,9 @@ and a page is modified, the file page is replaced by a private anonymous copy. "Swap" shows how much would-be-anonymous memory is also used, but out on swap. +The "Name" field will only be present on a mapping that has been named by +userspace, and will show the name passed in by userspace. + This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 00e7ac4..f43c540 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -88,6 +88,56 @@ static void pad_len_spaces(struct seq_file *m, int len) seq_printf(m, "%*c", len, ' '); } +static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma) +{ + const char __user *name = vma_get_anon_name(vma); + struct mm_struct *mm = vma->vm_mm; + + unsigned long page_start_vaddr; + unsigned long page_offset; + unsigned long num_pages; + unsigned long max_len = NAME_MAX; + int i; + + page_start_vaddr = (unsigned long)name & PAGE_MASK; + page_offset = (unsigned long)name - page_start_vaddr; + num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE); + + seq_puts(m, "[anon:"); + + for (i = 0; i < num_pages; i++) { + int len; + int write_len; + const char *kaddr; + long pages_pinned; + struct page *page; + + pages_pinned = get_user_pages(current, mm, page_start_vaddr, + 1, 0, 0, &page, NULL); + if (pages_pinned < 1) { + seq_puts(m, "]"); + return; + } + + kaddr = (const char *)kmap(page); + len = min(max_len, PAGE_SIZE - page_offset); + write_len = strnlen(kaddr + page_offset, len); + seq_write(m, kaddr + page_offset, write_len); + kunmap(page); + put_page(page); + + /* if strnlen hit a null terminator then we're done */ + if (write_len != len) + break; + + max_len -= len; + page_offset = 0; + page_start_vaddr += PAGE_SIZE; + } + + seq_putc(m, ']'); +} + static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma) { if (vma && vma != priv->tail_vma) { @@ -264,7 +314,14 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) } else { name = "[vdso]"; } + goto done; } + + if (vma_get_anon_name(vma)) { + pad_len_spaces(m, len); + seq_print_vma_name(m, vma); + } +done: if (name) { pad_len_spaces(m, len); seq_puts(m, name); @@ -474,6 +531,12 @@ static int show_smap(struct seq_file *m, void *v) (vma->vm_flags & VM_LOCKED) ? (unsigned long)(mss.pss >> (10 + PSS_SHIFT)) : 0); + if (vma_get_anon_name(vma)) { + seq_puts(m, "Name: "); + seq_print_vma_name(m, vma); + seq_putc(m, '\n'); + } + if (m->count < m->size) /* vma is copied successfully */ m->version = (vma != get_gate_vma(task->mm)) ? vma->vm_start : 0; diff --git a/include/linux/mm.h b/include/linux/mm.h index d5dc6af..92a055b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1418,7 +1418,7 @@ extern int vma_adjust(struct vm_area_struct *vma, unsigned long start, extern struct vm_area_struct *vma_merge(struct mm_struct *, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t, - struct mempolicy *); + struct mempolicy *, const char __user *); extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *); extern int split_vma(struct mm_struct *, struct vm_area_struct *, unsigned long addr, int new_below); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 059839c..557f9b6 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -162,6 +162,10 @@ struct vm_area_struct { * linkage into the address_space->i_mmap prio tree, or * linkage to the list of like vmas hanging off its node, or * linkage of vma in the address_space->i_mmap_nonlinear list. + * + * For private anonymous mappings, a pointer to a null terminated string + * in the user process containing the name given to the vma, or NULL + * if unnamed. */ union { struct { @@ -171,6 +175,7 @@ struct vm_area_struct { } vm_set; struct raw_prio_tree_node prio_tree_node; + const char __user *anon_name; } shared; /* @@ -345,4 +350,14 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm) return mm->cpu_vm_mask_var; } + +/* Return the name for an anonymous mapping or NULL for a file-backed mapping */ +static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma) +{ + if (vma->vm_file) + return NULL; + + return vma->shared.anon_name; +} + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/linux/prctl.h b/include/linux/prctl.h index a3baeb2..dd90287 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -102,4 +102,7 @@ #define PR_MCE_KILL_GET 34 +#define PR_SET_VMA 0x53564d41 +# define PR_SET_VMA_ANON_NAME 0 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index be53817..79b66c0 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -40,6 +40,9 @@ #include #include #include +#include +#include +#include #include #include @@ -1660,6 +1663,146 @@ SYSCALL_DEFINE1(umask, int, mask) return mask; } + +static int prctl_update_vma_anon_name(struct vm_area_struct *vma, + struct vm_area_struct **prev, + unsigned long start, unsigned long end, + const char __user *name_addr) +{ + struct mm_struct * mm = vma->vm_mm; + int error = 0; + pgoff_t pgoff; + + if (name_addr == vma_get_anon_name(vma)) { + *prev = vma; + goto out; + } + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, + vma->vm_file, pgoff, vma_policy(vma), + name_addr); + if (*prev) { + vma = *prev; + goto success; + } + + *prev = vma; + + if (start != vma->vm_start) { + error = split_vma(mm, vma, start, 1); + if (error) + goto out; + } + + if (end != vma->vm_end) { + error = split_vma(mm, vma, end, 0); + if (error) + goto out; + } + +success: + if (!vma->vm_file) + vma->shared.anon_name = name_addr; + +out: + if (error == -ENOMEM) + error = -EAGAIN; + return error; +} + +static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, + unsigned long arg) +{ + unsigned long tmp; + struct vm_area_struct * vma, *prev; + int unmapped_error = 0; + int error = -EINVAL; + + /* + * If the interval [start,end) covers some unmapped address + * ranges, just ignore them, but return -ENOMEM at the end. + * - this matches the handling in madvise. + */ + vma = find_vma_prev(current->mm, start, &prev); + if (vma && start > vma->vm_start) + prev = vma; + + for (;;) { + /* Still start < end. */ + error = -ENOMEM; + if (!vma) + return error; + + /* Here start < (end|vma->vm_end). */ + if (start < vma->vm_start) { + unmapped_error = -ENOMEM; + start = vma->vm_start; + if (start >= end) + return error; + } + + /* Here vma->vm_start <= start < (end|vma->vm_end) */ + tmp = vma->vm_end; + if (end < tmp) + tmp = end; + + /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ + error = prctl_update_vma_anon_name(vma, &prev, start, end, + (const char __user *)arg); + if (error) + return error; + start = tmp; + if (prev && start < prev->vm_end) + start = prev->vm_end; + error = unmapped_error; + if (start >= end) + return error; + if (prev) + vma = prev->vm_next; + else /* madvise_remove dropped mmap_sem */ + vma = find_vma(current->mm, start); + } +} + +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + struct mm_struct *mm = current->mm; + int error; + unsigned long len; + unsigned long end; + + if (start & ~PAGE_MASK) + return -EINVAL; + len = (len_in + ~PAGE_MASK) & PAGE_MASK; + + /* Check to see whether len was rounded up from small -ve to zero */ + if (len_in && !len) + return -EINVAL; + + end = start + len; + if (end < start) + return -EINVAL; + + if (end == start) + return 0; + + down_write(&mm->mmap_sem); + + switch (opt) { + case PR_SET_VMA_ANON_NAME: + error = prctl_set_vma_anon_name(start, end, arg); + break; + default: + error = -EINVAL; + } + + up_write(&mm->mmap_sem); + + return error; +} + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { @@ -1808,6 +1951,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, else error = PR_MCE_KILL_DEFAULT; break; + case PR_SET_VMA: + error = prctl_set_vma(arg2, arg3, arg4, arg5); + break; default: error = -EINVAL; break; diff --git a/mm/madvise.c b/mm/madvise.c index deabe5f6..5a273af 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -87,7 +87,8 @@ static long madvise_behavior(struct vm_area_struct * vma, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma, - vma->vm_file, pgoff, vma_policy(vma)); + vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 7065fb3..7c8caa9 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -664,8 +664,9 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, vmend = min(end, vma->vm_end); pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); - prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, - vma->anon_vma, vma->vm_file, pgoff, new_pol); + prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, + vma->anon_vma, vma->vm_file, pgoff, + new_pol, vma_get_anon_name(name)); if (prev) { vma = prev; next = vma->vm_next; diff --git a/mm/mlock.c b/mm/mlock.c index b41b3b7..734fd7f 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -321,7 +321,8 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, - vma->vm_file, pgoff, vma_policy(vma)); + vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mmap.c b/mm/mmap.c index d1cf520..96f124e 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -656,7 +656,8 @@ again: remove_next = 1 + (end > next->vm_end); * per-vma resources, so we don't attempt to merge those. */ static inline int is_mergeable_vma(struct vm_area_struct *vma, - struct file *file, unsigned long vm_flags) + struct file *file, unsigned long vm_flags, + const char __user *anon_name) { /* VM_CAN_NONLINEAR may get set later by f_op->mmap() */ if ((vma->vm_flags ^ vm_flags) & ~VM_CAN_NONLINEAR) @@ -665,6 +666,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, return 0; if (vma->vm_ops && vma->vm_ops->close) return 0; + if (vma_get_anon_name(vma) != anon_name) + return 0; return 1; } @@ -695,9 +698,10 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, */ static int can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, - struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) + struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags) && + if (is_mergeable_vma(vma, file, vm_flags, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { if (vma->vm_pgoff == vm_pgoff) return 1; @@ -714,9 +718,10 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, */ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, - struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) + struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags) && + if (is_mergeable_vma(vma, file, vm_flags, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { pgoff_t vm_pglen; vm_pglen = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; @@ -727,9 +732,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, } /* - * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out - * whether that can be merged with its predecessor or its successor. - * Or both (it neatly fills a hole). + * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name), + * figure out whether that can be merged with its predecessor or its + * successor. Or both (it neatly fills a hole). * * In most cases - when called for mmap, brk or mremap - [addr,end) is * certain not to be mapped by the time vma_merge is called; but when @@ -759,7 +764,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, - pgoff_t pgoff, struct mempolicy *policy) + pgoff_t pgoff, struct mempolicy *policy, + const char __user *anon_name) { pgoff_t pglen = (end - addr) >> PAGE_SHIFT; struct vm_area_struct *area, *next; @@ -785,15 +791,15 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, */ if (prev && prev->vm_end == addr && mpol_equal(vma_policy(prev), policy) && - can_vma_merge_after(prev, vm_flags, - anon_vma, file, pgoff)) { + can_vma_merge_after(prev, vm_flags, anon_vma, + file, pgoff, anon_name)) { /* * OK, it can. Can we now merge in the successor as well? */ if (next && end == next->vm_start && mpol_equal(policy, vma_policy(next)) && - can_vma_merge_before(next, vm_flags, - anon_vma, file, pgoff+pglen) && + can_vma_merge_before(next, vm_flags, anon_vma, + file, pgoff+pglen, anon_name) && is_mergeable_anon_vma(prev->anon_vma, next->anon_vma, NULL)) { /* cases 1, 6 */ @@ -813,8 +819,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, */ if (next && end == next->vm_start && mpol_equal(policy, vma_policy(next)) && - can_vma_merge_before(next, vm_flags, - anon_vma, file, pgoff+pglen)) { + can_vma_merge_before(next, vm_flags, anon_vma, + file, pgoff+pglen, anon_name)) { if (prev && addr < prev->vm_end) /* case 4 */ err = vma_adjust(prev, prev->vm_start, addr, prev->vm_pgoff, NULL); @@ -1251,7 +1257,8 @@ munmap_back: /* * Can we just expand an old mapping? */ - vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL); + vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, + NULL, NULL); if (vma) goto out; @@ -2202,7 +2209,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len) /* Can we just expand an old private anonymous mapping? */ vma = vma_merge(mm, prev, addr, addr + len, flags, - NULL, NULL, pgoff, NULL); + NULL, NULL, pgoff, NULL, NULL); if (vma) goto out; @@ -2340,7 +2347,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent); new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, - vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); + vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (new_vma) { /* * Source vma may have been merged into new_vma diff --git a/mm/mprotect.c b/mm/mprotect.c index 5a688a2..5f168eb 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -179,7 +179,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, */ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *pprev = vma_merge(mm, *pprev, start, end, newflags, - vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); + vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*pprev) { vma = *pprev; goto success; -- cgit v1.1 From 02e91838f76a89400007f228ef1c4ade43588e87 Mon Sep 17 00:00:00 2001 From: Fenix46 Date: Mon, 13 Jun 2016 23:11:44 +0200 Subject: i9300_defconfig: enable LMK OOM_ADJ_VALUES and ADJ_RBTREE Change-Id: Icada3ee0531466768d33785c17809ede47066bdb --- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index 015d300..ced7ba8 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2876,6 +2876,8 @@ CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d CONFIG_ANDROID_TIMED_OUTPUT=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_LMK_ADJ_RBTREE=y # CONFIG_POHMELFS is not set # CONFIG_LINE6_USB is not set # CONFIG_USB_SERIAL_QUATECH2 is not set -- cgit v1.1 From 43c083aff0ed2b932af1142676adafbc4f9c12eb Mon Sep 17 00:00:00 2001 From: RGIB Date: Mon, 29 Aug 2016 09:41:19 +0200 Subject: smdk4412 : kona devices : new defconfig Change-Id: Iafe4ac15a4c198e3c016ab40fc6d631999c5bdaf --- arch/arm/configs/cyanogenmod_n5100_defconfig | 216 ++++++++++---------- arch/arm/configs/cyanogenmod_n5110_defconfig | 211 ++++++++++---------- arch/arm/configs/cyanogenmod_n5120_defconfig | 288 +++++++++++++-------------- 3 files changed, 356 insertions(+), 359 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_n5100_defconfig b/arch/arm/configs/cyanogenmod_n5100_defconfig index 57d3ef0..16096f5 100644 --- a/arch/arm/configs/cyanogenmod_n5100_defconfig +++ b/arch/arm/configs/cyanogenmod_n5100_defconfig @@ -70,6 +70,7 @@ CONFIG_GENERIC_IRQ_CHIP=y # RCU Subsystem # CONFIG_TREE_PREEMPT_RCU=y +# CONFIG_TINY_RCU is not set CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set CONFIG_RCU_FANOUT=32 @@ -107,6 +108,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_CC_CHECK_WARNING_STRICTLY is not set CONFIG_SYSCTL=y @@ -189,6 +191,8 @@ CONFIG_IOSCHED_ROW=y CONFIG_IOSCHED_SIO=y # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_ROW is not set +# CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set @@ -219,7 +223,7 @@ CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_WRITE_UNLOCK_BH is not set # CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set # CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set -CONFIG_MUTEX_SPIN_ON_OWNER=y +# CONFIG_MUTEX_SPIN_ON_OWNER is not set CONFIG_FREEZER=y # @@ -355,6 +359,7 @@ CONFIG_S5P_DEV_USB_EHCI=y CONFIG_S5P_DEV_FIMD_S5P=y CONFIG_S5P_DEV_USBGADGET=y CONFIG_S5P_MEM_CMA=y +CONFIG_S5P_DEV_MIPI_DSI=y # CONFIG_S5P_BTS is not set # CONFIG_S3C_DEV_TSI is not set CONFIG_ARCH_EXYNOS4=y @@ -369,7 +374,6 @@ CONFIG_EXYNOS4_LOWPWR_IDLE=y CONFIG_EXYNOS_MCT=y CONFIG_EXYNOS_DEV_PD=y CONFIG_EXYNOS4_DEV_FIMC_LITE=y -CONFIG_EXYNOS4_DEV_FIMC_IS=y CONFIG_EXYNOS4_SETUP_I2C1=y CONFIG_EXYNOS4_SETUP_I2C3=y CONFIG_EXYNOS4_SETUP_I2C4=y @@ -385,13 +389,13 @@ CONFIG_EXYNOS4_SETUP_FIMC0=y CONFIG_EXYNOS4_SETUP_FIMC1=y CONFIG_EXYNOS4_SETUP_FIMC2=y CONFIG_EXYNOS4_SETUP_FIMC3=y -CONFIG_EXYNOS4_SETUP_FIMC_IS=y CONFIG_EXYNOS4_SETUP_USB_PHY=y CONFIG_EXYNOS4_SETUP_CSIS=y CONFIG_EXYNOS4_SETUP_FB_S5P=y CONFIG_EXYNOS4_SETUP_TVOUT=y CONFIG_EXYNOS4_SETUP_THERMAL=y # CONFIG_EXYNOS_SETUP_THERMAL is not set +CONFIG_EXYNOS4_SETUP_MIPI_DSI=y CONFIG_EXYNOS4_SETUP_JPEG=y CONFIG_EXYNOS4_ENABLE_CLOCK_DOWN=y CONFIG_EXYNOS4_CPUFREQ=y @@ -420,7 +424,6 @@ CONFIG_BUSFREQ_QOS_1280X800=y # CONFIG_BUSFREQ_DEBUG is not set # CONFIG_BUSFREQ_L2_160M is not set CONFIG_SEC_THERMISTOR=y -# CONFIG_SEC_SUBTHERMISTOR is not set # CONFIG_EXYNOS_SYSREG_PM is not set CONFIG_ANDROID_WIP=y # CONFIG_COMPACTION_RETRY is not set @@ -449,6 +452,11 @@ CONFIG_TARGET_LOCALE_EUR=y # CONFIG_TARGET_LOCALE_JPN is not set # CONFIG_TARGET_LOCALE_CHN is not set # CONFIG_TARGET_LOCALE_USA is not set +CONFIG_MACH_KONA_EUR_OPEN=y +# CONFIG_MACH_KONA_EUR_WIFI is not set +# CONFIG_MACH_KONA_EUR_LTE is not set +# CONFIG_MACH_KONALTE_USA_ATT is not set +# CONFIG_MACH_KONA_KOR_WIFI is not set # CONFIG_MACH_SMDK4X12 is not set CONFIG_MACH_MIDAS=y # CONFIG_MACH_M0 is not set @@ -458,7 +466,6 @@ CONFIG_MACH_MIDAS=y # CONFIG_MACH_GC1 is not set # CONFIG_MACH_T0 is not set CONFIG_MACH_KONA=y -CONFIG_MACH_KONA_SENSOR=y # CONFIG_MACH_IRON is not set # CONFIG_MACH_GRANDE is not set # CONFIG_MACH_BAFFIN is not set @@ -471,21 +478,12 @@ CONFIG_MACH_KONA_SENSOR=y CONFIG_KONA_01_BD=y # CONFIG_IRON_BD is not set # CONFIG_GRANDE_BD is not set -# CONFIG_SLP is not set -# CONFIG_MACH_REDWOOD is not set -# CONFIG_GPS_BCM47511 is not set -CONFIG_GPS_BCM4752=y -# CONFIG_GPS_GSD4T is not set -# CONFIG_GPIO_NAPLES_00_BD is not set -# CONFIG_SLP_DISP_DEBUG is not set -# CONFIG_EXYNOS4_DEV_TMU is not set -# CONFIG_BT_TIZEN is not set # CONFIG_WRITEBACK_ENABLED is not set CONFIG_EXYNOS_SOUND_PLATFORM_DATA=y -CONFIG_USE_ADC_DET=y # CONFIG_JACK_FET is not set # CONFIG_JACK_GROUND_DET is not set -# CONFIG_SAMSUNG_ANALOG_UART_SWITCH is not set +CONFIG_USE_ADC_DET=y +CONFIG_SAMSUNG_ANALOG_UART_SWITCH=1 # CONFIG_EXYNOS5_DEV_BTS is not set # @@ -522,6 +520,7 @@ CONFIG_EXYNOS4_MSHC_DDR=y # CONFIG_SEC_DEBUG=y CONFIG_SEC_DEBUG_SCHED_LOG=y +# CONFIG_SEC_DEBUG_HRTIMER_LOG is not set # CONFIG_SEC_DEBUG_SOFTIRQ_LOG is not set CONFIG_SEC_DEBUG_SCHED_LOG_NONCACHED=y # CONFIG_SEC_DEBUG_SEMAPHORE_LOG is not set @@ -538,6 +537,7 @@ CONFIG_SEC_LOG=y CONFIG_SEC_LOG_NONCACHED=y CONFIG_SEC_LOG_LAST_KMSG=y CONFIG_EHCI_IRQ_DISTRIBUTION=y +CONFIG_EHCI_MODEM_PORTNUM=2 # # Samsung Modem Feature @@ -576,14 +576,15 @@ CONFIG_BT_MGMT=y # Qualcomm Modem Feature # # CONFIG_QC_MODEM is not set -# CONFIG_CPU_FREQ_TETHERING is not set # CONFIG_MSM_SUBSYSTEM_RESTART is not set # CONFIG_QC_MODEM_MDM9X15 is not set # CONFIG_MDM_HSIC_PM is not set # CONFIG_EMI_ERROR_RECOVERY is not set +# CONFIG_SIM_DETECT is not set CONFIG_USB_CDFS_SUPPORT=y # CONFIG_SAMSUNG_PRODUCT_SHIP is not set # CONFIG_CORESIGHT_ETM is not set +CONFIG_MACH_KONA_SENSOR=y # # Processor Type @@ -638,6 +639,9 @@ CONFIG_ARM_ERRATA_761320=y # CONFIG_ARM_ERRATA_761171 is not set # CONFIG_ARM_ERRATA_762974 is not set # CONFIG_ARM_ERRATA_763722 is not set +CONFIG_ARM_ERRATA_764369=y +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set CONFIG_ARM_GIC=y CONFIG_PL330=y # CONFIG_FIQ_DEBUGGER is not set @@ -648,8 +652,6 @@ CONFIG_PL330=y # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set # CONFIG_PCCARD is not set -CONFIG_ARM_ERRATA_764369=y -# CONFIG_PL310_ERRATA_769419 is not set # # Kernel Features @@ -698,12 +700,12 @@ CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +# CONFIG_ZSMALLOC is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y # CONFIG_DEBUG_VMALLOC is not set -# CONFIG_LOWMEM_CHECK is not set -CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_FORCE_MAX_ZONEORDER=12 CONFIG_ALIGNMENT_TRAP=y # CONFIG_UACCESS_WITH_MEMCPY is not set # CONFIG_SECCOMP is not set @@ -754,6 +756,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +# CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST is not set # CONFIG_CPU_FREQ_GOV_SLP is not set CONFIG_CPU_FREQ_DVFS_MONITOR=y CONFIG_CPU_IDLE=y @@ -805,6 +808,7 @@ CONFIG_ARCH_HAS_OPP=y CONFIG_PM_OPP=y CONFIG_PM_RUNTIME_CLK=y # CONFIG_SUSPEND_TIME is not set +CONFIG_CPU_PM=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -847,6 +851,8 @@ CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_CUBIC=y +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_RENO is not set CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_TCP_MD5SIG is not set CONFIG_IPV6=y @@ -1193,6 +1199,7 @@ CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set # CONFIG_CFG80211_ALLOW_RECONNECT is not set # CONFIG_MAC80211 is not set +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set # CONFIG_WIMAX is not set CONFIG_RFKILL=y CONFIG_RFKILL_PM=y @@ -1206,6 +1213,14 @@ CONFIG_RFKILL_PM=y # # Device Drivers # +CONFIG_MALI400=y +CONFIG_MALI_VER_R3P2=y +# CONFIG_MALI400_DEBUG is not set +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_DVFS=y +CONFIG_MALI400_UMP=y +# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_UMP_DEBUG is not set # # Generic Driver Options @@ -1261,7 +1276,6 @@ CONFIG_MISC_DEVICES=y # CONFIG_SENSORS_APDS990X is not set # CONFIG_HMC6352 is not set # CONFIG_SENSORS_AK8975 is not set -# CONFIG_SENSORS_AK8963 is not set # CONFIG_DS1682 is not set # CONFIG_TI_DAC7512 is not set CONFIG_UID_STAT=y @@ -1271,19 +1285,24 @@ CONFIG_UID_STAT=y # CONFIG_JACK_MON is not set # CONFIG_UART_SELECT is not set # CONFIG_SWITCH_DUAL_MODEM is not set -# CONFIG_SWITCH_USB_PATH_AUTO is not set # CONFIG_WIMAX_CMC is not set # CONFIG_SEC_DEV_JACK is not set # CONFIG_MUIC_DET_JACK is not set # CONFIG_FM34_WE395 is not set # CONFIG_AUDIENCE_ES305 is not set # CONFIG_2MIC_FM34_WE395 is not set +# CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT is not set +CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK=y +CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK=y +# CONFIG_MUIC_MAX77693_SUPPORT_CAR_DOCK is not set # CONFIG_USBHUB_USB3503 is not set # CONFIG_USBHUB_USB3503_OTG_CONN is not set # CONFIG_USBHUB_USB3803 is not set # CONFIG_PN544 is not set +# CONFIG_STMPE811_ADC is not set # CONFIG_MPU_SENSORS_MPU3050 is not set # CONFIG_MPU_SENSORS_MPU6050 is not set +CONFIG_UID_CPUTIME=y # CONFIG_C2PORT is not set # @@ -1308,10 +1327,13 @@ CONFIG_SEC_MODEM=y CONFIG_UMTS_MODEM_XMM6262=y # CONFIG_CDMA_MODEM_CBP71 is not set # CONFIG_CDMA_MODEM_CBP72 is not set +# CONFIG_CDMA_MODEM_CBP82 is not set # CONFIG_LTE_MODEM_CMC221 is not set +# CONFIG_UMTS_MODEM_SS222 is not set # CONFIG_CDMA_MODEM_MDM6600 is not set # CONFIG_TDSCDMA_MODEM_SPRD8803 is not set # CONFIG_GSM_MODEM_ESC6270 is not set +# CONFIG_CDMA_MODEM_QSC6085 is not set # CONFIG_LINK_DEVICE_MIPI is not set # CONFIG_LINK_DEVICE_DPRAM is not set # CONFIG_LINK_DEVICE_PLD is not set @@ -1319,13 +1341,13 @@ CONFIG_UMTS_MODEM_XMM6262=y CONFIG_LINK_DEVICE_HSIC=y # CONFIG_LINK_DEVICE_C2C is not set # CONFIG_LINK_DEVICE_SPI is not set +# CONFIG_BOOT_DEVICE_SPI is not set # CONFIG_WORKQUEUE_FRONT is not set # CONFIG_IPC_CMC22x_OLD_RFS is not set # CONFIG_SIPC_VER_5 is not set # CONFIG_SIM_SLOT_SWITCH is not set # CONFIG_LTE_MODEM_CMC220 is not set # CONFIG_INTERNAL_MODEM_IF is not set -# CONFIG_CDMA_MODEM_QSC6085 is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -1406,9 +1428,16 @@ CONFIG_WLAN=y # CONFIG_USB_NET_RNDIS_WLAN is not set CONFIG_WIFI_CONTROL_FUNC=y # CONFIG_ATH_COMMON is not set +# CONFIG_B43LEGACY_DMA_AND_PIO_MODE is not set +# CONFIG_B43LEGACY_DMA_MODE is not set +# CONFIG_B43LEGACY_PIO_MODE is not set # CONFIG_BCM4330 is not set CONFIG_BCM4334=m +# CONFIG_BCM4335 is not set +# CONFIG_BCM4339 is not set +# CONFIG_BCM4354 is not set # CONFIG_BCM43241 is not set +CONFIG_BROADCOM_WIFI=y CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y @@ -1417,7 +1446,6 @@ CONFIG_WLAN_REGION_CODE=100 # CONFIG_IWM is not set # CONFIG_LIBERTAS is not set # CONFIG_MWIFIEX is not set -# CONFIG_LGUIWLAN is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -1479,8 +1507,6 @@ CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_SECBRIDGE is not set CONFIG_INPUT_KEYRESET=y # CONFIG_INPUT_FBSUSPEND is not set -# CONFIG_INPUT_MPU6050 is not set -# CONFIG_INPUT_MPU6050_POLLING is not set # # Input Device Drivers @@ -1504,7 +1530,9 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set +CONFIG_SENSORS_HALL=y # CONFIG_KEYBOARD_CYPRESS_TOUCH is not set +# CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set @@ -1562,7 +1590,7 @@ CONFIG_TOUCHSCREEN_SYNAPTICS_S7301=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYS=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_WORKAROUND=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYLED=y -# CONFIG_TOUCHSCREEN_CYPRESS_TMA46X is not set +# CONFIG_TOUCHSCREEN_CYTTSP4 is not set CONFIG_SEC_TOUCHSCREEN_DVFS_LOCK=y CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH=y # CONFIG_KEYPAD_MELFAS_TOUCH is not set @@ -1570,27 +1598,6 @@ CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH=y CONFIG_INPUT_WACOM=y # CONFIG_EPEN_WACOM_G5SP is not set # CONFIG_EPEN_WACOM_G9PM is not set -CONFIG_EPEN_WACOM_G9PL=y -# CONFIG_RMI4_DEBUG is not set -# CONFIG_RMI4_BUS is not set -# CONFIG_RMI4_GENERIC is not set -# CONFIG_RMI4_F09 is not set -# CONFIG_RMI4_F1A is not set -# CONFIG_RMI4_F11 is not set -# CONFIG_RMI4_VIRTUAL_BUTTONS is not set -# CONFIG_RMI4_F17 is not set -# CONFIG_RMI4_F19 is not set -# CONFIG_RMI4_F21 is not set -# CONFIG_RMI4_F30 is not set -# CONFIG_RMI4_F31 is not set -# CONFIG_RMI4_F34 is not set -# CONFIG_RMI4_F41 is not set -# CONFIG_RMI4_F54 is not set -# CONFIG_RMI4_SMB is not set -# CONFIG_RMI4_I2C is not set -# CONFIG_RMI4_SPI is not set -# CONFIG_RMI4_DEV is not set -# CONFIG_RMI4_FWLIB is not set CONFIG_INPUT_MISC=y # CONFIG_SENSORS_BH1721FVC is not set # CONFIG_INPUT_AD714X is not set @@ -1613,7 +1620,6 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_FLIP is not set # CONFIG_INPUT_KR3DH is not set - # # Hardware I/O ports # @@ -1825,10 +1831,15 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_MAX17042_FUELGAUGE is not set # CONFIG_BATTERY_MAX17047_FUELGAUGE is not set CONFIG_BATTERY_MAX17047_C_FUELGAUGE=y -CONFIG_BATTERY_MAX77693_CHARGER=y # CONFIG_BATTERY_SMB136_CHARGER is not set +CONFIG_BATTERY_MAX77693_CHARGER=y +# CONFIG_BATTERY_WPC_CHARGER is not set # CONFIG_BATTERY_SAMSUNG_P1X is not set +# CONFIG_FUELGAUGE_DUMMY is not set +# CONFIG_FUELGAUGE_MAX17048 is not set +# CONFIG_CHARGER_DUMMY is not set # CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_SMB328 is not set # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set # CONFIG_TEST_POWER is not set @@ -1851,16 +1862,13 @@ CONFIG_BATTERY_SAMSUNG=y # CONFIG_SMB328_CHARGER is not set # CONFIG_SMB347_CHARGER is not set # CONFIG_CHARGER_MANAGER is not set -CONFIG_SAMSUNG_LPM_MODE=y # CONFIG_HWMON is not set CONFIG_THERMAL=y # CONFIG_CPU_THERMAL is not set # CONFIG_SENSORS_EXYNOS4_TMU is not set CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set -# CONFIG_CHARGER_NCP1851 is not set -# CONFIG_FUELGAUGE_MAX17050 is not set -# CONFIG_FUELGAUGE_MAX17050_COULOMB_COUNTING is not set + # # Watchdog Device Drivers # @@ -2056,12 +2064,6 @@ CONFIG_VIDEO_IR_I2C=y # CONFIG_VIDEO_TVP5150 is not set # CONFIG_VIDEO_TVP7002 is not set # CONFIG_VIDEO_VPX3220 is not set -# CONFIG_VIDEO_S5K3H2 is not set -# CONFIG_VIDEO_S5K3H7 is not set -# CONFIG_VIDEO_S5K4E5 is not set -# CONFIG_VIDEO_S5K6A3 is not set -# CONFIG_S5K6A3_CSI_C is not set -# CONFIG_S5K6A3_CSI_D is not set # CONFIG_VIDEO_M5MO is not set # CONFIG_VIDEO_M9MO is not set # CONFIG_VIDEO_S5K5BAFX is not set @@ -2069,12 +2071,12 @@ CONFIG_VIDEO_IR_I2C=y # CONFIG_VIDEO_SR200PC20 is not set # CONFIG_VIDEO_SR200PC20M is not set CONFIG_VIDEO_ISX012=y -CONFIG_VIDEO_SR130PC20=y # CONFIG_VIDEO_SLP_S5K4ECGX is not set # CONFIG_VIDEO_SLP_DB8131M is not set # CONFIG_VIDEO_S5K4EA is not set # CONFIG_VIDEO_S5C73M3 is not set # CONFIG_VIDEO_SLP_S5C73M3 is not set +CONFIG_VIDEO_SR130PC20=y CONFIG_VIDEO_IMPROVE_STREAMOFF=y # @@ -2141,7 +2143,6 @@ CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y # CONFIG_USB_ZR364XX is not set # CONFIG_USB_STKWEBCAM is not set # CONFIG_USB_S2255 is not set -CONFIG_MALI_400MP_UMP=y CONFIG_VIDEO_SAMSUNG=y CONFIG_VIDEO_SAMSUNG_V4L2=y CONFIG_VIDEO_FIMC=y @@ -2170,6 +2171,8 @@ CONFIG_LSI_HDMI_AUDIO_CH_EVENT=y CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 # CONFIG_VIDEO_MFC5X_DEBUG is not set +# CONFIG_VIDEO_MALI400MP is not set +# CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y # CONFIG_VIDEO_FIMG2D_DEBUG is not set CONFIG_VIDEO_FIMG2D4X=y @@ -2191,20 +2194,10 @@ CONFIG_VIDEO_SAMSUNG_MEMSIZE_JPEG=0 CONFIG_VIDEO_SAMSUNG_MEMSIZE_TVOUT=0 CONFIG_VIDEO_EXYNOS=y CONFIG_VIDEO_EXYNOS_MEMSIZE_FIMC_IS=12288 -CONFIG_EXYNOS_MEDIA_DEVICE=y -# CONFIG_VIDEO_EXYNOS_FIMC_LITE is not set - -# -# Reserved memory configurations -# -CONFIG_VIDEO_SAMSUNG_MEMSIZE_FLITE0=10240 -CONFIG_VIDEO_SAMSUNG_MEMSIZE_FLITE1=10240 -# CONFIG_VIDEO_EXYNOS_MIPI_CSIS is not set +# CONFIG_VIDEO_EXYNOS_FIMC_LITE is not set # CONFIG_VIDEO_EXYNOS_TV is not set # CONFIG_VIDEO_EXYNOS_ROTATOR is not set -# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set -# CONFIG_VIDEO_EXYNOS_FIMC_IS_BAYER is not set -CONFIG_MEDIA_EXYNOS=y +# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set CONFIG_V4L_MEM2MEM_DRIVERS=y # CONFIG_VIDEO_MEM2MEM_TESTDEV is not set @@ -2221,29 +2214,14 @@ CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE=y # CONFIG_ISDBT is not set # -# MUIC device -# -# CONFIG_STMPE811_ADC is not set -CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK=y -CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK=y - -# # Graphics support # -# CONFIG_MALI_VER_BEFORE_R3P2 is not set # CONFIG_DRM is not set CONFIG_ION=y CONFIG_ION_EXYNOS=y CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=81920 # CONFIG_ION_EXYNOS_CONTIGHEAP_DEBUG is not set -# CONFIG_VITHAR is not set -CONFIG_MALI400=y -CONFIG_MALI_VER_R3P2=y -# CONFIG_MALI400_DEBUG is not set -# CONFIG_MALI400_PROFILING is not set -CONFIG_MALI_DVFS=y -CONFIG_MALI400_UMP=y -# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set @@ -2274,6 +2252,7 @@ CONFIG_FB_S5P_SPLASH_SCREEN=y # CONFIG_FB_S5P_LCD_INIT is not set # CONFIG_FB_S5P_DEBUG is not set CONFIG_FB_S5P_VSYNC_THREAD=y +# CONFIG_FB_S5P_VSYNC_SEND_UEVENTS is not set CONFIG_FB_S5P_VSYNC_SYSFS=y # CONFIG_FB_S5P_TRACE_UNDERRUN is not set CONFIG_FB_S5P_DEFAULT_WINDOW=3 @@ -2282,12 +2261,18 @@ CONFIG_FB_S5P_NR_BUFFERS=2 CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMD=8192 CONFIG_FB_S5P_MDNIE=y CONFIG_FB_MDNIE_PWM=y -CONFIG_FB_EBOOK_PANEL_SCENARIO=y CONFIG_FB_S5P_MIPI_DSIM=y CONFIG_FB_BGRA_ORDER=y # CONFIG_FB_RGBA_ORDER is not set # CONFIG_FB_S5P_S6C1372 is not set # CONFIG_FB_S5P_LD9040 is not set +# CONFIG_FB_S5P_LMS501XX is not set +# CONFIG_FB_S5P_DUMMY_MIPI_LCD is not set +# CONFIG_FB_S5P_S6E8AA0 is not set +# CONFIG_FB_S5P_EA8061 is not set +# CONFIG_FB_S5P_S6EVR02 is not set +# CONFIG_FB_S5P_S6D6AA1 is not set +# CONFIG_FB_S5P_S6E63M0 is not set CONFIG_FB_S5P_NT71391=y # CONFIG_LCD_FREQ_SWITCH is not set CONFIG_FB_S5P_EXTDSP=y @@ -2296,10 +2281,10 @@ CONFIG_FB_S5P_EXTDSP_NR_BUFFERS=3 # CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD is not set # CONFIG_FB_S1D13XXX is not set # CONFIG_FB_TMIO is not set -# CONFIG_S5P_MIPI_DSI2 is not set # CONFIG_FB_UDL is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX_PCI_GDC is not set # CONFIG_FB_BROADSHEET is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y @@ -2351,7 +2336,6 @@ CONFIG_SND_JACK=y # CONFIG_SND_VERBOSE_PROCFS is not set # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set -# CONFIG_SND_DEBUG_VERBOSE is not set # CONFIG_SND_RAWMIDI_SEQ is not set # CONFIG_SND_OPL3_LIB_SEQ is not set # CONFIG_SND_OPL4_LIB_SEQ is not set @@ -2369,6 +2353,7 @@ CONFIG_SND_SOC=y # CONFIG_SND_SOC_CACHE_LZO is not set CONFIG_SND_SOC_SAMSUNG=y CONFIG_SND_SAMSUNG_I2S=y +# CONFIG_SND_SOC_SAMSUNG_MIDAS_WM1811 is not set CONFIG_SND_SOC_SAMSUNG_KONA_WM1811=y # CONFIG_SND_SOC_SAMSUNG_USE_DMA_WRAPPER is not set CONFIG_SND_SOC_SAMSUNG_I2S_SEC=y @@ -2406,7 +2391,6 @@ CONFIG_USB_HID=y # # Special HID drivers # -CONFIG_UHID=y CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y # CONFIG_HID_ACRUX_FF is not set @@ -2552,7 +2536,6 @@ CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_KARMA is not set # CONFIG_USB_STORAGE_CYPRESS_ATACB is not set # CONFIG_USB_STORAGE_ENE_UB6250 is not set -# CONFIG_USB_UAS is not set # CONFIG_USB_LIBUSUAL is not set # @@ -2654,6 +2637,12 @@ CONFIG_USB_GADGET_SELECTED=y CONFIG_USB_GADGET_S3C_OTGD=y # CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_CI13XXX_PCI is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_EG20T is not set # CONFIG_USB_GADGET_DUMMY_HCD is not set # @@ -2750,12 +2739,14 @@ CONFIG_LEDS_CLASS=y # CONFIG_LEDS_BD2802 is not set # CONFIG_LEDS_LT3593 is not set # CONFIG_LEDS_SWITCH is not set +# CONFIG_LEDS_MAX77693 is not set # CONFIG_LEDS_AAT1290A is not set # CONFIG_LEDS_TRIGGERS is not set # # LED Triggers # +# CONFIG_LEDS_TRIGGER_NOTIFICATION is not set # CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y @@ -2868,14 +2859,14 @@ CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d CONFIG_ANDROID_TIMED_OUTPUT=y # CONFIG_ANDROID_TIMED_GPIO is not set CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_LMK_ADJ_RBTREE=y # CONFIG_POHMELFS is not set # CONFIG_LINE6_USB is not set # CONFIG_USB_SERIAL_QUATECH2 is not set # CONFIG_USB_SERIAL_QUATECH_USB2 is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -# CONFIG_XVMALLOC is not set -# CONFIG_ZRAM is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -2901,7 +2892,6 @@ CONFIG_CLKDEV_LOOKUP=y CONFIG_VIBETONZ=y CONFIG_MOTOR_DRV_MAX77693=y # CONFIG_MOTOR_DRV_ISA1200 is not set -# CONFIG_MOTOR_DRV_DRV2603 is not set # CONFIG_FM_RADIO is not set CONFIG_SENSORS_CORE=y # CONFIG_SENSORS_AK8975C is not set @@ -2909,10 +2899,7 @@ CONFIG_SENSORS_CORE=y # CONFIG_SENSORS_BMP180 is not set # CONFIG_SENSORS_CM3663 is not set # CONFIG_SENSORS_PAS2M110 is not set -CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=2 # CONFIG_SENSORS_BMA254 is not set -CONFIG_SENSORS_YAS532=y -CONFIG_SENSORS_YAS_ORI=y # CONFIG_SENSORS_TAOS is not set CONFIG_SENSORS_GP2A=y # CONFIG_SENSORS_GP2A_ANALOG is not set @@ -2925,6 +2912,9 @@ CONFIG_SENSOR_K3DH_INPUTDEV=y # CONFIG_SENSORS_K3G is not set # CONFIG_SENSORS_LSM330DLC is not set # CONFIG_SENSORS_LPS331 is not set +CONFIG_SENSORS_YAS532=y +CONFIG_SENSORS_YAS_ORI=y +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=2 # CONFIG_SENSORS_SYSFS is not set # CONFIG_SENSORS_SSP is not set # CONFIG_SENSORS_SSP_LSM330 is not set @@ -2946,13 +2936,13 @@ CONFIG_IR_REMOCON=y CONFIG_IR_REMOCON_MC96=y # CONFIG_EXTCON is not set # CONFIG_BARCODE_EMUL is not set +# CONFIG_SAMSUNG_PHONE_TTY is not set CONFIG_MOBICORE_SUPPORT=y # CONFIG_MOBICORE_DEBUG is not set CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set # CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR is not set -# CONFIG_J4FS is not set # # File systems @@ -2969,6 +2959,7 @@ CONFIG_EXT4_FS_SECURITY=y # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y # CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_XFS_FS is not set @@ -3031,6 +3022,10 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS 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_LOGFS is not set # CONFIG_CRAMFS is not set # CONFIG_SQUASHFS is not set @@ -3040,9 +3035,13 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set +# CONFIG_ROMFS_BACKED_BY_BLOCK is not set +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y @@ -3140,7 +3139,6 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y -CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y @@ -3286,6 +3284,9 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_SECURITY_APPARMOR is not set # CONFIG_IMA is not set CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set # CONFIG_DEFAULT_SECURITY_DAC is not set CONFIG_DEFAULT_SECURITY="selinux" CONFIG_CRYPTO=y @@ -3386,6 +3387,8 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set # CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation @@ -3415,6 +3418,9 @@ CONFIG_ZLIB_DEFLATE=y # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y CONFIG_TEXTSEARCH=y CONFIG_TEXTSEARCH_KMP=y CONFIG_TEXTSEARCH_BM=y @@ -3424,5 +3430,3 @@ CONFIG_HAS_DMA=y CONFIG_CPU_RMAP=y CONFIG_NLATTR=y # CONFIG_AVERAGE is not set - -CONFIG_SENSORS_HALL=y diff --git a/arch/arm/configs/cyanogenmod_n5110_defconfig b/arch/arm/configs/cyanogenmod_n5110_defconfig index 1000a70..c5e7be1 100644 --- a/arch/arm/configs/cyanogenmod_n5110_defconfig +++ b/arch/arm/configs/cyanogenmod_n5110_defconfig @@ -70,6 +70,7 @@ CONFIG_GENERIC_IRQ_CHIP=y # RCU Subsystem # CONFIG_TREE_PREEMPT_RCU=y +# CONFIG_TINY_RCU is not set CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set CONFIG_RCU_FANOUT=32 @@ -107,6 +108,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_CC_CHECK_WARNING_STRICTLY is not set CONFIG_SYSCTL=y @@ -189,6 +191,8 @@ CONFIG_IOSCHED_ROW=y CONFIG_IOSCHED_SIO=y # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_ROW is not set +# CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set @@ -219,7 +223,7 @@ CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_WRITE_UNLOCK_BH is not set # CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set # CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set -CONFIG_MUTEX_SPIN_ON_OWNER=y +# CONFIG_MUTEX_SPIN_ON_OWNER is not set CONFIG_FREEZER=y # @@ -355,6 +359,7 @@ CONFIG_S5P_DEV_USB_EHCI=y CONFIG_S5P_DEV_FIMD_S5P=y CONFIG_S5P_DEV_USBGADGET=y CONFIG_S5P_MEM_CMA=y +CONFIG_S5P_DEV_MIPI_DSI=y # CONFIG_S5P_BTS is not set # CONFIG_S3C_DEV_TSI is not set CONFIG_ARCH_EXYNOS4=y @@ -369,7 +374,6 @@ CONFIG_EXYNOS4_LOWPWR_IDLE=y CONFIG_EXYNOS_MCT=y CONFIG_EXYNOS_DEV_PD=y CONFIG_EXYNOS4_DEV_FIMC_LITE=y -CONFIG_EXYNOS4_DEV_FIMC_IS=y CONFIG_EXYNOS4_SETUP_I2C1=y CONFIG_EXYNOS4_SETUP_I2C3=y CONFIG_EXYNOS4_SETUP_I2C4=y @@ -385,13 +389,13 @@ CONFIG_EXYNOS4_SETUP_FIMC0=y CONFIG_EXYNOS4_SETUP_FIMC1=y CONFIG_EXYNOS4_SETUP_FIMC2=y CONFIG_EXYNOS4_SETUP_FIMC3=y -CONFIG_EXYNOS4_SETUP_FIMC_IS=y CONFIG_EXYNOS4_SETUP_USB_PHY=y CONFIG_EXYNOS4_SETUP_CSIS=y CONFIG_EXYNOS4_SETUP_FB_S5P=y CONFIG_EXYNOS4_SETUP_TVOUT=y CONFIG_EXYNOS4_SETUP_THERMAL=y # CONFIG_EXYNOS_SETUP_THERMAL is not set +CONFIG_EXYNOS4_SETUP_MIPI_DSI=y CONFIG_EXYNOS4_SETUP_JPEG=y CONFIG_EXYNOS4_ENABLE_CLOCK_DOWN=y CONFIG_EXYNOS4_CPUFREQ=y @@ -420,7 +424,6 @@ CONFIG_BUSFREQ_QOS_1280X800=y # CONFIG_BUSFREQ_DEBUG is not set # CONFIG_BUSFREQ_L2_160M is not set CONFIG_SEC_THERMISTOR=y -# CONFIG_SEC_SUBTHERMISTOR is not set # CONFIG_EXYNOS_SYSREG_PM is not set CONFIG_ANDROID_WIP=y # CONFIG_COMPACTION_RETRY is not set @@ -449,6 +452,11 @@ CONFIG_TARGET_LOCALE_EUR=y # CONFIG_TARGET_LOCALE_JPN is not set # CONFIG_TARGET_LOCALE_CHN is not set # CONFIG_TARGET_LOCALE_USA is not set +CONFIG_MACH_KONA_EUR_OPEN=y +# CONFIG_MACH_KONA_EUR_WIFI is not set +# CONFIG_MACH_KONA_EUR_LTE is not set +# CONFIG_MACH_KONALTE_USA_ATT is not set +# CONFIG_MACH_KONA_KOR_WIFI is not set # CONFIG_MACH_SMDK4X12 is not set CONFIG_MACH_MIDAS=y # CONFIG_MACH_M0 is not set @@ -458,7 +466,6 @@ CONFIG_MACH_MIDAS=y # CONFIG_MACH_GC1 is not set # CONFIG_MACH_T0 is not set CONFIG_MACH_KONA=y -CONFIG_MACH_KONA_SENSOR=y # CONFIG_MACH_IRON is not set # CONFIG_MACH_GRANDE is not set # CONFIG_MACH_BAFFIN is not set @@ -471,21 +478,12 @@ CONFIG_MACH_KONA_SENSOR=y CONFIG_KONA_01_BD=y # CONFIG_IRON_BD is not set # CONFIG_GRANDE_BD is not set -# CONFIG_SLP is not set -# CONFIG_MACH_REDWOOD is not set -# CONFIG_GPS_BCM47511 is not set -CONFIG_GPS_BCM4752=y -# CONFIG_GPS_GSD4T is not set -# CONFIG_GPIO_NAPLES_00_BD is not set -# CONFIG_SLP_DISP_DEBUG is not set -# CONFIG_EXYNOS4_DEV_TMU is not set -# CONFIG_BT_TIZEN is not set # CONFIG_WRITEBACK_ENABLED is not set CONFIG_EXYNOS_SOUND_PLATFORM_DATA=y -CONFIG_USE_ADC_DET=y # CONFIG_JACK_FET is not set # CONFIG_JACK_GROUND_DET is not set -# CONFIG_SAMSUNG_ANALOG_UART_SWITCH is not set +CONFIG_USE_ADC_DET=y +CONFIG_SAMSUNG_ANALOG_UART_SWITCH=1 # CONFIG_EXYNOS5_DEV_BTS is not set # @@ -539,6 +537,7 @@ CONFIG_SEC_LOG=y CONFIG_SEC_LOG_NONCACHED=y CONFIG_SEC_LOG_LAST_KMSG=y CONFIG_EHCI_IRQ_DISTRIBUTION=y +CONFIG_EHCI_MODEM_PORTNUM=2 # # Samsung Modem Feature @@ -577,7 +576,6 @@ CONFIG_BT_MGMT=y # Qualcomm Modem Feature # # CONFIG_QC_MODEM is not set -# CONFIG_CPU_FREQ_TETHERING is not set # CONFIG_MSM_SUBSYSTEM_RESTART is not set # CONFIG_QC_MODEM_MDM9X15 is not set # CONFIG_MDM_HSIC_PM is not set @@ -586,6 +584,7 @@ CONFIG_SIM_DETECT=y CONFIG_USB_CDFS_SUPPORT=y # CONFIG_SAMSUNG_PRODUCT_SHIP is not set # CONFIG_CORESIGHT_ETM is not set +CONFIG_MACH_KONA_SENSOR=y # # Processor Type @@ -640,6 +639,9 @@ CONFIG_ARM_ERRATA_761320=y # CONFIG_ARM_ERRATA_761171 is not set # CONFIG_ARM_ERRATA_762974 is not set # CONFIG_ARM_ERRATA_763722 is not set +CONFIG_ARM_ERRATA_764369=y +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set CONFIG_ARM_GIC=y CONFIG_PL330=y # CONFIG_FIQ_DEBUGGER is not set @@ -650,8 +652,6 @@ CONFIG_PL330=y # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set # CONFIG_PCCARD is not set -CONFIG_ARM_ERRATA_764369=y -# CONFIG_PL310_ERRATA_769419 is not set # # Kernel Features @@ -700,12 +700,12 @@ CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +# CONFIG_ZSMALLOC is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y # CONFIG_DEBUG_VMALLOC is not set -# CONFIG_LOWMEM_CHECK is not set -CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_FORCE_MAX_ZONEORDER=12 CONFIG_ALIGNMENT_TRAP=y # CONFIG_UACCESS_WITH_MEMCPY is not set # CONFIG_SECCOMP is not set @@ -756,6 +756,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +# CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST is not set # CONFIG_CPU_FREQ_GOV_SLP is not set CONFIG_CPU_FREQ_DVFS_MONITOR=y CONFIG_CPU_IDLE=y @@ -807,6 +808,7 @@ CONFIG_ARCH_HAS_OPP=y CONFIG_PM_OPP=y CONFIG_PM_RUNTIME_CLK=y # CONFIG_SUSPEND_TIME is not set +CONFIG_CPU_PM=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -849,6 +851,8 @@ CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_CUBIC=y +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_RENO is not set CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_TCP_MD5SIG is not set CONFIG_IPV6=y @@ -1195,6 +1199,7 @@ CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set # CONFIG_CFG80211_ALLOW_RECONNECT is not set # CONFIG_MAC80211 is not set +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set # CONFIG_WIMAX is not set CONFIG_RFKILL=y CONFIG_RFKILL_PM=y @@ -1208,6 +1213,14 @@ CONFIG_RFKILL_PM=y # # Device Drivers # +CONFIG_MALI400=y +CONFIG_MALI_VER_R3P2=y +# CONFIG_MALI400_DEBUG is not set +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_DVFS=y +CONFIG_MALI400_UMP=y +# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_UMP_DEBUG is not set # # Generic Driver Options @@ -1263,7 +1276,6 @@ CONFIG_MISC_DEVICES=y # CONFIG_SENSORS_APDS990X is not set # CONFIG_HMC6352 is not set # CONFIG_SENSORS_AK8975 is not set -# CONFIG_SENSORS_AK8963 is not set # CONFIG_DS1682 is not set # CONFIG_TI_DAC7512 is not set CONFIG_UID_STAT=y @@ -1273,19 +1285,24 @@ CONFIG_UID_STAT=y # CONFIG_JACK_MON is not set # CONFIG_UART_SELECT is not set # CONFIG_SWITCH_DUAL_MODEM is not set -# CONFIG_SWITCH_USB_PATH_AUTO is not set # CONFIG_WIMAX_CMC is not set # CONFIG_SEC_DEV_JACK is not set # CONFIG_MUIC_DET_JACK is not set # CONFIG_FM34_WE395 is not set # CONFIG_AUDIENCE_ES305 is not set # CONFIG_2MIC_FM34_WE395 is not set +# CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT is not set +CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK=y +CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK=y +# CONFIG_MUIC_MAX77693_SUPPORT_CAR_DOCK is not set # CONFIG_USBHUB_USB3503 is not set # CONFIG_USBHUB_USB3503_OTG_CONN is not set # CONFIG_USBHUB_USB3803 is not set # CONFIG_PN544 is not set +# CONFIG_STMPE811_ADC is not set # CONFIG_MPU_SENSORS_MPU3050 is not set # CONFIG_MPU_SENSORS_MPU6050 is not set +CONFIG_UID_CPUTIME=y # CONFIG_C2PORT is not set # @@ -1310,10 +1327,13 @@ CONFIG_SEC_MODEM=y CONFIG_UMTS_MODEM_XMM6262=y # CONFIG_CDMA_MODEM_CBP71 is not set # CONFIG_CDMA_MODEM_CBP72 is not set +# CONFIG_CDMA_MODEM_CBP82 is not set # CONFIG_LTE_MODEM_CMC221 is not set +# CONFIG_UMTS_MODEM_SS222 is not set # CONFIG_CDMA_MODEM_MDM6600 is not set # CONFIG_TDSCDMA_MODEM_SPRD8803 is not set # CONFIG_GSM_MODEM_ESC6270 is not set +# CONFIG_CDMA_MODEM_QSC6085 is not set # CONFIG_LINK_DEVICE_MIPI is not set # CONFIG_LINK_DEVICE_DPRAM is not set # CONFIG_LINK_DEVICE_PLD is not set @@ -1321,13 +1341,13 @@ CONFIG_UMTS_MODEM_XMM6262=y CONFIG_LINK_DEVICE_HSIC=y # CONFIG_LINK_DEVICE_C2C is not set # CONFIG_LINK_DEVICE_SPI is not set +# CONFIG_BOOT_DEVICE_SPI is not set # CONFIG_WORKQUEUE_FRONT is not set # CONFIG_IPC_CMC22x_OLD_RFS is not set # CONFIG_SIPC_VER_5 is not set # CONFIG_SIM_SLOT_SWITCH is not set # CONFIG_LTE_MODEM_CMC220 is not set # CONFIG_INTERNAL_MODEM_IF is not set -# CONFIG_CDMA_MODEM_QSC6085 is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -1408,9 +1428,16 @@ CONFIG_WLAN=y # CONFIG_USB_NET_RNDIS_WLAN is not set CONFIG_WIFI_CONTROL_FUNC=y # CONFIG_ATH_COMMON is not set +# CONFIG_B43LEGACY_DMA_AND_PIO_MODE is not set +# CONFIG_B43LEGACY_DMA_MODE is not set +# CONFIG_B43LEGACY_PIO_MODE is not set # CONFIG_BCM4330 is not set CONFIG_BCM4334=m +# CONFIG_BCM4335 is not set +# CONFIG_BCM4339 is not set +# CONFIG_BCM4354 is not set # CONFIG_BCM43241 is not set +CONFIG_BROADCOM_WIFI=y CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y @@ -1419,7 +1446,6 @@ CONFIG_WLAN_REGION_CODE=100 # CONFIG_IWM is not set # CONFIG_LIBERTAS is not set # CONFIG_MWIFIEX is not set -# CONFIG_LGUIWLAN is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -1481,8 +1507,6 @@ CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_SECBRIDGE is not set CONFIG_INPUT_KEYRESET=y # CONFIG_INPUT_FBSUSPEND is not set -# CONFIG_INPUT_MPU6050 is not set -# CONFIG_INPUT_MPU6050_POLLING is not set # # Input Device Drivers @@ -1506,7 +1530,9 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set +CONFIG_SENSORS_HALL=y # CONFIG_KEYBOARD_CYPRESS_TOUCH is not set +# CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set @@ -1564,7 +1590,7 @@ CONFIG_TOUCHSCREEN_SYNAPTICS_S7301=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYS=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_WORKAROUND=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYLED=y -# CONFIG_TOUCHSCREEN_CYPRESS_TMA46X is not set +# CONFIG_TOUCHSCREEN_CYTTSP4 is not set CONFIG_SEC_TOUCHSCREEN_DVFS_LOCK=y CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH=y # CONFIG_KEYPAD_MELFAS_TOUCH is not set @@ -1572,27 +1598,6 @@ CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH=y CONFIG_INPUT_WACOM=y # CONFIG_EPEN_WACOM_G5SP is not set # CONFIG_EPEN_WACOM_G9PM is not set -CONFIG_EPEN_WACOM_G9PL=y -# CONFIG_RMI4_DEBUG is not set -# CONFIG_RMI4_BUS is not set -# CONFIG_RMI4_GENERIC is not set -# CONFIG_RMI4_F09 is not set -# CONFIG_RMI4_F1A is not set -# CONFIG_RMI4_F11 is not set -# CONFIG_RMI4_VIRTUAL_BUTTONS is not set -# CONFIG_RMI4_F17 is not set -# CONFIG_RMI4_F19 is not set -# CONFIG_RMI4_F21 is not set -# CONFIG_RMI4_F30 is not set -# CONFIG_RMI4_F31 is not set -# CONFIG_RMI4_F34 is not set -# CONFIG_RMI4_F41 is not set -# CONFIG_RMI4_F54 is not set -# CONFIG_RMI4_SMB is not set -# CONFIG_RMI4_I2C is not set -# CONFIG_RMI4_SPI is not set -# CONFIG_RMI4_DEV is not set -# CONFIG_RMI4_FWLIB is not set CONFIG_INPUT_MISC=y # CONFIG_SENSORS_BH1721FVC is not set # CONFIG_INPUT_AD714X is not set @@ -1826,10 +1831,15 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_MAX17042_FUELGAUGE is not set # CONFIG_BATTERY_MAX17047_FUELGAUGE is not set CONFIG_BATTERY_MAX17047_C_FUELGAUGE=y -CONFIG_BATTERY_MAX77693_CHARGER=y # CONFIG_BATTERY_SMB136_CHARGER is not set +CONFIG_BATTERY_MAX77693_CHARGER=y +# CONFIG_BATTERY_WPC_CHARGER is not set # CONFIG_BATTERY_SAMSUNG_P1X is not set +# CONFIG_FUELGAUGE_DUMMY is not set +# CONFIG_FUELGAUGE_MAX17048 is not set +# CONFIG_CHARGER_DUMMY is not set # CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_SMB328 is not set # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set # CONFIG_TEST_POWER is not set @@ -1852,16 +1862,13 @@ CONFIG_BATTERY_SAMSUNG=y # CONFIG_SMB328_CHARGER is not set # CONFIG_SMB347_CHARGER is not set # CONFIG_CHARGER_MANAGER is not set -CONFIG_SAMSUNG_LPM_MODE=y # CONFIG_HWMON is not set CONFIG_THERMAL=y # CONFIG_CPU_THERMAL is not set # CONFIG_SENSORS_EXYNOS4_TMU is not set CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set -# CONFIG_CHARGER_NCP1851 is not set -# CONFIG_FUELGAUGE_MAX17050 is not set -# CONFIG_FUELGAUGE_MAX17050_COULOMB_COUNTING is not set + # # Watchdog Device Drivers # @@ -2057,12 +2064,6 @@ CONFIG_VIDEO_IR_I2C=y # CONFIG_VIDEO_TVP5150 is not set # CONFIG_VIDEO_TVP7002 is not set # CONFIG_VIDEO_VPX3220 is not set -# CONFIG_VIDEO_S5K3H2 is not set -# CONFIG_VIDEO_S5K3H7 is not set -# CONFIG_VIDEO_S5K4E5 is not set -# CONFIG_VIDEO_S5K6A3 is not set -# CONFIG_S5K6A3_CSI_C is not set -# CONFIG_S5K6A3_CSI_D is not set # CONFIG_VIDEO_M5MO is not set # CONFIG_VIDEO_M9MO is not set # CONFIG_VIDEO_S5K5BAFX is not set @@ -2070,12 +2071,12 @@ CONFIG_VIDEO_IR_I2C=y # CONFIG_VIDEO_SR200PC20 is not set # CONFIG_VIDEO_SR200PC20M is not set CONFIG_VIDEO_ISX012=y -CONFIG_VIDEO_SR130PC20=y # CONFIG_VIDEO_SLP_S5K4ECGX is not set # CONFIG_VIDEO_SLP_DB8131M is not set # CONFIG_VIDEO_S5K4EA is not set # CONFIG_VIDEO_S5C73M3 is not set # CONFIG_VIDEO_SLP_S5C73M3 is not set +CONFIG_VIDEO_SR130PC20=y CONFIG_VIDEO_IMPROVE_STREAMOFF=y # @@ -2142,7 +2143,6 @@ CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y # CONFIG_USB_ZR364XX is not set # CONFIG_USB_STKWEBCAM is not set # CONFIG_USB_S2255 is not set -CONFIG_MALI_400MP_UMP=y CONFIG_VIDEO_SAMSUNG=y CONFIG_VIDEO_SAMSUNG_V4L2=y CONFIG_VIDEO_FIMC=y @@ -2171,6 +2171,8 @@ CONFIG_LSI_HDMI_AUDIO_CH_EVENT=y CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 # CONFIG_VIDEO_MFC5X_DEBUG is not set +# CONFIG_VIDEO_MALI400MP is not set +# CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y # CONFIG_VIDEO_FIMG2D_DEBUG is not set CONFIG_VIDEO_FIMG2D4X=y @@ -2192,20 +2194,10 @@ CONFIG_VIDEO_SAMSUNG_MEMSIZE_JPEG=0 CONFIG_VIDEO_SAMSUNG_MEMSIZE_TVOUT=0 CONFIG_VIDEO_EXYNOS=y CONFIG_VIDEO_EXYNOS_MEMSIZE_FIMC_IS=12288 -CONFIG_EXYNOS_MEDIA_DEVICE=y -# CONFIG_VIDEO_EXYNOS_FIMC_LITE is not set - -# -# Reserved memory configurations -# -CONFIG_VIDEO_SAMSUNG_MEMSIZE_FLITE0=10240 -CONFIG_VIDEO_SAMSUNG_MEMSIZE_FLITE1=10240 -# CONFIG_VIDEO_EXYNOS_MIPI_CSIS is not set +# CONFIG_VIDEO_EXYNOS_FIMC_LITE is not set # CONFIG_VIDEO_EXYNOS_TV is not set # CONFIG_VIDEO_EXYNOS_ROTATOR is not set -# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set -# CONFIG_VIDEO_EXYNOS_FIMC_IS_BAYER is not set -CONFIG_MEDIA_EXYNOS=y +# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set CONFIG_V4L_MEM2MEM_DRIVERS=y # CONFIG_VIDEO_MEM2MEM_TESTDEV is not set @@ -2222,29 +2214,14 @@ CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE=y # CONFIG_ISDBT is not set # -# MUIC device -# -# CONFIG_STMPE811_ADC is not set -CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK=y -CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK=y - -# # Graphics support # -# CONFIG_MALI_VER_BEFORE_R3P2 is not set # CONFIG_DRM is not set CONFIG_ION=y CONFIG_ION_EXYNOS=y CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=81920 # CONFIG_ION_EXYNOS_CONTIGHEAP_DEBUG is not set -# CONFIG_VITHAR is not set -CONFIG_MALI400=y -CONFIG_MALI_VER_R3P2=y -# CONFIG_MALI400_DEBUG is not set -# CONFIG_MALI400_PROFILING is not set -CONFIG_MALI_DVFS=y -CONFIG_MALI400_UMP=y -# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set @@ -2275,6 +2252,7 @@ CONFIG_FB_S5P_SPLASH_SCREEN=y # CONFIG_FB_S5P_LCD_INIT is not set # CONFIG_FB_S5P_DEBUG is not set CONFIG_FB_S5P_VSYNC_THREAD=y +# CONFIG_FB_S5P_VSYNC_SEND_UEVENTS is not set CONFIG_FB_S5P_VSYNC_SYSFS=y # CONFIG_FB_S5P_TRACE_UNDERRUN is not set CONFIG_FB_S5P_DEFAULT_WINDOW=3 @@ -2283,12 +2261,18 @@ CONFIG_FB_S5P_NR_BUFFERS=2 CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMD=8192 CONFIG_FB_S5P_MDNIE=y CONFIG_FB_MDNIE_PWM=y -CONFIG_FB_EBOOK_PANEL_SCENARIO=y CONFIG_FB_S5P_MIPI_DSIM=y CONFIG_FB_BGRA_ORDER=y # CONFIG_FB_RGBA_ORDER is not set # CONFIG_FB_S5P_S6C1372 is not set # CONFIG_FB_S5P_LD9040 is not set +# CONFIG_FB_S5P_LMS501XX is not set +# CONFIG_FB_S5P_DUMMY_MIPI_LCD is not set +# CONFIG_FB_S5P_S6E8AA0 is not set +# CONFIG_FB_S5P_EA8061 is not set +# CONFIG_FB_S5P_S6EVR02 is not set +# CONFIG_FB_S5P_S6D6AA1 is not set +# CONFIG_FB_S5P_S6E63M0 is not set CONFIG_FB_S5P_NT71391=y # CONFIG_LCD_FREQ_SWITCH is not set CONFIG_FB_S5P_EXTDSP=y @@ -2297,10 +2281,10 @@ CONFIG_FB_S5P_EXTDSP_NR_BUFFERS=3 # CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD is not set # CONFIG_FB_S1D13XXX is not set # CONFIG_FB_TMIO is not set -# CONFIG_S5P_MIPI_DSI2 is not set # CONFIG_FB_UDL is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX_PCI_GDC is not set # CONFIG_FB_BROADSHEET is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y @@ -2370,6 +2354,7 @@ CONFIG_SND_SOC=y # CONFIG_SND_SOC_CACHE_LZO is not set CONFIG_SND_SOC_SAMSUNG=y CONFIG_SND_SAMSUNG_I2S=y +# CONFIG_SND_SOC_SAMSUNG_MIDAS_WM1811 is not set CONFIG_SND_SOC_SAMSUNG_KONA_WM1811=y # CONFIG_SND_SOC_SAMSUNG_USE_DMA_WRAPPER is not set CONFIG_SND_SOC_SAMSUNG_I2S_SEC=y @@ -2552,7 +2537,6 @@ CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_KARMA is not set # CONFIG_USB_STORAGE_CYPRESS_ATACB is not set # CONFIG_USB_STORAGE_ENE_UB6250 is not set -# CONFIG_USB_UAS is not set # CONFIG_USB_LIBUSUAL is not set # @@ -2654,6 +2638,12 @@ CONFIG_USB_GADGET_SELECTED=y CONFIG_USB_GADGET_S3C_OTGD=y # CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_CI13XXX_PCI is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_EG20T is not set # CONFIG_USB_GADGET_DUMMY_HCD is not set # @@ -2750,12 +2740,14 @@ CONFIG_LEDS_CLASS=y # CONFIG_LEDS_BD2802 is not set # CONFIG_LEDS_LT3593 is not set # CONFIG_LEDS_SWITCH is not set +# CONFIG_LEDS_MAX77693 is not set # CONFIG_LEDS_AAT1290A is not set # CONFIG_LEDS_TRIGGERS is not set # # LED Triggers # +# CONFIG_LEDS_TRIGGER_NOTIFICATION is not set # CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y @@ -2868,14 +2860,14 @@ CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d CONFIG_ANDROID_TIMED_OUTPUT=y # CONFIG_ANDROID_TIMED_GPIO is not set CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_LMK_ADJ_RBTREE=y # CONFIG_POHMELFS is not set # CONFIG_LINE6_USB is not set # CONFIG_USB_SERIAL_QUATECH2 is not set # CONFIG_USB_SERIAL_QUATECH_USB2 is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -# CONFIG_XVMALLOC is not set -# CONFIG_ZRAM is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -2901,7 +2893,6 @@ CONFIG_CLKDEV_LOOKUP=y CONFIG_VIBETONZ=y CONFIG_MOTOR_DRV_MAX77693=y # CONFIG_MOTOR_DRV_ISA1200 is not set -# CONFIG_MOTOR_DRV_DRV2603 is not set # CONFIG_FM_RADIO is not set CONFIG_SENSORS_CORE=y # CONFIG_SENSORS_AK8975C is not set @@ -2909,10 +2900,7 @@ CONFIG_SENSORS_CORE=y # CONFIG_SENSORS_BMP180 is not set # CONFIG_SENSORS_CM3663 is not set # CONFIG_SENSORS_PAS2M110 is not set -CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=2 # CONFIG_SENSORS_BMA254 is not set -CONFIG_SENSORS_YAS532=y -CONFIG_SENSORS_YAS_ORI=y # CONFIG_SENSORS_TAOS is not set # CONFIG_SENSORS_GP2A is not set # CONFIG_SENSORS_GP2A_ANALOG is not set @@ -2925,6 +2913,9 @@ CONFIG_SENSOR_K3DH_INPUTDEV=y # CONFIG_SENSORS_K3G is not set # CONFIG_SENSORS_LSM330DLC is not set # CONFIG_SENSORS_LPS331 is not set +CONFIG_SENSORS_YAS532=y +CONFIG_SENSORS_YAS_ORI=y +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=2 # CONFIG_SENSORS_SYSFS is not set # CONFIG_SENSORS_SSP is not set # CONFIG_SENSORS_SSP_LSM330 is not set @@ -2946,13 +2937,13 @@ CONFIG_IR_REMOCON=y CONFIG_IR_REMOCON_MC96=y # CONFIG_EXTCON is not set # CONFIG_BARCODE_EMUL is not set +# CONFIG_SAMSUNG_PHONE_TTY is not set CONFIG_MOBICORE_SUPPORT=y # CONFIG_MOBICORE_DEBUG is not set CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set # CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR is not set -# CONFIG_J4FS is not set # # File systems @@ -2969,6 +2960,7 @@ CONFIG_EXT4_FS_SECURITY=y # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y # CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_XFS_FS is not set @@ -3031,6 +3023,10 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS 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_LOGFS is not set # CONFIG_CRAMFS is not set # CONFIG_SQUASHFS is not set @@ -3040,9 +3036,13 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set +# CONFIG_ROMFS_BACKED_BY_BLOCK is not set +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y @@ -3140,7 +3140,6 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y -CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y @@ -3286,6 +3285,9 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_SECURITY_APPARMOR is not set # CONFIG_IMA is not set CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set # CONFIG_DEFAULT_SECURITY_DAC is not set CONFIG_DEFAULT_SECURITY="selinux" CONFIG_CRYPTO=y @@ -3386,6 +3388,8 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set # CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation @@ -3415,6 +3419,9 @@ CONFIG_ZLIB_DEFLATE=y # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y CONFIG_TEXTSEARCH=y CONFIG_TEXTSEARCH_KMP=y CONFIG_TEXTSEARCH_BM=y @@ -3424,5 +3431,3 @@ CONFIG_HAS_DMA=y CONFIG_CPU_RMAP=y CONFIG_NLATTR=y # CONFIG_AVERAGE is not set - -CONFIG_SENSORS_HALL=y diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index bc698f1..d039b39 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -70,6 +70,7 @@ CONFIG_GENERIC_IRQ_CHIP=y # RCU Subsystem # CONFIG_TREE_PREEMPT_RCU=y +# CONFIG_TINY_RCU is not set CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set CONFIG_RCU_FANOUT=32 @@ -107,6 +108,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_CC_CHECK_WARNING_STRICTLY is not set CONFIG_SYSCTL=y @@ -189,6 +191,8 @@ CONFIG_IOSCHED_ROW=y CONFIG_IOSCHED_SIO=y # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_ROW is not set +# CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set @@ -355,6 +359,7 @@ CONFIG_S5P_DEV_USB_EHCI=y CONFIG_S5P_DEV_FIMD_S5P=y CONFIG_S5P_DEV_USBGADGET=y CONFIG_S5P_MEM_CMA=y +CONFIG_S5P_DEV_MIPI_DSI=y # CONFIG_S5P_BTS is not set # CONFIG_S3C_DEV_TSI is not set CONFIG_ARCH_EXYNOS4=y @@ -369,7 +374,6 @@ CONFIG_EXYNOS4_LOWPWR_IDLE=y CONFIG_EXYNOS_MCT=y CONFIG_EXYNOS_DEV_PD=y CONFIG_EXYNOS4_DEV_FIMC_LITE=y -CONFIG_EXYNOS4_DEV_FIMC_IS=y CONFIG_EXYNOS4_SETUP_I2C1=y CONFIG_EXYNOS4_SETUP_I2C3=y CONFIG_EXYNOS4_SETUP_I2C4=y @@ -385,13 +389,13 @@ CONFIG_EXYNOS4_SETUP_FIMC0=y CONFIG_EXYNOS4_SETUP_FIMC1=y CONFIG_EXYNOS4_SETUP_FIMC2=y CONFIG_EXYNOS4_SETUP_FIMC3=y -CONFIG_EXYNOS4_SETUP_FIMC_IS=y CONFIG_EXYNOS4_SETUP_USB_PHY=y CONFIG_EXYNOS4_SETUP_CSIS=y CONFIG_EXYNOS4_SETUP_FB_S5P=y CONFIG_EXYNOS4_SETUP_TVOUT=y CONFIG_EXYNOS4_SETUP_THERMAL=y # CONFIG_EXYNOS_SETUP_THERMAL is not set +CONFIG_EXYNOS4_SETUP_MIPI_DSI=y CONFIG_EXYNOS4_SETUP_JPEG=y CONFIG_EXYNOS4_ENABLE_CLOCK_DOWN=y CONFIG_EXYNOS4_CPUFREQ=y @@ -420,7 +424,6 @@ CONFIG_BUSFREQ_QOS_1280X800=y # CONFIG_BUSFREQ_DEBUG is not set # CONFIG_BUSFREQ_L2_160M is not set CONFIG_SEC_THERMISTOR=y -# CONFIG_SEC_SUBTHERMISTOR is not set # CONFIG_EXYNOS_SYSREG_PM is not set CONFIG_ANDROID_WIP=y # CONFIG_COMPACTION_RETRY is not set @@ -449,6 +452,11 @@ CONFIG_TARGET_LOCALE_EUR=y # CONFIG_TARGET_LOCALE_JPN is not set # CONFIG_TARGET_LOCALE_CHN is not set # CONFIG_TARGET_LOCALE_USA is not set +# CONFIG_MACH_KONA_EUR_OPEN is not set +# CONFIG_MACH_KONA_EUR_WIFI is not set +CONFIG_MACH_KONA_EUR_LTE=y +# CONFIG_MACH_KONALTE_USA_ATT is not set +# CONFIG_MACH_KONA_KOR_WIFI is not set # CONFIG_MACH_SMDK4X12 is not set CONFIG_MACH_MIDAS=y # CONFIG_MACH_M0 is not set @@ -458,7 +466,6 @@ CONFIG_MACH_MIDAS=y # CONFIG_MACH_GC1 is not set # CONFIG_MACH_T0 is not set CONFIG_MACH_KONA=y -CONFIG_MACH_KONA_SENSOR=y # CONFIG_MACH_IRON is not set # CONFIG_MACH_GRANDE is not set # CONFIG_MACH_BAFFIN is not set @@ -469,24 +476,14 @@ CONFIG_MACH_KONA_SENSOR=y # CONFIG_T0_04_BD is not set # CONFIG_KONA_00_BD is not set CONFIG_KONA_01_BD=y -CONFIG_MACH_KONA_EUR_LTE=y # CONFIG_IRON_BD is not set # CONFIG_GRANDE_BD is not set -# CONFIG_SLP is not set -# CONFIG_MACH_REDWOOD is not set -# CONFIG_GPS_BCM47511 is not set -# CONFIG_GPS_BCM4752 is not set -# CONFIG_GPS_GSD4T is not set -# CONFIG_GPIO_NAPLES_00_BD is not set -# CONFIG_SLP_DISP_DEBUG is not set -# CONFIG_EXYNOS4_DEV_TMU is not set -# CONFIG_BT_TIZEN is not set # CONFIG_WRITEBACK_ENABLED is not set CONFIG_EXYNOS_SOUND_PLATFORM_DATA=y -CONFIG_USE_ADC_DET=y # CONFIG_JACK_FET is not set # CONFIG_JACK_GROUND_DET is not set -# CONFIG_SAMSUNG_ANALOG_UART_SWITCH is not set +CONFIG_USE_ADC_DET=y +CONFIG_SAMSUNG_ANALOG_UART_SWITCH=1 # CONFIG_EXYNOS5_DEV_BTS is not set # @@ -523,7 +520,6 @@ CONFIG_EXYNOS4_MSHC_DDR=y # CONFIG_SEC_DEBUG=y CONFIG_SEC_DEBUG_SCHED_LOG=y -CONFIG_SEC_DEBUG_HRTIMER_LOG=y # CONFIG_SEC_DEBUG_SOFTIRQ_LOG is not set CONFIG_SEC_DEBUG_SCHED_LOG_NONCACHED=y # CONFIG_SEC_DEBUG_SEMAPHORE_LOG is not set @@ -542,29 +538,6 @@ CONFIG_SEC_LOG_LAST_KMSG=y CONFIG_EHCI_IRQ_DISTRIBUTION=y # -# Samsung Modem Feature -# -# CONFIG_LTE_VIA_SWITCH is not set -# CONFIG_SEC_DUAL_MODEM_MODE is not set -# CONFIG_SEC_MODEM_M0_C2C is not set -# CONFIG_SEC_MODEM_M0 is not set -# CONFIG_SEC_MODEM_M0_CTC is not set -# CONFIG_SEC_MODEM_T0_CU_DUOS is not set -# CONFIG_SEC_MODEM_T0_OPEN_DUOS is not set -# CONFIG_SEC_MODEM_M0_GRANDECTC is not set -# CONFIG_SEC_MODEM_M1 is not set -# CONFIG_SEC_MODEM_C1 is not set -# CONFIG_SEC_MODEM_C1_LGT is not set -# CONFIG_SEC_MODEM_M2 is not set -# CONFIG_SEC_MODEM_U1 is not set -# CONFIG_SEC_MODEM_U1_LGT is not set -# CONFIG_SEC_MODEM_GAIA is not set -# CONFIG_SEC_MODEM_IRON is not set -# CONFIG_SEC_MODEM_P8LTE is not set -# CONFIG_SEC_MODEM_T0_TD_DUAL is not set -# CONFIG_SEC_MODEM_U1_SPR is not set - -# # Connectivity Feature # # CONFIG_GPS_BRCM_475X is not set @@ -578,17 +551,16 @@ CONFIG_BT_MGMT=y # Qualcomm Modem Feature # CONFIG_QC_MODEM=y -CONFIG_SIM_DETECT=y -CONFIG_MSM_RMNET_USB=y -CONFIG_SENSORS_HALL=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_QC_MODEM_MDM9X15=y CONFIG_MDM_HSIC_PM=y # CONFIG_EMI_ERROR_RECOVERY is not set +CONFIG_SIM_DETECT=y CONFIG_QC_MODEM_M3=y CONFIG_USB_CDFS_SUPPORT=y CONFIG_SAMSUNG_PRODUCT_SHIP=y # CONFIG_CORESIGHT_ETM is not set +CONFIG_MACH_KONA_SENSOR=y # # Processor Type @@ -643,6 +615,9 @@ CONFIG_ARM_ERRATA_761320=y # CONFIG_ARM_ERRATA_761171 is not set # CONFIG_ARM_ERRATA_762974 is not set # CONFIG_ARM_ERRATA_763722 is not set +CONFIG_ARM_ERRATA_764369=y +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set CONFIG_ARM_GIC=y CONFIG_PL330=y # CONFIG_FIQ_DEBUGGER is not set @@ -653,8 +628,6 @@ CONFIG_PL330=y # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set # CONFIG_PCCARD is not set -CONFIG_ARM_ERRATA_764369=y -# CONFIG_PL310_ERRATA_769419 is not set # # Kernel Features @@ -703,12 +676,12 @@ CONFIG_VIRT_TO_BUS=y CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +# CONFIG_ZSMALLOC is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y # CONFIG_DEBUG_VMALLOC is not set -# CONFIG_LOWMEM_CHECK is not set -CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_FORCE_MAX_ZONEORDER=12 CONFIG_ALIGNMENT_TRAP=y # CONFIG_UACCESS_WITH_MEMCPY is not set # CONFIG_SECCOMP is not set @@ -759,6 +732,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +# CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST is not set # CONFIG_CPU_FREQ_GOV_SLP is not set CONFIG_CPU_FREQ_DVFS_MONITOR=y CONFIG_CPU_IDLE=y @@ -810,6 +784,7 @@ CONFIG_ARCH_HAS_OPP=y CONFIG_PM_OPP=y CONFIG_PM_RUNTIME_CLK=y # CONFIG_SUSPEND_TIME is not set +CONFIG_CPU_PM=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -852,6 +827,8 @@ CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_CUBIC=y +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_RENO is not set CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_TCP_MD5SIG is not set CONFIG_IPV6=y @@ -876,6 +853,7 @@ CONFIG_IPV6_TUNNEL=y CONFIG_IPV6_MULTIPLE_TABLES=y # CONFIG_IPV6_SUBTREES is not set # CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set CONFIG_ANDROID_PARANOID_NETWORK=y CONFIG_NET_ACTIVITY_STATS=y CONFIG_NETWORK_SECMARK=y @@ -892,6 +870,7 @@ CONFIG_NETFILTER_NETLINK_QUEUE=y CONFIG_NETFILTER_NETLINK_LOG=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_SECMARK is not set CONFIG_NF_CONNTRACK_EVENTS=y # CONFIG_NF_CONNTRACK_TIMESTAMP is not set CONFIG_NF_CT_PROTO_DCCP=y @@ -922,6 +901,7 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set # CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y @@ -937,6 +917,7 @@ CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y # CONFIG_NETFILTER_XT_TARGET_TEE is not set CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set CONFIG_NETFILTER_XT_TARGET_TCPMSS=y # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -1023,6 +1004,7 @@ CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_ECN is not set # CONFIG_IP_NF_TARGET_TTL is not set CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -1049,6 +1031,7 @@ CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_TARGET_REJECT_SKERR=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set # CONFIG_RDS is not set @@ -1192,6 +1175,7 @@ CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set # CONFIG_CFG80211_ALLOW_RECONNECT is not set # CONFIG_MAC80211 is not set +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set # CONFIG_WIMAX is not set CONFIG_RFKILL=y CONFIG_RFKILL_PM=y @@ -1205,6 +1189,14 @@ CONFIG_RFKILL_PM=y # # Device Drivers # +CONFIG_MALI400=y +CONFIG_MALI_VER_R3P2=y +# CONFIG_MALI400_DEBUG is not set +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_DVFS=y +CONFIG_MALI400_UMP=y +# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_UMP_DEBUG is not set # # Generic Driver Options @@ -1260,7 +1252,6 @@ CONFIG_MISC_DEVICES=y # CONFIG_SENSORS_APDS990X is not set # CONFIG_HMC6352 is not set # CONFIG_SENSORS_AK8975 is not set -# CONFIG_SENSORS_AK8963 is not set # CONFIG_DS1682 is not set # CONFIG_TI_DAC7512 is not set CONFIG_UID_STAT=y @@ -1270,19 +1261,24 @@ CONFIG_UID_STAT=y # CONFIG_JACK_MON is not set # CONFIG_UART_SELECT is not set # CONFIG_SWITCH_DUAL_MODEM is not set -# CONFIG_SWITCH_USB_PATH_AUTO is not set # CONFIG_WIMAX_CMC is not set # CONFIG_SEC_DEV_JACK is not set # CONFIG_MUIC_DET_JACK is not set # CONFIG_FM34_WE395 is not set # CONFIG_AUDIENCE_ES305 is not set # CONFIG_2MIC_FM34_WE395 is not set +# CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT is not set +CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK=y +CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK=y +# CONFIG_MUIC_MAX77693_SUPPORT_CAR_DOCK is not set # CONFIG_USBHUB_USB3503 is not set # CONFIG_USBHUB_USB3503_OTG_CONN is not set # CONFIG_USBHUB_USB3803 is not set # CONFIG_PN544 is not set +# CONFIG_STMPE811_ADC is not set # CONFIG_MPU_SENSORS_MPU3050 is not set # CONFIG_MPU_SENSORS_MPU6050 is not set +CONFIG_UID_CPUTIME=y # CONFIG_C2PORT is not set # @@ -1303,28 +1299,6 @@ CONFIG_UID_STAT=y # CONFIG_SENSORS_LIS3_I2C is not set # CONFIG_SAMSUNG_C2C is not set # CONFIG_SEC_MODEM is not set -# CONFIG_UMTS_MODEM_XMM6260 is not set -# CONFIG_UMTS_MODEM_XMM6262 is not set -# CONFIG_CDMA_MODEM_CBP71 is not set -# CONFIG_CDMA_MODEM_CBP72 is not set -# CONFIG_LTE_MODEM_CMC221 is not set -# CONFIG_CDMA_MODEM_MDM6600 is not set -# CONFIG_TDSCDMA_MODEM_SPRD8803 is not set -# CONFIG_GSM_MODEM_ESC6270 is not set -# CONFIG_LINK_DEVICE_MIPI is not set -# CONFIG_LINK_DEVICE_DPRAM is not set -# CONFIG_LINK_DEVICE_PLD is not set -# CONFIG_LINK_DEVICE_USB is not set -# CONFIG_LINK_DEVICE_HSIC is not set -# CONFIG_LINK_DEVICE_C2C is not set -# CONFIG_LINK_DEVICE_SPI is not set -# CONFIG_WORKQUEUE_FRONT is not set -# CONFIG_IPC_CMC22x_OLD_RFS is not set -# CONFIG_SIPC_VER_5 is not set -# CONFIG_SIM_SLOT_SWITCH is not set -# CONFIG_LTE_MODEM_CMC220 is not set -# CONFIG_INTERNAL_MODEM_IF is not set -# CONFIG_CDMA_MODEM_QSC6085 is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -1405,9 +1379,16 @@ CONFIG_WLAN=y # CONFIG_USB_NET_RNDIS_WLAN is not set CONFIG_WIFI_CONTROL_FUNC=y # CONFIG_ATH_COMMON is not set +# CONFIG_B43LEGACY_DMA_AND_PIO_MODE is not set +# CONFIG_B43LEGACY_DMA_MODE is not set +# CONFIG_B43LEGACY_PIO_MODE is not set # CONFIG_BCM4330 is not set CONFIG_BCM4334=m +# CONFIG_BCM4335 is not set +# CONFIG_BCM4339 is not set +# CONFIG_BCM4354 is not set # CONFIG_BCM43241 is not set +CONFIG_BROADCOM_WIFI=y CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y @@ -1416,7 +1397,6 @@ CONFIG_WLAN_REGION_CODE=100 # CONFIG_IWM is not set # CONFIG_LIBERTAS is not set # CONFIG_MWIFIEX is not set -# CONFIG_LGUIWLAN is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -1430,9 +1410,35 @@ CONFIG_WLAN_REGION_CODE=100 # CONFIG_USB_PEGASUS is not set # CONFIG_USB_RTL8150 is not set CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +CONFIG_USB_NET_CDC_NCM=y +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set # CONFIG_USB_HSO is not set +# CONFIG_USB_NET_INT51X1 is not set # CONFIG_USB_CDC_PHONET is not set # CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_VL600 is not set +CONFIG_MSM_RMNET_USB=y # CONFIG_WAN is not set # @@ -1478,8 +1484,6 @@ CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_SECBRIDGE is not set CONFIG_INPUT_KEYRESET=y # CONFIG_INPUT_FBSUSPEND is not set -# CONFIG_INPUT_MPU6050 is not set -# CONFIG_INPUT_MPU6050_POLLING is not set # # Input Device Drivers @@ -1503,7 +1507,9 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set +CONFIG_SENSORS_HALL=y # CONFIG_KEYBOARD_CYPRESS_TOUCH is not set +# CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set @@ -1561,7 +1567,7 @@ CONFIG_TOUCHSCREEN_SYNAPTICS_S7301=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYS=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_WORKAROUND=y CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYLED=y -# CONFIG_TOUCHSCREEN_CYPRESS_TMA46X is not set +# CONFIG_TOUCHSCREEN_CYTTSP4 is not set CONFIG_SEC_TOUCHSCREEN_DVFS_LOCK=y CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH=y # CONFIG_KEYPAD_MELFAS_TOUCH is not set @@ -1569,27 +1575,6 @@ CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH=y CONFIG_INPUT_WACOM=y # CONFIG_EPEN_WACOM_G5SP is not set # CONFIG_EPEN_WACOM_G9PM is not set -CONFIG_EPEN_WACOM_G9PL=y -# CONFIG_RMI4_DEBUG is not set -# CONFIG_RMI4_BUS is not set -# CONFIG_RMI4_GENERIC is not set -# CONFIG_RMI4_F09 is not set -# CONFIG_RMI4_F1A is not set -# CONFIG_RMI4_F11 is not set -# CONFIG_RMI4_VIRTUAL_BUTTONS is not set -# CONFIG_RMI4_F17 is not set -# CONFIG_RMI4_F19 is not set -# CONFIG_RMI4_F21 is not set -# CONFIG_RMI4_F30 is not set -# CONFIG_RMI4_F31 is not set -# CONFIG_RMI4_F34 is not set -# CONFIG_RMI4_F41 is not set -# CONFIG_RMI4_F54 is not set -# CONFIG_RMI4_SMB is not set -# CONFIG_RMI4_I2C is not set -# CONFIG_RMI4_SPI is not set -# CONFIG_RMI4_DEV is not set -# CONFIG_RMI4_FWLIB is not set CONFIG_INPUT_MISC=y # CONFIG_SENSORS_BH1721FVC is not set # CONFIG_INPUT_AD714X is not set @@ -1672,7 +1657,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y # # Diag Support # -# CONFIG_DIAG_CHAR is not set +CONFIG_DIAG_CHAR=y # # DIAG traffic over USB @@ -1825,10 +1810,15 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_MAX17042_FUELGAUGE is not set # CONFIG_BATTERY_MAX17047_FUELGAUGE is not set CONFIG_BATTERY_MAX17047_C_FUELGAUGE=y -CONFIG_BATTERY_MAX77693_CHARGER=y # CONFIG_BATTERY_SMB136_CHARGER is not set +CONFIG_BATTERY_MAX77693_CHARGER=y +# CONFIG_BATTERY_WPC_CHARGER is not set # CONFIG_BATTERY_SAMSUNG_P1X is not set +# CONFIG_FUELGAUGE_DUMMY is not set +# CONFIG_FUELGAUGE_MAX17048 is not set +# CONFIG_CHARGER_DUMMY is not set # CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_SMB328 is not set # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set # CONFIG_TEST_POWER is not set @@ -1851,16 +1841,13 @@ CONFIG_BATTERY_SAMSUNG=y # CONFIG_SMB328_CHARGER is not set # CONFIG_SMB347_CHARGER is not set # CONFIG_CHARGER_MANAGER is not set -CONFIG_SAMSUNG_LPM_MODE=y # CONFIG_HWMON is not set CONFIG_THERMAL=y # CONFIG_CPU_THERMAL is not set # CONFIG_SENSORS_EXYNOS4_TMU is not set CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set -# CONFIG_CHARGER_NCP1851 is not set -# CONFIG_FUELGAUGE_MAX17050 is not set -# CONFIG_FUELGAUGE_MAX17050_COULOMB_COUNTING is not set + # # Watchdog Device Drivers # @@ -2056,12 +2043,6 @@ CONFIG_VIDEO_IR_I2C=y # CONFIG_VIDEO_TVP5150 is not set # CONFIG_VIDEO_TVP7002 is not set # CONFIG_VIDEO_VPX3220 is not set -# CONFIG_VIDEO_S5K3H2 is not set -# CONFIG_VIDEO_S5K3H7 is not set -# CONFIG_VIDEO_S5K4E5 is not set -# CONFIG_VIDEO_S5K6A3 is not set -# CONFIG_S5K6A3_CSI_C is not set -# CONFIG_S5K6A3_CSI_D is not set # CONFIG_VIDEO_M5MO is not set # CONFIG_VIDEO_M9MO is not set # CONFIG_VIDEO_S5K5BAFX is not set @@ -2069,12 +2050,12 @@ CONFIG_VIDEO_IR_I2C=y # CONFIG_VIDEO_SR200PC20 is not set # CONFIG_VIDEO_SR200PC20M is not set CONFIG_VIDEO_ISX012=y -CONFIG_VIDEO_SR130PC20=y # CONFIG_VIDEO_SLP_S5K4ECGX is not set # CONFIG_VIDEO_SLP_DB8131M is not set # CONFIG_VIDEO_S5K4EA is not set # CONFIG_VIDEO_S5C73M3 is not set # CONFIG_VIDEO_SLP_S5C73M3 is not set +CONFIG_VIDEO_SR130PC20=y CONFIG_VIDEO_IMPROVE_STREAMOFF=y # @@ -2141,7 +2122,6 @@ CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y # CONFIG_USB_ZR364XX is not set # CONFIG_USB_STKWEBCAM is not set # CONFIG_USB_S2255 is not set -CONFIG_MALI_400MP_UMP=y CONFIG_VIDEO_SAMSUNG=y CONFIG_VIDEO_SAMSUNG_V4L2=y CONFIG_VIDEO_FIMC=y @@ -2170,6 +2150,8 @@ CONFIG_LSI_HDMI_AUDIO_CH_EVENT=y CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 # CONFIG_VIDEO_MFC5X_DEBUG is not set +# CONFIG_VIDEO_MALI400MP is not set +# CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y # CONFIG_VIDEO_FIMG2D_DEBUG is not set CONFIG_VIDEO_FIMG2D4X=y @@ -2191,20 +2173,10 @@ CONFIG_VIDEO_SAMSUNG_MEMSIZE_JPEG=0 CONFIG_VIDEO_SAMSUNG_MEMSIZE_TVOUT=0 CONFIG_VIDEO_EXYNOS=y CONFIG_VIDEO_EXYNOS_MEMSIZE_FIMC_IS=12288 -CONFIG_EXYNOS_MEDIA_DEVICE=y -# CONFIG_VIDEO_EXYNOS_FIMC_LITE is not set - -# -# Reserved memory configurations -# -CONFIG_VIDEO_SAMSUNG_MEMSIZE_FLITE0=10240 -CONFIG_VIDEO_SAMSUNG_MEMSIZE_FLITE1=10240 -# CONFIG_VIDEO_EXYNOS_MIPI_CSIS is not set +# CONFIG_VIDEO_EXYNOS_FIMC_LITE is not set # CONFIG_VIDEO_EXYNOS_TV is not set # CONFIG_VIDEO_EXYNOS_ROTATOR is not set -# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set -# CONFIG_VIDEO_EXYNOS_FIMC_IS_BAYER is not set -CONFIG_MEDIA_EXYNOS=y +# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set CONFIG_V4L_MEM2MEM_DRIVERS=y # CONFIG_VIDEO_MEM2MEM_TESTDEV is not set @@ -2221,29 +2193,14 @@ CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE=y # CONFIG_ISDBT is not set # -# MUIC device -# -# CONFIG_STMPE811_ADC is not set -CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK=y -CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK=y - -# # Graphics support # -# CONFIG_MALI_VER_BEFORE_R3P2 is not set # CONFIG_DRM is not set CONFIG_ION=y CONFIG_ION_EXYNOS=y CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=81920 # CONFIG_ION_EXYNOS_CONTIGHEAP_DEBUG is not set -# CONFIG_VITHAR is not set -CONFIG_MALI400=y -CONFIG_MALI_VER_R3P2=y -# CONFIG_MALI400_DEBUG is not set -# CONFIG_MALI400_PROFILING is not set -CONFIG_MALI_DVFS=y -CONFIG_MALI400_UMP=y -# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set @@ -2274,6 +2231,7 @@ CONFIG_FB_S5P_SPLASH_SCREEN=y # CONFIG_FB_S5P_LCD_INIT is not set # CONFIG_FB_S5P_DEBUG is not set CONFIG_FB_S5P_VSYNC_THREAD=y +# CONFIG_FB_S5P_VSYNC_SEND_UEVENTS is not set CONFIG_FB_S5P_VSYNC_SYSFS=y # CONFIG_FB_S5P_TRACE_UNDERRUN is not set CONFIG_FB_S5P_DEFAULT_WINDOW=3 @@ -2282,12 +2240,18 @@ CONFIG_FB_S5P_NR_BUFFERS=2 CONFIG_VIDEO_SAMSUNG_MEMSIZE_FIMD=8192 CONFIG_FB_S5P_MDNIE=y CONFIG_FB_MDNIE_PWM=y -CONFIG_FB_EBOOK_PANEL_SCENARIO=y CONFIG_FB_S5P_MIPI_DSIM=y CONFIG_FB_BGRA_ORDER=y # CONFIG_FB_RGBA_ORDER is not set # CONFIG_FB_S5P_S6C1372 is not set # CONFIG_FB_S5P_LD9040 is not set +# CONFIG_FB_S5P_LMS501XX is not set +# CONFIG_FB_S5P_DUMMY_MIPI_LCD is not set +# CONFIG_FB_S5P_S6E8AA0 is not set +# CONFIG_FB_S5P_EA8061 is not set +# CONFIG_FB_S5P_S6EVR02 is not set +# CONFIG_FB_S5P_S6D6AA1 is not set +# CONFIG_FB_S5P_S6E63M0 is not set CONFIG_FB_S5P_NT71391=y # CONFIG_LCD_FREQ_SWITCH is not set CONFIG_FB_S5P_EXTDSP=y @@ -2296,10 +2260,10 @@ CONFIG_FB_S5P_EXTDSP_NR_BUFFERS=3 # CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD is not set # CONFIG_FB_S1D13XXX is not set # CONFIG_FB_TMIO is not set -# CONFIG_S5P_MIPI_DSI2 is not set # CONFIG_FB_UDL is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX_PCI_GDC is not set # CONFIG_FB_BROADSHEET is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y @@ -2369,6 +2333,7 @@ CONFIG_SND_SOC=y # CONFIG_SND_SOC_CACHE_LZO is not set CONFIG_SND_SOC_SAMSUNG=y CONFIG_SND_SAMSUNG_I2S=y +# CONFIG_SND_SOC_SAMSUNG_MIDAS_WM1811 is not set CONFIG_SND_SOC_SAMSUNG_KONA_WM1811=y # CONFIG_SND_SOC_SAMSUNG_USE_DMA_WRAPPER is not set CONFIG_SND_SOC_SAMSUNG_I2S_SEC=y @@ -2406,7 +2371,6 @@ CONFIG_USB_HID=y # # Special HID drivers # -CONFIG_UHID=y CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y # CONFIG_HID_ACRUX_FF is not set @@ -2552,7 +2516,6 @@ CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_KARMA is not set # CONFIG_USB_STORAGE_CYPRESS_ATACB is not set # CONFIG_USB_STORAGE_ENE_UB6250 is not set -# CONFIG_USB_UAS is not set # CONFIG_USB_LIBUSUAL is not set # @@ -2609,6 +2572,7 @@ CONFIG_USB_SERIAL_QUALCOMM=y # CONFIG_USB_SERIAL_TI is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_XIRCOM is not set +CONFIG_USB_SERIAL_WWAN=y # CONFIG_USB_SERIAL_OPTION is not set # CONFIG_USB_SERIAL_OMNINET is not set # CONFIG_USB_SERIAL_OPTICON is not set @@ -2642,6 +2606,7 @@ CONFIG_USB_SERIAL_CSVT=y # CONFIG_USB_ISIGHTFW is not set # CONFIG_USB_YUREX is not set CONFIG_USB_QCOM_DIAG_BRIDGE=y +# CONFIG_USB_QCOM_DIAG_BRIDGE_TEST is not set CONFIG_USB_QCOM_MDM_BRIDGE=y CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG is not set @@ -2654,6 +2619,12 @@ CONFIG_USB_GADGET_SELECTED=y CONFIG_USB_GADGET_S3C_OTGD=y # CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_CI13XXX_PCI is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_EG20T is not set # CONFIG_USB_GADGET_DUMMY_HCD is not set # @@ -2750,12 +2721,14 @@ CONFIG_LEDS_CLASS=y # CONFIG_LEDS_BD2802 is not set # CONFIG_LEDS_LT3593 is not set # CONFIG_LEDS_SWITCH is not set +# CONFIG_LEDS_MAX77693 is not set # CONFIG_LEDS_AAT1290A is not set # CONFIG_LEDS_TRIGGERS is not set # # LED Triggers # +# CONFIG_LEDS_TRIGGER_NOTIFICATION is not set # CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y @@ -2868,14 +2841,14 @@ CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d CONFIG_ANDROID_TIMED_OUTPUT=y # CONFIG_ANDROID_TIMED_GPIO is not set CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_LMK_ADJ_RBTREE=y # CONFIG_POHMELFS is not set # CONFIG_LINE6_USB is not set # CONFIG_USB_SERIAL_QUATECH2 is not set # CONFIG_USB_SERIAL_QUATECH_USB2 is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -# CONFIG_XVMALLOC is not set -# CONFIG_ZRAM is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -2901,7 +2874,6 @@ CONFIG_CLKDEV_LOOKUP=y CONFIG_VIBETONZ=y CONFIG_MOTOR_DRV_MAX77693=y # CONFIG_MOTOR_DRV_ISA1200 is not set -# CONFIG_MOTOR_DRV_DRV2603 is not set # CONFIG_FM_RADIO is not set CONFIG_SENSORS_CORE=y # CONFIG_SENSORS_AK8975C is not set @@ -2909,10 +2881,7 @@ CONFIG_SENSORS_CORE=y # CONFIG_SENSORS_BMP180 is not set # CONFIG_SENSORS_CM3663 is not set # CONFIG_SENSORS_PAS2M110 is not set -CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=2 # CONFIG_SENSORS_BMA254 is not set -CONFIG_SENSORS_YAS532=y -CONFIG_SENSORS_YAS_ORI=y # CONFIG_SENSORS_TAOS is not set CONFIG_SENSORS_GP2A=y # CONFIG_SENSORS_GP2A_ANALOG is not set @@ -2925,6 +2894,9 @@ CONFIG_SENSOR_K3DH_INPUTDEV=y # CONFIG_SENSORS_K3G is not set # CONFIG_SENSORS_LSM330DLC is not set # CONFIG_SENSORS_LPS331 is not set +CONFIG_SENSORS_YAS532=y +CONFIG_SENSORS_YAS_ORI=y +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=2 # CONFIG_SENSORS_SYSFS is not set # CONFIG_SENSORS_SSP is not set # CONFIG_SENSORS_SSP_LSM330 is not set @@ -2946,13 +2918,13 @@ CONFIG_IR_REMOCON=y CONFIG_IR_REMOCON_MC96=y # CONFIG_EXTCON is not set # CONFIG_BARCODE_EMUL is not set +# CONFIG_SAMSUNG_PHONE_TTY is not set CONFIG_MOBICORE_SUPPORT=y # CONFIG_MOBICORE_DEBUG is not set CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set # CONFIG_AUTHENTEC_VPNCLIENT_INTERCEPTOR is not set -# CONFIG_J4FS is not set # # File systems @@ -2969,6 +2941,7 @@ CONFIG_EXT4_FS_SECURITY=y # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y # CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set # CONFIG_XFS_FS is not set @@ -3031,6 +3004,10 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS 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_LOGFS is not set # CONFIG_CRAMFS is not set # CONFIG_SQUASHFS is not set @@ -3040,9 +3017,13 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set +# CONFIG_ROMFS_BACKED_BY_BLOCK is not set +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y @@ -3140,7 +3121,6 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y -CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y @@ -3286,6 +3266,9 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_SECURITY_APPARMOR is not set # CONFIG_IMA is not set CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set # CONFIG_DEFAULT_SECURITY_DAC is not set CONFIG_DEFAULT_SECURITY="selinux" CONFIG_CRYPTO=y @@ -3386,6 +3369,8 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set # CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation @@ -3415,6 +3400,9 @@ CONFIG_ZLIB_DEFLATE=y # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y CONFIG_TEXTSEARCH=y CONFIG_TEXTSEARCH_KMP=y CONFIG_TEXTSEARCH_BM=y -- cgit v1.1 From 3c3a954b7befa1eb41b02b11c66fb9bd7ec18c85 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Mon, 3 Oct 2016 19:48:03 +1100 Subject: cm: disable CONFIG_USB_DEVICEFS unneeded, only causes crashes Change-Id: I58a5121ed80c3460f20a4afce32d6925588b877e --- arch/arm/configs/cyanogenmod_d710_defconfig | 2 +- arch/arm/configs/cyanogenmod_i777_defconfig | 2 +- arch/arm/configs/cyanogenmod_i9100_defconfig | 2 +- arch/arm/configs/cyanogenmod_i925_defconfig | 2 +- arch/arm/configs/cyanogenmod_i9300_defconfig | 2 +- arch/arm/configs/cyanogenmod_i9305_defconfig | 2 +- arch/arm/configs/cyanogenmod_n5100_defconfig | 2 +- arch/arm/configs/cyanogenmod_n5110_defconfig | 2 +- arch/arm/configs/cyanogenmod_n5120_defconfig | 2 +- arch/arm/configs/cyanogenmod_n7000_defconfig | 2 +- arch/arm/configs/cyanogenmod_n7100_defconfig | 2 +- arch/arm/configs/cyanogenmod_n8000_defconfig | 2 +- arch/arm/configs/cyanogenmod_n8013_defconfig | 2 +- arch/arm/configs/cyanogenmod_t0lte_defconfig | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_d710_defconfig b/arch/arm/configs/cyanogenmod_d710_defconfig index bca492d..ecc7052 100644 --- a/arch/arm/configs/cyanogenmod_d710_defconfig +++ b/arch/arm/configs/cyanogenmod_d710_defconfig @@ -2326,7 +2326,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_i777_defconfig b/arch/arm/configs/cyanogenmod_i777_defconfig index 2ac674b..447226f 100644 --- a/arch/arm/configs/cyanogenmod_i777_defconfig +++ b/arch/arm/configs/cyanogenmod_i777_defconfig @@ -2355,7 +2355,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_i9100_defconfig b/arch/arm/configs/cyanogenmod_i9100_defconfig index a568077..16ec98c 100644 --- a/arch/arm/configs/cyanogenmod_i9100_defconfig +++ b/arch/arm/configs/cyanogenmod_i9100_defconfig @@ -2395,7 +2395,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_i925_defconfig b/arch/arm/configs/cyanogenmod_i925_defconfig index 01d452c..08b4ef3 100644 --- a/arch/arm/configs/cyanogenmod_i925_defconfig +++ b/arch/arm/configs/cyanogenmod_i925_defconfig @@ -2427,7 +2427,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_i9300_defconfig b/arch/arm/configs/cyanogenmod_i9300_defconfig index ced7ba8..5cd9ed6 100644 --- a/arch/arm/configs/cyanogenmod_i9300_defconfig +++ b/arch/arm/configs/cyanogenmod_i9300_defconfig @@ -2488,7 +2488,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_i9305_defconfig b/arch/arm/configs/cyanogenmod_i9305_defconfig index a5737e3..1646b9e 100755 --- a/arch/arm/configs/cyanogenmod_i9305_defconfig +++ b/arch/arm/configs/cyanogenmod_i9305_defconfig @@ -2417,7 +2417,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n5100_defconfig b/arch/arm/configs/cyanogenmod_n5100_defconfig index 16096f5..a2416bd 100644 --- a/arch/arm/configs/cyanogenmod_n5100_defconfig +++ b/arch/arm/configs/cyanogenmod_n5100_defconfig @@ -2467,7 +2467,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n5110_defconfig b/arch/arm/configs/cyanogenmod_n5110_defconfig index c5e7be1..8bd50a1 100644 --- a/arch/arm/configs/cyanogenmod_n5110_defconfig +++ b/arch/arm/configs/cyanogenmod_n5110_defconfig @@ -2468,7 +2468,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index d039b39..a2e02ce 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -2447,7 +2447,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n7000_defconfig b/arch/arm/configs/cyanogenmod_n7000_defconfig index ca51569..ca6968c 100644 --- a/arch/arm/configs/cyanogenmod_n7000_defconfig +++ b/arch/arm/configs/cyanogenmod_n7000_defconfig @@ -2374,7 +2374,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n7100_defconfig b/arch/arm/configs/cyanogenmod_n7100_defconfig index cda07f5..124066d 100644 --- a/arch/arm/configs/cyanogenmod_n7100_defconfig +++ b/arch/arm/configs/cyanogenmod_n7100_defconfig @@ -2448,7 +2448,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n8000_defconfig b/arch/arm/configs/cyanogenmod_n8000_defconfig index e4e8e12..45cf051 100644 --- a/arch/arm/configs/cyanogenmod_n8000_defconfig +++ b/arch/arm/configs/cyanogenmod_n8000_defconfig @@ -2423,7 +2423,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_n8013_defconfig b/arch/arm/configs/cyanogenmod_n8013_defconfig index 9f749f2..f55906c 100644 --- a/arch/arm/configs/cyanogenmod_n8013_defconfig +++ b/arch/arm/configs/cyanogenmod_n8013_defconfig @@ -2383,7 +2383,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y diff --git a/arch/arm/configs/cyanogenmod_t0lte_defconfig b/arch/arm/configs/cyanogenmod_t0lte_defconfig index d58be9e..7a07860 100755 --- a/arch/arm/configs/cyanogenmod_t0lte_defconfig +++ b/arch/arm/configs/cyanogenmod_t0lte_defconfig @@ -2436,7 +2436,7 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y # # Miscellaneous USB options # -CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICEFS is not set CONFIG_USB_DEVICE_CLASS=y # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y -- cgit v1.1 From 3a24c2b12f2e433c2d69b9b0ffb09db6a542617c Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Wed, 17 Aug 2016 16:00:08 -0700 Subject: binder: prevent kptr leak by using %pK format specifier Works in conjunction with kptr_restrict. Bug: 30143283 Change-Id: I2b3ce22f4e206e74614d51453a1d59b7080ab05a --- drivers/staging/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index e2b69f2..72db295 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -3346,7 +3346,7 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node) static void print_binder_ref(struct seq_file *m, struct binder_ref *ref) { - seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %p\n", + seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n", ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ", ref->node->debug_id, ref->strong, ref->weak, ref->death); } -- cgit v1.1 From aadaf237377c1dbce968982bc55c148fc97aef90 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Mar 2016 09:56:35 -0300 Subject: net: Fix use after free in the recvmmsg exit path The syzkaller fuzzer hit the following use-after-free: Call Trace: [] __asan_report_load8_noabort+0x3e/0x40 mm/kasan/report.c:295 [] __sys_recvmmsg+0x6fa/0x7f0 net/socket.c:2261 [< inline >] SYSC_recvmmsg net/socket.c:2281 [] SyS_recvmmsg+0x16f/0x180 net/socket.c:2270 [] entry_SYSCALL_64_fastpath+0x16/0x7a arch/x86/entry/entry_64.S:185 And, as Dmitry rightly assessed, that is because we can drop the reference and then touch it when the underlying recvmsg calls return some packets and then hit an error, which will make recvmmsg to set sock->sk->sk_err, oops, fix it. Reported-and-Tested-by: Dmitry Vyukov Cc: Alexander Potapenko Cc: Eric Dumazet Cc: Kostya Serebryany Cc: Sasha Levin Fixes: a2e2725541fa ("net: Introduce recvmmsg socket syscall") http://lkml.kernel.org/r/20160122211644.GC2470@redhat.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller Change-Id: Ie3b6ee89ad3e8cd3a0fe8f50f74aaa4834d0b4ca --- net/socket.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/net/socket.c b/net/socket.c index 53882f7..3b5e6bb 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2298,31 +2298,31 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, break; } -out_put: - fput_light(sock->file, fput_needed); - if (err == 0) - return datagrams; + goto out_put; - if (datagrams != 0) { + if (datagrams == 0) { + datagrams = err; + goto out_put; + } + + /* + * We may return less entries than requested (vlen) if the + * sock is non block and there aren't enough datagrams... + */ + if (err != -EAGAIN) { /* - * We may return less entries than requested (vlen) if the - * sock is non block and there aren't enough datagrams... + * ... or if recvmsg returns an error after we + * received some datagrams, where we record the + * error to return on the next call or if the + * app asks about it using getsockopt(SO_ERROR). */ - if (err != -EAGAIN) { - /* - * ... or if recvmsg returns an error after we - * received some datagrams, where we record the - * error to return on the next call or if the - * app asks about it using getsockopt(SO_ERROR). - */ - sock->sk->sk_err = -err; - } - - return datagrams; + sock->sk->sk_err = -err; } +out_put: + fput_light(sock->file, fput_needed); - return err; + return datagrams; } SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, -- cgit v1.1 From 8849894bfafa295d6a1acab288b6deec4dc26ca8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Mar 2016 09:56:35 -0300 Subject: net: Fix use after free in the recvmmsg exit path The syzkaller fuzzer hit the following use-after-free: Call Trace: [] __asan_report_load8_noabort+0x3e/0x40 mm/kasan/report.c:295 [] __sys_recvmmsg+0x6fa/0x7f0 net/socket.c:2261 [< inline >] SYSC_recvmmsg net/socket.c:2281 [] SyS_recvmmsg+0x16f/0x180 net/socket.c:2270 [] entry_SYSCALL_64_fastpath+0x16/0x7a arch/x86/entry/entry_64.S:185 And, as Dmitry rightly assessed, that is because we can drop the reference and then touch it when the underlying recvmsg calls return some packets and then hit an error, which will make recvmmsg to set sock->sk->sk_err, oops, fix it. Reported-and-Tested-by: Dmitry Vyukov Cc: Alexander Potapenko Cc: Eric Dumazet Cc: Kostya Serebryany Cc: Sasha Levin Fixes: a2e2725541fa ("net: Introduce recvmmsg socket syscall") http://lkml.kernel.org/r/20160122211644.GC2470@redhat.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller Change-Id: Ie3b6ee89ad3e8cd3a0fe8f50f74aaa4834d0b4ca --- kernel/events/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index acdc087..b30d204 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6477,6 +6477,9 @@ SYSCALL_DEFINE5(perf_event_open, if (err) return err; + if (attr.constraint_duplicate || attr.__reserved_1) + return -EINVAL; + if (!attr.exclude_kernel) { if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) return -EACCES; -- cgit v1.1 From 41187bafd882b48feb3b9757809d2b73bac7852e Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Mon, 18 Jul 2016 12:45:17 -0700 Subject: fs: ext4: disable support for fallocate FALLOC_FL_PUNCH_HOLE Bug: 28760453 Change-Id: I019c2de559db9e4b95860ab852211b456d78c4ca Signed-off-by: Nick Desaulniers --- fs/ext4/inode.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 294bc8f..c2bd302 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4484,6 +4484,7 @@ int ext4_can_truncate(struct inode *inode) int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) { +#if 0 struct inode *inode = file->f_path.dentry->d_inode; if (!S_ISREG(inode->i_mode)) return -ENOTSUPP; @@ -4494,6 +4495,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) } return ext4_ext_punch_hole(file, offset, length); +#else + /* + * Disabled as per b/28760453 + */ + return -EOPNOTSUPP; +#endif } /* -- cgit v1.1 From 782f37cdcc5a65f9efdfa3a583ad11bb6092eda2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 18 Jun 2014 13:32:32 +0200 Subject: UPSTREAM: ALSA: control: Fix replacing user controls (cherry pick from commit 82262a46627bebb0febcc26664746c25cef08563) There are two issues with the current implementation for replacing user controls. The first is that the code does not check if the control is actually a user control and neither does it check if the control is owned by the process that tries to remove it. That allows userspace applications to remove arbitrary controls, which can cause a user after free if a for example a driver does not expect a control to be removed from under its feed. The second issue is that on one hand when a control is replaced the user_ctl_count limit is not checked and on the other hand the user_ctl_count is increased (even though the number of user controls does not change). This allows userspace, once the user_ctl_count limit has been reached, to repeatedly replace a control until user_ctl_count overflows. Once that happens new controls can be added effectively bypassing the user_ctl_count limit. Both issues can be fixed by instead of open-coding the removal of the control that is to be replaced to use snd_ctl_remove_user_ctl(). This function does proper permission checks as well as decrements user_ctl_count after the control has been removed. Note that by using snd_ctl_remove_user_ctl() the check which returns -EBUSY at beginning of the function if the control already exists is removed. This is not a problem though since the check is quite useless, because the lock that is protecting the control list is released between the check and before adding the new control to the list, which means that it is possible that a different control with the same settings is added to the list after the check. Luckily there is another check that is done while holding the lock in snd_ctl_add(), so we'll rely on that to make sure that the same control is not added twice. Signed-off-by: Lars-Peter Clausen Acked-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai Change-Id: I0b183e2d52afe8e747f59e1ecca6f6fbbac2d016 Bug: 29916012 --- sound/core/control.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/sound/core/control.c b/sound/core/control.c index 6128f69..2b79043 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1077,9 +1077,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, long private_size; struct user_element *ue; int idx, err; - - if (card->user_ctl_count >= MAX_USER_CONTROLS) - return -ENOMEM; + if (info->count < 1) return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : @@ -1088,21 +1086,16 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); info->id.numid = 0; memset(&kctl, 0, sizeof(kctl)); - down_write(&card->controls_rwsem); - _kctl = snd_ctl_find_id(card, &info->id); - err = 0; - if (_kctl) { - if (replace) - err = snd_ctl_remove(card, _kctl); - else - err = -EBUSY; - } else { - if (replace) - err = -ENOENT; + + if (replace) { + err = snd_ctl_remove_user_ctl(file, &info->id); + if (err) + return err; } - up_write(&card->controls_rwsem); - if (err < 0) - return err; + + if (card->user_ctl_count >= MAX_USER_CONTROLS) + return -ENOMEM; + memcpy(&kctl.id, &info->id, sizeof(info->id)); kctl.count = info->owner ? info->owner : 1; access |= SNDRV_CTL_ELEM_ACCESS_USER; -- cgit v1.1 From 025b7b8515899ef02d4be760b08d7dd4c96d1b07 Mon Sep 17 00:00:00 2001 From: Mohamad Ayyash Date: Tue, 24 May 2016 10:28:30 -0700 Subject: Replace %p with %pK to prevent leaking kernel address BUG: 27532522 Change-Id: Ic0710a9a8cfc682acd88ecf3bbfeece2d798c4a4 Signed-off-by: Mohamad Ayyash --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index c8a53e5..5fa5e46 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1987,7 +1987,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, 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 " + "sock=%pK tag=0x%llx (uid=%u) pid=%u " "f_count=%lu\n", sock_tag_entry->sk, sock_tag_entry->tag, uid, -- cgit v1.1 From ce1f4b0226fb50b3574b5203db4053957fcad57e Mon Sep 17 00:00:00 2001 From: kangjie Date: Tue, 3 May 2016 21:49:23 -0400 Subject: fix infoleak in rtnetlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the stack object “map” has a total size of 32 bytes. Its last 4 bytes are padding generated by compiler. These padding bytes are not initialized and sent out via “nla_put” Bug: 28620102 Change-Id: I13da380c6fe8abca49e3cf9f05293c02b44d2e5e Signed-off-by: kangjie --- net/core/rtnetlink.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b6aaa7a..75727c6 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -915,14 +915,14 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias); if (1) { - struct rtnl_link_ifmap map = { - .mem_start = dev->mem_start, - .mem_end = dev->mem_end, - .base_addr = dev->base_addr, - .irq = dev->irq, - .dma = dev->dma, - .port = dev->if_port, - }; + struct rtnl_link_ifmap map; + memset(&map, 0, sizeof(map)); + map.mem_start = dev->mem_start; + map.mem_end = dev->mem_end; + map.base_addr = dev->base_addr; + map.irq = dev->irq; + map.dma = dev->dma; + map.port = dev->if_port; NLA_PUT(skb, IFLA_MAP, sizeof(map), &map); } -- cgit v1.1 From ca3aa2bccc89a21ccf2bbfdf59545cbf0cb9c977 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 17 Aug 2016 05:56:26 -0700 Subject: tcp: fix use after free in tcp_xmit_retransmit_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When tcp_sendmsg() allocates a fresh and empty skb, it puts it at the tail of the write queue using tcp_add_write_queue_tail() Then it attempts to copy user data into this fresh skb. If the copy fails, we undo the work and remove the fresh skb. Unfortunately, this undo lacks the change done to tp->highest_sack and we can leave a dangling pointer (to a freed skb) Later, tcp_xmit_retransmit_queue() can dereference this pointer and access freed memory. For regular kernels where memory is not unmapped, this might cause SACK bugs because tcp_highest_sack_seq() is buggy, returning garbage instead of tp->snd_nxt, but with various debug features like CONFIG_DEBUG_PAGEALLOC, this can crash the kernel. This bug was found by Marco Grassi thanks to syzkaller. Change-Id: I264f97d30d0a623011d9ee811c63fa0e0c2149a2 Fixes: 6859d49475d4 ("[TCP]: Abstract tp->highest_sack accessing & point to next skb") Reported-by: Marco Grassi Signed-off-by: Eric Dumazet Cc: Ilpo Järvinen Cc: Yuchung Cheng Cc: Neal Cardwell Acked-by: Neal Cardwell Reviewed-by: Cong Wang Signed-off-by: David S. Miller --- include/net/tcp.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/tcp.h b/include/net/tcp.h index 0f6a452..4fdc312 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1250,6 +1250,8 @@ static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unli { if (sk->sk_send_head == skb_unlinked) sk->sk_send_head = NULL; + if (tcp_sk(sk)->highest_sack == skb_unlinked) + tcp_sk(sk)->highest_sack = NULL; } static inline void tcp_init_send_head(struct sock *sk) -- cgit v1.1 From a20c4e114c47ab6c34025110cd44775df36079b0 Mon Sep 17 00:00:00 2001 From: RGIB Date: Thu, 20 Oct 2016 21:13:37 +0200 Subject: smdk4412 : fix build Change-Id: I0e5b9979850042d790cb89996163bdc69a4c7879 --- kernel/events/core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index b30d204..acdc087 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6477,9 +6477,6 @@ SYSCALL_DEFINE5(perf_event_open, if (err) return err; - if (attr.constraint_duplicate || attr.__reserved_1) - return -EINVAL; - if (!attr.exclude_kernel) { if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) return -EACCES; -- cgit v1.1 From 6c0837fc69be45a1a1bf91c433ac3a4955597c4e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Oct 2016 00:49:40 +0200 Subject: mm: remove gup_flags FOLL_WRITE games from __get_user_pages() This is an ancient bug that was actually attempted to be fixed once (badly) by me eleven years ago in commit 4ceb5db9757a ("Fix get_user_pages() race for write access") but that was then undone due to problems on s390 by commit f33ea7f404e5 ("fix get_user_pages bug"). In the meantime, the s390 situation has long been fixed, and we can now fix it by checking the pte_dirty() bit properly (and do it better). The s390 dirty bit was implemented in abf09bed3cce ("s390/mm: implement software dirty bits") which made it into v3.9. Earlier kernels will have to look at the page state itself. Also, the VM has become more scalable, and what used a purely theoretical race back then has become easier to trigger. To fix it, we introduce a new internal FOLL_COW flag to mark the "yes, we already did a COW" rather than play racy games with FOLL_WRITE that is very fundamental, and then use the pte dirty flag to validate that the FOLL_COW flag is still valid. Change-Id: Id9bec3722797dff7d0ff0d9f6097c4229e31fd62 Reported-and-tested-by: Phil "not Paul" Oester Acked-by: Hugh Dickins Reviewed-by: Michal Hocko Cc: Andy Lutomirski Cc: Kees Cook Cc: Oleg Nesterov Cc: Willy Tarreau Cc: Nick Piggin Cc: Greg Thelen Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds [wt: s/gup.c/memory.c; s/follow_page_pte/follow_page_mask; s/faultin_page/__get_user_page] Signed-off-by: Willy Tarreau --- include/linux/mm.h | 1 + mm/memory.c | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 92a055b..b465c7f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1574,6 +1574,7 @@ struct page *follow_page(struct vm_area_struct *, unsigned long address, #define FOLL_SPLIT 0x80 /* don't return transhuge pages, split them */ #define FOLL_HWPOISON 0x100 /* check page is hwpoisoned */ #define FOLL_NO_CMA 0x200 /* avoid putting pages to CMA regions */ +#define FOLL_COW 0x4000 /* internal GUP flag */ typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr, void *data); diff --git a/mm/memory.c b/mm/memory.c index 067a4f3..29c8112 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1436,6 +1436,16 @@ int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address, } EXPORT_SYMBOL_GPL(zap_vma_ptes); +/* + * FOLL_FORCE can write to even unwritable pte's, but only + * after we've gone through a COW cycle and they are dirty. + */ +static inline bool can_follow_write_pte(pte_t pte, unsigned int flags) +{ + return pte_write(pte) || + ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte)); +} + /** * follow_page - look up a page descriptor from a user-virtual address * @vma: vm_area_struct mapping @address @@ -1518,7 +1528,7 @@ split_fallthrough: pte = *ptep; if (!pte_present(pte)) goto no_page; - if ((flags & FOLL_WRITE) && !pte_write(pte)) + if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) goto unlock; page = vm_normal_page(vma, address, pte); @@ -1832,7 +1842,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, */ if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE)) - foll_flags &= ~FOLL_WRITE; + foll_flags |= FOLL_COW; cond_resched(); } -- cgit v1.1 From 9c988962c4d7ff230fbf83c601d8aaa5eaf0c990 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 6 Sep 2016 14:03:45 +0100 Subject: KEYS: Fix short sprintf buffer in /proc/keys show function Fix a short sprintf buffer in proc_keys_show(). If the gcc stack protector is turned on, this can cause a panic due to stack corruption. The problem is that xbuf[] is not big enough to hold a 64-bit timeout rendered as weeks: (gdb) p 0xffffffffffffffffULL/(60*60*24*7) $2 = 30500568904943 That's 14 chars plus NUL, not 11 chars plus NUL. Expand the buffer to 16 chars. I think the unpatched code apparently works if the stack-protector is not enabled because on a 32-bit machine the buffer won't be overflowed and on a 64-bit machine there's a 64-bit aligned pointer at one side and an int that isn't checked again on the other side. The panic incurred looks something like: Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffffff81352ebe CPU: 0 PID: 1692 Comm: reproducer Not tainted 4.7.2-201.fc24.x86_64 #1 Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 0000000000000086 00000000fbbd2679 ffff8800a044bc00 ffffffff813d941f ffffffff81a28d58 ffff8800a044bc98 ffff8800a044bc88 ffffffff811b2cb6 ffff880000000010 ffff8800a044bc98 ffff8800a044bc30 00000000fbbd2679 Call Trace: [] dump_stack+0x63/0x84 [] panic+0xde/0x22a [] ? proc_keys_show+0x3ce/0x3d0 [] __stack_chk_fail+0x19/0x30 [] proc_keys_show+0x3ce/0x3d0 [] ? key_validate+0x50/0x50 [] ? key_default_cmp+0x20/0x20 [] seq_read+0x2cc/0x390 [] proc_reg_read+0x42/0x70 [] __vfs_read+0x37/0x150 [] ? security_file_permission+0xa0/0xc0 [] vfs_read+0x96/0x130 [] SyS_read+0x55/0xc0 [] entry_SYSCALL_64_fastpath+0x1a/0xa4 Change-Id: I0787d5a38c730ecb75d3c08f28f0ab36295d59e7 Reported-by: Ondrej Kozina Signed-off-by: David Howells Tested-by: Ondrej Kozina --- security/keys/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/proc.c b/security/keys/proc.c index 49bbc97..3f7b410 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -188,7 +188,7 @@ static int proc_keys_show(struct seq_file *m, void *v) struct timespec now; unsigned long timo; key_ref_t key_ref, skey_ref; - char xbuf[12]; + char xbuf[16]; int rc; key_ref = make_key_ref(key, 0); -- cgit v1.1 From eebccc5eaf01170383699919fe8ebb952ce8329f Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Fri, 29 Jul 2016 10:40:31 +0200 Subject: block: fix use-after-free in seq file I got a KASAN report of use-after-free: ================================================================== BUG: KASAN: use-after-free in klist_iter_exit+0x61/0x70 at addr ffff8800b6581508 Read of size 8 by task trinity-c1/315 ============================================================================= BUG kmalloc-32 (Not tainted): kasan: bad access detected ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Allocated in disk_seqf_start+0x66/0x110 age=144 cpu=1 pid=315 ___slab_alloc+0x4f1/0x520 __slab_alloc.isra.58+0x56/0x80 kmem_cache_alloc_trace+0x260/0x2a0 disk_seqf_start+0x66/0x110 traverse+0x176/0x860 seq_read+0x7e3/0x11a0 proc_reg_read+0xbc/0x180 do_loop_readv_writev+0x134/0x210 do_readv_writev+0x565/0x660 vfs_readv+0x67/0xa0 do_preadv+0x126/0x170 SyS_preadv+0xc/0x10 do_syscall_64+0x1a1/0x460 return_from_SYSCALL_64+0x0/0x6a INFO: Freed in disk_seqf_stop+0x42/0x50 age=160 cpu=1 pid=315 __slab_free+0x17a/0x2c0 kfree+0x20a/0x220 disk_seqf_stop+0x42/0x50 traverse+0x3b5/0x860 seq_read+0x7e3/0x11a0 proc_reg_read+0xbc/0x180 do_loop_readv_writev+0x134/0x210 do_readv_writev+0x565/0x660 vfs_readv+0x67/0xa0 do_preadv+0x126/0x170 SyS_preadv+0xc/0x10 do_syscall_64+0x1a1/0x460 return_from_SYSCALL_64+0x0/0x6a CPU: 1 PID: 315 Comm: trinity-c1 Tainted: G B 4.7.0+ #62 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014 ffffea0002d96000 ffff880119b9f918 ffffffff81d6ce81 ffff88011a804480 ffff8800b6581500 ffff880119b9f948 ffffffff8146c7bd ffff88011a804480 ffffea0002d96000 ffff8800b6581500 fffffffffffffff4 ffff880119b9f970 Call Trace: [] dump_stack+0x65/0x84 [] print_trailer+0x10d/0x1a0 [] object_err+0x2f/0x40 [] kasan_report_error+0x221/0x520 [] __asan_report_load8_noabort+0x3e/0x40 [] klist_iter_exit+0x61/0x70 [] class_dev_iter_exit+0x9/0x10 [] disk_seqf_stop+0x3a/0x50 [] seq_read+0x4b2/0x11a0 [] proc_reg_read+0xbc/0x180 [] do_loop_readv_writev+0x134/0x210 [] do_readv_writev+0x565/0x660 [] vfs_readv+0x67/0xa0 [] do_preadv+0x126/0x170 [] SyS_preadv+0xc/0x10 This problem can occur in the following situation: open() - pread() - .seq_start() - iter = kmalloc() // succeeds - seqf->private = iter - .seq_stop() - kfree(seqf->private) - pread() - .seq_start() - iter = kmalloc() // fails - .seq_stop() - class_dev_iter_exit(seqf->private) // boom! old pointer As the comment in disk_seqf_stop() says, stop is called even if start failed, so we need to reinitialise the private pointer to NULL when seq iteration stops. An alternative would be to set the private pointer to NULL when the kmalloc() in disk_seqf_start() fails. Change-Id: I41ee55505a213f99a92ce630885e6c31b4b60232 Cc: stable@vger.kernel.org Signed-off-by: Vegard Nossum Acked-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/genhd.c b/block/genhd.c index 6ece7ab..6c93aa1 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -831,6 +831,7 @@ static void disk_seqf_stop(struct seq_file *seqf, void *v) if (iter) { class_dev_iter_exit(iter); kfree(iter); + seqf->private = NULL; } } -- cgit v1.1 From f7df1e58091c030352a63ac5ecd1952ebf758c1d Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 1 Jul 2016 00:39:35 -0700 Subject: block: fix use-after-free in sys_ioprio_get() get_task_ioprio() accesses the task->io_context without holding the task lock and thus can race with exit_io_context(), leading to a use-after-free. The reproducer below hits this within a few seconds on my 4-core QEMU VM: int main(int argc, char **argv) { pid_t pid, child; long nproc, i; /* ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)); */ syscall(SYS_ioprio_set, 1, 0, 0x6000); nproc = sysconf(_SC_NPROCESSORS_ONLN); for (i = 0; i < nproc; i++) { pid = fork(); assert(pid != -1); if (pid == 0) { for (;;) { pid = fork(); assert(pid != -1); if (pid == 0) { _exit(0); } else { child = wait(NULL); assert(child == pid); } } } pid = fork(); assert(pid != -1); if (pid == 0) { for (;;) { /* ioprio_get(IOPRIO_WHO_PGRP, 0); */ syscall(SYS_ioprio_get, 2, 0); } } } for (;;) { /* ioprio_get(IOPRIO_WHO_PGRP, 0); */ syscall(SYS_ioprio_get, 2, 0); } return 0; } This gets us KASAN dumps like this: [ 35.526914] ================================================================== [ 35.530009] BUG: KASAN: out-of-bounds in get_task_ioprio+0x7b/0x90 at addr ffff880066f34e6c [ 35.530009] Read of size 2 by task ioprio-gpf/363 [ 35.530009] ============================================================================= [ 35.530009] BUG blkdev_ioc (Not tainted): kasan: bad access detected [ 35.530009] ----------------------------------------------------------------------------- [ 35.530009] Disabling lock debugging due to kernel taint [ 35.530009] INFO: Allocated in create_task_io_context+0x2b/0x370 age=0 cpu=0 pid=360 [ 35.530009] ___slab_alloc+0x55d/0x5a0 [ 35.530009] __slab_alloc.isra.20+0x2b/0x40 [ 35.530009] kmem_cache_alloc_node+0x84/0x200 [ 35.530009] create_task_io_context+0x2b/0x370 [ 35.530009] get_task_io_context+0x92/0xb0 [ 35.530009] copy_process.part.8+0x5029/0x5660 [ 35.530009] _do_fork+0x155/0x7e0 [ 35.530009] SyS_clone+0x19/0x20 [ 35.530009] do_syscall_64+0x195/0x3a0 [ 35.530009] return_from_SYSCALL_64+0x0/0x6a [ 35.530009] INFO: Freed in put_io_context+0xe7/0x120 age=0 cpu=0 pid=1060 [ 35.530009] __slab_free+0x27b/0x3d0 [ 35.530009] kmem_cache_free+0x1fb/0x220 [ 35.530009] put_io_context+0xe7/0x120 [ 35.530009] put_io_context_active+0x238/0x380 [ 35.530009] exit_io_context+0x66/0x80 [ 35.530009] do_exit+0x158e/0x2b90 [ 35.530009] do_group_exit+0xe5/0x2b0 [ 35.530009] SyS_exit_group+0x1d/0x20 [ 35.530009] entry_SYSCALL_64_fastpath+0x1a/0xa4 [ 35.530009] INFO: Slab 0xffffea00019bcd00 objects=20 used=4 fp=0xffff880066f34ff0 flags=0x1fffe0000004080 [ 35.530009] INFO: Object 0xffff880066f34e58 @offset=3672 fp=0x0000000000000001 [ 35.530009] ================================================================== Fix it by grabbing the task lock while we poke at the io_context. Change-Id: I4261aaf076fab943a80a45b0a77e023aa4ecbbd8 Cc: stable@vger.kernel.org Reported-by: Dmitry Vyukov Signed-off-by: Omar Sandoval Signed-off-by: Jens Axboe --- fs/ioprio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/ioprio.c b/fs/ioprio.c index 7da2a06..e325e9f 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -160,8 +160,10 @@ static int get_task_ioprio(struct task_struct *p) if (ret) goto out; ret = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, IOPRIO_NORM); + task_lock(p); if (p->io_context) ret = p->io_context->ioprio; + task_unlock(p); out: return ret; } -- cgit v1.1 From 9aead2670a8345f6fe74b63a6350e2136cbe3ff2 Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Fri, 30 Oct 2015 16:57:00 -0700 Subject: sg: Fix double-free when drives detach during SG_IO In sg_common_write(), we free the block request and return -ENODEV if the device is detached in the middle of the SG_IO ioctl(). Unfortunately, sg_finish_rem_req() also tries to free srp->rq, so we end up freeing rq->cmd in the already free rq object, and then free the object itself out from under the current user. This ends up corrupting random memory via the list_head on the rq object. The most common crash trace I saw is this: ------------[ cut here ]------------ kernel BUG at block/blk-core.c:1420! Call Trace: [] blk_put_request+0x5b/0x80 [] sg_finish_rem_req+0x6b/0x120 [sg] [] sg_common_write.isra.14+0x459/0x5a0 [sg] [] ? selinux_file_alloc_security+0x48/0x70 [] sg_new_write.isra.17+0x195/0x2d0 [sg] [] sg_ioctl+0x644/0xdb0 [sg] [] do_vfs_ioctl+0x90/0x520 [] ? file_has_perm+0x97/0xb0 [] SyS_ioctl+0x91/0xb0 [] tracesys+0xdd/0xe2 RIP [] __blk_put_request+0x154/0x1a0 The solution is straightforward: just set srp->rq to NULL in the failure branch so that sg_finish_rem_req() doesn't attempt to re-free it. Additionally, since sg_rq_end_io() will never be called on the object when this happens, we need to free memory backing ->cmd if it isn't embedded in the object itself. KASAN was extremely helpful in finding the root cause of this bug. Change-Id: I8c2389a4e2e1b5f753a47f8af60502a761b891b5 Signed-off-by: Calvin Owens Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/sg.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 909ed9e..4172f89 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -739,8 +739,14 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, return k; /* probably out of space --> ENOMEM */ } if (sdp->detached) { - if (srp->bio) + if (srp->bio) { + if (srp->rq->cmd != srp->rq->__cmd) + kfree(srp->rq->cmd); + blk_end_request_all(srp->rq, -EIO); + srp->rq = NULL; + } + sg_finish_rem_req(srp); return -ENODEV; } -- cgit v1.1 From 264a926b060fb244459a5e9feb0f0a60c3a559f5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 12:09:10 +0100 Subject: ALSA: usb-audio: Fix NULL dereference in create_fixed_stream_quirk() commit 0f886ca12765d20124bd06291c82951fd49a33be upstream. create_fixed_stream_quirk() may cause a NULL-pointer dereference by accessing the non-existing endpoint when a USB device with a malformed USB descriptor is used. This patch avoids it simply by adding a sanity check of bNumEndpoints before the accesses. Change-Id: I94025f3eec256347b50805b388940774e559dae2 Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=971125 Signed-off-by: Takashi Iwai [bwh: Backported to 3.2: - There's no altsd variable - Adjust context] Signed-off-by: Ben Hutchings --- sound/usb/quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4a650ab..62b955b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -164,6 +164,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, return -EINVAL; } alts = &iface->altsetting[fp->altset_idx]; + if (get_iface_desc(alts)->bNumEndpoints < 1) { + kfree(fp); + kfree(rate_table); + return -EINVAL; + } + fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); usb_set_interface(chip->dev, fp->iface, 0); -- cgit v1.1 From 60117f1016086f658c7d765a7f928e217699dbc4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 12:14:49 +0100 Subject: ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk() commit 902eb7fd1e4af3ac69b9b30f8373f118c92b9729 upstream. Just a minor code cleanup: unify the error paths. Change-Id: Idbac50c4d1602ea3b075c747a716cc6eab905b52 Signed-off-by: Takashi Iwai [bwh: Backported to 3.2: adjust context] Signed-off-by: Ben Hutchings --- sound/usb/quirks.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 62b955b..39e5c77 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -152,22 +152,17 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, stream = (fp->endpoint & USB_DIR_IN) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_endpoint(chip, stream, fp); - if (err < 0) { - kfree(fp); - kfree(rate_table); - return err; - } + if (err < 0) + goto error; if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || fp->altset_idx >= iface->num_altsetting) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } alts = &iface->altsetting[fp->altset_idx]; if (get_iface_desc(alts)->bNumEndpoints < 1) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } fp->datainterval = snd_usb_parse_datainterval(chip, alts); @@ -176,6 +171,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, snd_usb_init_pitch(chip, fp->iface, alts, fp); snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); return 0; + + error: + kfree(fp); + kfree(rate_table); + return err; } /* -- cgit v1.1 From 4c72b649cb557ff8b613b94bd68471ecfb42f0fb Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Thu, 31 Mar 2016 12:05:43 -0400 Subject: ALSA: usb-audio: Fix double-free in error paths after snd_usb_add_audio_stream() call commit 836b34a935abc91e13e63053d0a83b24dfb5ea78 upstream. create_fixed_stream_quirk(), snd_usb_parse_audio_interface() and create_uaxx_quirk() functions allocate the audioformat object by themselves and free it upon error before returning. However, once the object is linked to a stream, it's freed again in snd_usb_audio_pcm_free(), thus it'll be double-freed, eventually resulting in a memory corruption. This patch fixes these failures in the error paths by unlinking the audioformat object before freeing it. Based on a patch by Takashi Iwai [Note for stable backports: this patch requires the commit 902eb7fd1e4a ('ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk()')] Change-Id: Ia332409f06bbd20c0abf9cf915f4a041200e4736 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1283358 Reported-by: Ralf Spenneberg Signed-off-by: Vladis Dronov Signed-off-by: Takashi Iwai Signed-off-by: Ben Hutchings --- sound/usb/endpoint.c | 2 ++ sound/usb/quirks.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 05842c8..2d7b5e7 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -393,6 +393,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) * (fp->maxpacksize & 0x7ff); fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; + INIT_LIST_HEAD(&fp->list); /* some quirks for attributes here */ @@ -434,6 +435,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint); err = snd_usb_add_audio_endpoint(chip, stream, fp); if (err < 0) { + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp->rate_table); kfree(fp); return err; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 39e5c77..d7f1822 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -139,6 +139,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, kfree(fp); return -EINVAL; } + INIT_LIST_HEAD(&fp->list); if (fp->nr_rates > 0) { rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL); if (!rate_table) { @@ -173,6 +174,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, return 0; error: + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp); kfree(rate_table); return err; @@ -243,6 +245,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = 0; fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + INIT_LIST_HEAD(&fp->list); switch (fp->maxpacksize) { case 0x120: @@ -266,6 +269,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_endpoint(chip, stream, fp); if (err < 0) { + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp); return err; } -- cgit v1.1 From b794ccaf33cd5ac7afa88df3bccaf11b4f1a0d55 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 15 Dec 2015 13:49:05 +0100 Subject: perf: Fix race in swevent hash There's a race on CPU unplug where we free the swevent hash array while it can still have events on. This will result in a use-after-free which is BAD. Simply do not free the hash array on unplug. This leaves the thing around and no use-after-free takes place. When the last swevent dies, we do a for_each_possible_cpu() iteration anyway to clean these up, at which time we'll free it, so no leakage will occur. Change-Id: I751faf3215bbdaa6b6358f3a752bdd24126cfa0b Reported-by: Sasha Levin Tested-by: Sasha Levin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Signed-off-by: Ingo Molnar --- kernel/events/core.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index acdc087..3c75ec6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5416,7 +5416,6 @@ static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) int err = 0; mutex_lock(&swhash->hlist_mutex); - if (!swevent_hlist_deref(swhash) && cpu_online(cpu)) { struct swevent_hlist *hlist; @@ -7309,12 +7308,6 @@ static void perf_event_exit_cpu_context(int cpu) static void perf_event_exit_cpu(int cpu) { - struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); - - mutex_lock(&swhash->hlist_mutex); - swevent_hlist_release(swhash); - mutex_unlock(&swhash->hlist_mutex); - perf_event_exit_cpu_context(cpu); } #else -- cgit v1.1 From f4610dd025e74a9cd63a755e1c1ecf71e9cdec0a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 27 Nov 2015 14:30:21 -0500 Subject: BACKPORT: tty: Prevent ldisc drivers from re-using stale tty fields (cherry picked from commit dd42bf1197144ede075a9d4793123f7689e164bc) Line discipline drivers may mistakenly misuse ldisc-related fields when initializing. For example, a failure to initialize tty->receive_room in the N_GIGASET_M101 line discipline was recently found and fixed [1]. Now, the N_X25 line discipline has been discovered accessing the previous line discipline's already-freed private data [2]. Harden the ldisc interface against misuse by initializing revelant tty fields before instancing the new line discipline. [1] commit fd98e9419d8d622a4de91f76b306af6aa627aa9c Author: Tilman Schmidt Date: Tue Jul 14 00:37:13 2015 +0200 isdn/gigaset: reset tty->receive_room when attaching ser_gigaset [2] Report from Sasha Levin [ 634.336761] ================================================================== [ 634.338226] BUG: KASAN: use-after-free in x25_asy_open_tty+0x13d/0x490 at addr ffff8800a743efd0 [ 634.339558] Read of size 4 by task syzkaller_execu/8981 [ 634.340359] ============================================================================= [ 634.341598] BUG kmalloc-512 (Not tainted): kasan: bad access detected ... [ 634.405018] Call Trace: [ 634.405277] dump_stack (lib/dump_stack.c:52) [ 634.405775] print_trailer (mm/slub.c:655) [ 634.406361] object_err (mm/slub.c:662) [ 634.406824] kasan_report_error (mm/kasan/report.c:138 mm/kasan/report.c:236) [ 634.409581] __asan_report_load4_noabort (mm/kasan/report.c:279) [ 634.411355] x25_asy_open_tty (drivers/net/wan/x25_asy.c:559 (discriminator 1)) [ 634.413997] tty_ldisc_open.isra.2 (drivers/tty/tty_ldisc.c:447) [ 634.414549] tty_set_ldisc (drivers/tty/tty_ldisc.c:567) [ 634.415057] tty_ioctl (drivers/tty/tty_io.c:2646 drivers/tty/tty_io.c:2879) [ 634.423524] do_vfs_ioctl (fs/ioctl.c:43 fs/ioctl.c:607) [ 634.427491] SyS_ioctl (fs/ioctl.c:622 fs/ioctl.c:613) [ 634.427945] entry_SYSCALL_64_fastpath (arch/x86/entry/entry_64.S:188) Cc: Tilman Schmidt Cc: Sasha Levin Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman Change-Id: Ibed6feadfb9706d478f93feec3b240aecfc64af3 Bug: 30951112 --- drivers/tty/tty_ldisc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index a76c808..606c0956 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -424,6 +424,10 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); * they are not on hot paths so a little discipline won't do * any harm. * + * The line discipline-related tty_struct fields are reset to + * prevent the ldisc driver from re-using stale information for + * the new ldisc instance. + * * Locking: takes termios_mutex */ @@ -432,6 +436,9 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) mutex_lock(&tty->termios_mutex); tty->termios->c_line = num; mutex_unlock(&tty->termios_mutex); + + tty->disc_data = NULL; + tty->receive_room = 0; } /** -- cgit v1.1 From 92614279392e9115866e2ac4694120f1b732e05c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 19 Jan 2016 12:34:58 +0100 Subject: HID: core: prevent out-of-bound readings Plugging a Logitech DJ receiver with KASAN activated raises a bunch of out-of-bound readings. The fields are allocated up to MAX_USAGE, meaning that potentially, we do not have enough fields to fit the incoming values. Add checks and silence KASAN. Change-Id: I11d44957b450a3eda258c05f9e833c71a079e83c Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8f8728b..9d09e9e 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -977,6 +977,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, /* Ignore report if ErrorRollOver */ if (!(field->flags & HID_MAIN_ITEM_VARIABLE) && value[n] >= min && value[n] <= max && + value[n] - min < field->maxusage && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) goto exit; } @@ -989,11 +990,13 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, } if (field->value[n] >= min && field->value[n] <= max + && field->value[n] - min < field->maxusage && field->usage[field->value[n] - min].hid && search(value, field->value[n], count)) hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt); if (value[n] >= min && value[n] <= max + && value[n] - min < field->maxusage && field->usage[value[n] - min].hid && search(field->value, value[n], count)) hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt); -- cgit v1.1 From f76dac55bdb82459cf3b9c6f988e3797100ff320 Mon Sep 17 00:00:00 2001 From: CNBLACK Date: Fri, 7 Oct 2016 12:04:24 +0900 Subject: i777: Regenerate defconfig Change-Id: I28764a4d6752ccb1501b6af24c7efa443b037dac Signed-off-by: Jeonghun Yang --- arch/arm/configs/cyanogenmod_i777_defconfig | 83 ++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/arch/arm/configs/cyanogenmod_i777_defconfig b/arch/arm/configs/cyanogenmod_i777_defconfig index 447226f..12a6419 100644 --- a/arch/arm/configs/cyanogenmod_i777_defconfig +++ b/arch/arm/configs/cyanogenmod_i777_defconfig @@ -86,12 +86,14 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set # CONFIG_SCHED_AUTOGROUP is not set +CONFIG_MM_OWNER=y # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -103,6 +105,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set CONFIG_INITRAMFS_COMPRESSION_NONE=y # CONFIG_INITRAMFS_COMPRESSION_GZIP is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set @@ -144,6 +147,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set CONFIG_PROFILING=y +# CONFIG_OPROFILE is not set CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -452,12 +456,15 @@ CONFIG_PANEL_U1=y # CONFIG_GC1_00_BD is not set # CONFIG_T0_00_BD is not set # CONFIG_T0_04_BD is not set +# CONFIG_KONA_00_BD is not set +# CONFIG_KONA_01_BD is not set # CONFIG_IRON_BD is not set # CONFIG_GRANDE_BD is not set # CONFIG_WRITEBACK_ENABLED is not set # CONFIG_EXYNOS_SOUND_PLATFORM_DATA is not set # CONFIG_JACK_FET is not set # CONFIG_JACK_GROUND_DET is not set +# CONFIG_USE_ADC_DET is not set CONFIG_SAMSUNG_ANALOG_UART_SWITCH=1 # CONFIG_EXYNOS5_DEV_BTS is not set @@ -508,6 +515,7 @@ CONFIG_SEC_LOG=y CONFIG_SEC_LOG_NONCACHED=y CONFIG_SEC_LOG_LAST_KMSG=y CONFIG_EHCI_IRQ_DISTRIBUTION=y +CONFIG_EHCI_MODEM_PORTNUM=2 # # Samsung Modem Feature @@ -531,6 +539,11 @@ CONFIG_SEC_MODEM_U1=y # CONFIG_SEC_MODEM_P8LTE is not set # CONFIG_SEC_MODEM_T0_TD_DUAL is not set # CONFIG_SEC_MODEM_U1_SPR is not set + +# +# Connectivity Feature +# +# CONFIG_GPS_BRCM_475X is not set # CONFIG_BT_CSR8811 is not set CONFIG_BT_BCM4330=y # CONFIG_BT_BCM4334 is not set @@ -549,6 +562,7 @@ CONFIG_BT_MGMT=y CONFIG_USB_CDFS_SUPPORT=y CONFIG_SAMSUNG_PRODUCT_SHIP=y # CONFIG_CORESIGHT_ETM is not set +# CONFIG_MACH_KONA_SENSOR is not set # # Processor Type @@ -662,6 +676,8 @@ CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y @@ -718,6 +734,7 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND_FLEXRATE_MAX_DURATION=100 CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +# CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST is not set # CONFIG_CPU_FREQ_GOV_SLP is not set # CONFIG_CPU_FREQ_DVFS_MONITOR is not set CONFIG_CPU_IDLE=y @@ -762,6 +779,7 @@ CONFIG_EARLYSUSPEND=y # CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set # CONFIG_CONSOLE_EARLYSUSPEND is not set CONFIG_FB_EARLYSUSPEND=y +# CONFIG_HIBERNATION is not set CONFIG_PM_SLEEP=y CONFIG_PM_SLEEP_SMP=y CONFIG_PM_RUNTIME=y @@ -772,6 +790,7 @@ CONFIG_ARCH_HAS_OPP=y CONFIG_PM_OPP=y CONFIG_PM_RUNTIME_CLK=y # CONFIG_SUSPEND_TIME is not set +CONFIG_CPU_PM=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -1172,6 +1191,14 @@ CONFIG_RFKILL_PM=y # # Device Drivers # +CONFIG_MALI400=y +CONFIG_MALI_VER_R3P2=y +# CONFIG_MALI400_DEBUG is not set +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_DVFS=y +CONFIG_MALI400_UMP=y +# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_UMP_DEBUG is not set # # Generic Driver Options @@ -1195,6 +1222,9 @@ CONFIG_SW_SYNC_USER=y # CONFIG_MTD is not set # CONFIG_PARPORT is not set CONFIG_BLK_DEV=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_LZ4_COMPRESS is not set +# CONFIG_ZRAM_DEBUG is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -1251,6 +1281,7 @@ CONFIG_PN544=y # CONFIG_STMPE811_ADC is not set # CONFIG_MPU_SENSORS_MPU3050 is not set # CONFIG_MPU_SENSORS_MPU6050 is not set +CONFIG_UID_CPUTIME=y # CONFIG_C2PORT is not set # @@ -1274,10 +1305,13 @@ CONFIG_UMTS_MODEM_XMM6260=y # CONFIG_UMTS_MODEM_XMM6262 is not set # CONFIG_CDMA_MODEM_CBP71 is not set # CONFIG_CDMA_MODEM_CBP72 is not set +# CONFIG_CDMA_MODEM_CBP82 is not set # CONFIG_LTE_MODEM_CMC221 is not set +# CONFIG_UMTS_MODEM_SS222 is not set # CONFIG_CDMA_MODEM_MDM6600 is not set # CONFIG_TDSCDMA_MODEM_SPRD8803 is not set # CONFIG_GSM_MODEM_ESC6270 is not set +# CONFIG_CDMA_MODEM_QSC6085 is not set # CONFIG_LINK_DEVICE_MIPI is not set # CONFIG_LINK_DEVICE_DPRAM is not set # CONFIG_LINK_DEVICE_PLD is not set @@ -1285,6 +1319,7 @@ CONFIG_UMTS_MODEM_XMM6260=y CONFIG_LINK_DEVICE_HSIC=y # CONFIG_LINK_DEVICE_C2C is not set # CONFIG_LINK_DEVICE_SPI is not set +# CONFIG_BOOT_DEVICE_SPI is not set # CONFIG_WORKQUEUE_FRONT is not set # CONFIG_IPC_CMC22x_OLD_RFS is not set # CONFIG_SIPC_VER_5 is not set @@ -1373,6 +1408,8 @@ CONFIG_WIFI_CONTROL_FUNC=y CONFIG_BCM4330=m # CONFIG_BCM4334 is not set # CONFIG_BCM4335 is not set +# CONFIG_BCM4339 is not set +# CONFIG_BCM4354 is not set # CONFIG_BCM43241 is not set CONFIG_BROADCOM_WIFI=y CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" @@ -1463,7 +1500,10 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set -CONFIG_KEYBOARD_CYPRESS_TOUCH=y +# CONFIG_SENSORS_HALL is not set +# CONFIG_KEYBOARD_CYPRESS_TOUCH is not set +CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN=y +CONFIG_TOUCHKEY_BLN=y # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set @@ -1519,8 +1559,12 @@ CONFIG_TOUCHSCREEN_ATMEL_MXT224_U1=y # CONFIG_TOUCHSCREEN_MXT1386 is not set # CONFIG_TOUCHSCREEN_MXT768E is not set # CONFIG_TOUCHSCREEN_SYNAPTICS_S7301 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYS is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_WORKAROUND is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYLED is not set # CONFIG_TOUCHSCREEN_CYTTSP4 is not set # CONFIG_SEC_TOUCHSCREEN_DVFS_LOCK is not set +# CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH is not set # CONFIG_KEYPAD_MELFAS_TOUCH is not set # CONFIG_TOUCHSCREEN_ATMEL_MXT540S is not set # CONFIG_INPUT_WACOM is not set @@ -1758,6 +1802,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_MAX17043_FUELGAUGE is not set # CONFIG_BATTERY_MAX17042_FUELGAUGE is not set # CONFIG_BATTERY_MAX17047_FUELGAUGE is not set +# CONFIG_BATTERY_MAX17047_C_FUELGAUGE is not set # CONFIG_BATTERY_SMB136_CHARGER is not set # CONFIG_BATTERY_SAMSUNG_P1X is not set # CONFIG_CHARGER_MAX8903 is not set @@ -1981,6 +2026,7 @@ CONFIG_VIDEO_S5K5BAFX=y # CONFIG_VIDEO_S5K4EA is not set # CONFIG_VIDEO_S5C73M3 is not set # CONFIG_VIDEO_SLP_S5C73M3 is not set +# CONFIG_VIDEO_SR130PC20 is not set CONFIG_VIDEO_IMPROVE_STREAMOFF=y CONFIG_CSI_C=y # CONFIG_CSI_D is not set @@ -2047,7 +2093,6 @@ CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y # CONFIG_USB_ZR364XX is not set # CONFIG_USB_STKWEBCAM is not set # CONFIG_USB_S2255 is not set -CONFIG_MALI_400MP_UMP=y CONFIG_VIDEO_SAMSUNG=y CONFIG_VIDEO_SAMSUNG_V4L2=y CONFIG_VIDEO_FIMC=y @@ -2076,6 +2121,8 @@ CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 CONFIG_VIDEO_MFC_MEM_PORT_COUNT=2 # CONFIG_VIDEO_MFC5X_DEBUG is not set +# CONFIG_VIDEO_MALI400MP is not set +# CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y # CONFIG_VIDEO_FIMG2D_DEBUG is not set CONFIG_VIDEO_FIMG2D3X=y @@ -2112,19 +2159,12 @@ CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE=y # # Graphics support # -# CONFIG_MALI_VER_BEFORE_R3P2 is not set # CONFIG_DRM is not set CONFIG_ION=y CONFIG_ION_EXYNOS=y CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=71680 # CONFIG_ION_EXYNOS_CONTIGHEAP_DEBUG is not set -CONFIG_MALI400=y -CONFIG_MALI_VER_R3P2=y -# CONFIG_MALI400_DEBUG is not set -# CONFIG_MALI400_PROFILING is not set -CONFIG_MALI_DVFS=y -CONFIG_MALI400_UMP=y -# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set @@ -2199,6 +2239,7 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_BACKLIGHT_PWM is not set # CONFIG_BACKLIGHT_ADP8860 is not set # CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set # # Display device support @@ -2661,8 +2702,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_LINE6_USB is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -# CONFIG_XVMALLOC is not set -CONFIG_ZRAM=y # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -2704,10 +2743,15 @@ CONFIG_SENSORS_CM3663=y # CONFIG_SENSORS_CM36651 is not set # CONFIG_SENSORS_BH1721 is not set # CONFIG_SENSORS_AL3201 is not set +# CONFIG_SENSORS_K2DH is not set CONFIG_SENSORS_K3DH=y +# CONFIG_SENSOR_K3DH_INPUTDEV is not set CONFIG_SENSORS_K3G=y # CONFIG_SENSORS_LSM330DLC is not set # CONFIG_SENSORS_LPS331 is not set +# CONFIG_SENSORS_YAS532 is not set +# CONFIG_SENSORS_YAS_ORI is not set +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=0 # CONFIG_SENSORS_SYSFS is not set # CONFIG_SENSORS_SSP is not set # CONFIG_SENSORS_SSP_LSM330 is not set @@ -2822,6 +2866,12 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +CONFIG_F2FS_FS=y +# CONFIG_F2FS_STAT_FS is not set +CONFIG_F2FS_FS_XATTR=y +# CONFIG_F2FS_FS_POSIX_ACL is not set +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_CHECK_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y @@ -2920,7 +2970,6 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y -CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y @@ -3137,7 +3186,9 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y # CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set -# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_LZ4=y +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation @@ -3163,6 +3214,10 @@ CONFIG_LIBCRC32C=y CONFIG_AUDIT_GENERIC=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_XZ_DEC is not set # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y -- cgit v1.1 From e5d626e2c4eb2acb92b9cd05f0d474185c4d586f Mon Sep 17 00:00:00 2001 From: RGIB Date: Wed, 23 Nov 2016 00:09:23 +0100 Subject: smdk4412 : new i9305 defconfig Change-Id: I1d67d7e79502eec9dca036b4ea7c4f8a41d1c128 --- arch/arm/configs/cyanogenmod_i9305_defconfig | 254 +++++++++++++++++++++++---- 1 file changed, 221 insertions(+), 33 deletions(-) mode change 100755 => 100644 arch/arm/configs/cyanogenmod_i9305_defconfig diff --git a/arch/arm/configs/cyanogenmod_i9305_defconfig b/arch/arm/configs/cyanogenmod_i9305_defconfig old mode 100755 new mode 100644 index 1646b9e..75b99a1 --- a/arch/arm/configs/cyanogenmod_i9305_defconfig +++ b/arch/arm/configs/cyanogenmod_i9305_defconfig @@ -30,7 +30,6 @@ CONFIG_ARM_PATCH_PHYS_VIRT=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup @@ -70,6 +69,7 @@ CONFIG_GENERIC_IRQ_CHIP=y # RCU Subsystem # CONFIG_TREE_PREEMPT_RCU=y +# CONFIG_TINY_RCU is not set CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set CONFIG_RCU_FANOUT=32 @@ -106,6 +106,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_CC_CHECK_WARNING_STRICTLY is not set CONFIG_SYSCTL=y @@ -145,6 +146,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set CONFIG_PROFILING=y +# CONFIG_OPROFILE is not set CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -154,7 +156,6 @@ CONFIG_HAVE_DMA_CONTIGUOUS=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -185,6 +186,8 @@ CONFIG_IOSCHED_ROW=y CONFIG_IOSCHED_SIO=y # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_ROW is not set +# CONFIG_DEFAULT_SIO is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set @@ -422,7 +425,6 @@ CONFIG_SEC_THERMISTOR=y # CONFIG_EXYNOS_SYSREG_PM is not set CONFIG_ANDROID_WIP=y # CONFIG_COMPACTION_RETRY is not set -# CONFIG_COMPACTION_RETRY_DEBUG is not set # # EXYNOS4 Machines @@ -435,11 +437,11 @@ CONFIG_ANDROID_WIP=y # CONFIG_MACH_U1_NA_SPR is not set # CONFIG_MACH_U1_NA_USCC is not set # CONFIG_MACH_U1 is not set +# CONFIG_TARGET_LOCALE_NAATT_TEMP is not set # CONFIG_MACH_PX is not set CONFIG_TARGET_LOCALE_EUR=y # CONFIG_TARGET_LOCALE_LTN is not set # CONFIG_TARGET_LOCALE_KOR is not set -# CONFIG_TARGET_LOCALE_NAATT_TEMP is not set # CONFIG_TARGET_LOCALE_P2EUR_TEMP is not set # CONFIG_TARGET_LOCALE_P2TMO_TEMP is not set # CONFIG_TARGET_LOCALE_NA is not set @@ -456,6 +458,7 @@ CONFIG_MACH_M3=y # CONFIG_MACH_P4NOTE is not set # CONFIG_MACH_GC1 is not set # CONFIG_MACH_T0 is not set +# CONFIG_MACH_KONA is not set # CONFIG_MACH_IRON is not set # CONFIG_MACH_GRANDE is not set # CONFIG_MACH_BAFFIN is not set @@ -464,12 +467,15 @@ CONFIG_MIDAS_COMMON_BD=y # CONFIG_GC1_00_BD is not set # CONFIG_T0_00_BD is not set # CONFIG_T0_04_BD is not set +# CONFIG_KONA_00_BD is not set +# CONFIG_KONA_01_BD is not set # CONFIG_IRON_BD is not set # CONFIG_GRANDE_BD is not set # CONFIG_WRITEBACK_ENABLED is not set # CONFIG_EXYNOS_SOUND_PLATFORM_DATA is not set # CONFIG_JACK_FET is not set # CONFIG_JACK_GROUND_DET is not set +# CONFIG_USE_ADC_DET is not set CONFIG_SAMSUNG_ANALOG_UART_SWITCH=1 # CONFIG_EXYNOS5_DEV_BTS is not set @@ -522,6 +528,11 @@ CONFIG_SEC_LOG=y CONFIG_SEC_LOG_NONCACHED=y CONFIG_SEC_LOG_LAST_KMSG=y # CONFIG_EHCI_IRQ_DISTRIBUTION is not set + +# +# Connectivity Feature +# +# CONFIG_GPS_BRCM_475X is not set # CONFIG_BT_CSR8811 is not set # CONFIG_BT_BCM4330 is not set CONFIG_BT_BCM4334=y @@ -536,10 +547,12 @@ CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_QC_MODEM_MDM9X15=y CONFIG_MDM_HSIC_PM=y # CONFIG_EMI_ERROR_RECOVERY is not set +# CONFIG_SIM_DETECT is not set CONFIG_QC_MODEM_M3=y CONFIG_USB_CDFS_SUPPORT=y CONFIG_SAMSUNG_PRODUCT_SHIP=y # CONFIG_CORESIGHT_ETM is not set +# CONFIG_MACH_KONA_SENSOR is not set # # Processor Type @@ -594,6 +607,9 @@ CONFIG_ARM_ERRATA_761320=y # CONFIG_ARM_ERRATA_761171 is not set # CONFIG_ARM_ERRATA_762974 is not set # CONFIG_ARM_ERRATA_763722 is not set +CONFIG_ARM_ERRATA_764369=y +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set CONFIG_ARM_GIC=y CONFIG_PL330=y # CONFIG_FIQ_DEBUGGER is not set @@ -604,8 +620,6 @@ CONFIG_PL330=y # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set # CONFIG_PCCARD is not set -CONFIG_ARM_ERRATA_764369=y -# CONFIG_PL310_ERRATA_769419 is not set # # Kernel Features @@ -637,14 +651,13 @@ CONFIG_HAVE_ARCH_PFN_VALID=y CONFIG_ARCH_SKIP_SECONDARY_CALIBRATE=y CONFIG_HIGHMEM=y # CONFIG_HIGHPTE is not set -CONFIG_HW_PERF_EVENTS=y CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_SPLIT_PTLOCK_CPUS=999999 CONFIG_COMPACTION=y CONFIG_MIGRATION=y # CONFIG_PHYS_ADDR_T_64BIT is not set @@ -654,6 +667,7 @@ CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 # CONFIG_CLEANCACHE is not set +# CONFIG_ZSMALLOC is not set CONFIG_CMA=y # CONFIG_CMA_DEVELOPEMENT is not set CONFIG_CMA_BEST_FIT=y @@ -709,8 +723,9 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_GOV_ADAPTIVE is not set CONFIG_CPU_FREQ_GOV_PEGASUSQ=y +# CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST is not set # CONFIG_CPU_FREQ_GOV_SLP is not set -# CONFIG_CPU_FREQ_DVFS_MONITOR is not set +CONFIG_CPU_FREQ_DVFS_MONITOR=y CONFIG_CPU_IDLE=y CONFIG_CPU_IDLE_GOV_LADDER=y CONFIG_CPU_IDLE_GOV_MENU=y @@ -760,6 +775,7 @@ CONFIG_ARCH_HAS_OPP=y CONFIG_PM_OPP=y CONFIG_PM_RUNTIME_CLK=y # CONFIG_SUSPEND_TIME is not set +CONFIG_CPU_PM=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -1094,7 +1110,6 @@ CONFIG_XPS=y # Network testing # # CONFIG_NET_PKTGEN is not set -# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1148,6 +1163,7 @@ CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set # CONFIG_CFG80211_ALLOW_RECONNECT is not set # CONFIG_MAC80211 is not set +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set # CONFIG_WIMAX is not set CONFIG_RFKILL=y CONFIG_RFKILL_PM=y @@ -1161,6 +1177,14 @@ CONFIG_RFKILL_PM=y # # Device Drivers # +CONFIG_MALI400=y +CONFIG_MALI_VER_R3P2=y +# CONFIG_MALI400_DEBUG is not set +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_DVFS=y +CONFIG_MALI400_UMP=y +# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_UMP_DEBUG is not set # # Generic Driver Options @@ -1242,6 +1266,7 @@ CONFIG_MUIC_MAX77693_SUPPORT_CAR_DOCK=y # CONFIG_STMPE811_ADC is not set # CONFIG_MPU_SENSORS_MPU3050 is not set # CONFIG_MPU_SENSORS_MPU6050 is not set +CONFIG_UID_CPUTIME=y # CONFIG_C2PORT is not set # @@ -1342,11 +1367,19 @@ CONFIG_WLAN=y # CONFIG_USB_NET_RNDIS_WLAN is not set CONFIG_WIFI_CONTROL_FUNC=y # CONFIG_ATH_COMMON is not set -CONFIG_BCM4334=m +# CONFIG_B43LEGACY_DMA_AND_PIO_MODE is not set +# CONFIG_B43LEGACY_DMA_MODE is not set +# CONFIG_B43LEGACY_PIO_MODE is not set # CONFIG_BCM4330 is not set +CONFIG_BCM4334=m +# CONFIG_BCM4335 is not set +# CONFIG_BCM4339 is not set +# CONFIG_BCM4354 is not set # CONFIG_BCM43241 is not set +CONFIG_BROADCOM_WIFI=y +CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y -# CONFIG_WLAN_COUNTRY_CODE is not set CONFIG_WLAN_REGION_CODE=100 # CONFIG_HOSTAP is not set # CONFIG_IWM is not set @@ -1416,7 +1449,7 @@ CONFIG_SLHC=y # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set # CONFIG_ISDN is not set -# CONFIG_PHONE is not set +CONFIG_PHONE=y # # Input device support @@ -1462,7 +1495,9 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_KEYBOARD_STOWAWAY is not set # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_SENSORS_HALL is not set CONFIG_KEYBOARD_CYPRESS_TOUCH=y +# CONFIG_KEYBOARD_CYPRESS_TOUCH_BLN is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set @@ -1517,8 +1552,12 @@ CONFIG_TOUCHSCREEN_MELFAS=y # CONFIG_TOUCHSCREEN_MXT1386 is not set # CONFIG_TOUCHSCREEN_MXT768E is not set # CONFIG_TOUCHSCREEN_SYNAPTICS_S7301 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYS is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_WORKAROUND is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_S7301_KEYLED is not set # CONFIG_TOUCHSCREEN_CYTTSP4 is not set # CONFIG_SEC_TOUCHSCREEN_DVFS_LOCK is not set +# CONFIG_SEC_TOUCHSCREEN_SURFACE_TOUCH is not set # CONFIG_KEYPAD_MELFAS_TOUCH is not set # CONFIG_TOUCHSCREEN_ATMEL_MXT540S is not set # CONFIG_INPUT_WACOM is not set @@ -1758,6 +1797,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_MAX17043_FUELGAUGE is not set # CONFIG_BATTERY_MAX17042_FUELGAUGE is not set CONFIG_BATTERY_MAX17047_FUELGAUGE=y +# CONFIG_BATTERY_MAX17047_C_FUELGAUGE is not set # CONFIG_BATTERY_SMB136_CHARGER is not set CONFIG_BATTERY_MAX77693_CHARGER=y CONFIG_BATTERY_WPC_CHARGER=y @@ -1789,10 +1829,103 @@ CONFIG_BATTERY_SAMSUNG=y # CONFIG_SMB328_CHARGER is not set # CONFIG_SMB347_CHARGER is not set # CONFIG_CHARGER_MANAGER is not set -# CONFIG_HWMON is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_S3C is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set CONFIG_THERMAL=y +# CONFIG_THERMAL_HWMON is not set # CONFIG_CPU_THERMAL is not set -# CONFIG_SENSORS_EXYNOS4_TMU is not set +CONFIG_SENSORS_EXYNOS4_TMU=y CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set @@ -2010,6 +2143,7 @@ CONFIG_S5K6A3_CSI_D=y CONFIG_VIDEO_S5C73M3=y CONFIG_VIDEO_S5C73M3_SPI=y # CONFIG_VIDEO_SLP_S5C73M3 is not set +# CONFIG_VIDEO_SR130PC20 is not set # CONFIG_VIDEO_IMPROVE_STREAMOFF is not set # @@ -2076,7 +2210,6 @@ CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y # CONFIG_USB_ZR364XX is not set # CONFIG_USB_STKWEBCAM is not set # CONFIG_USB_S2255 is not set -CONFIG_MALI_400MP_UMP=y CONFIG_VIDEO_SAMSUNG=y CONFIG_VIDEO_SAMSUNG_V4L2=y CONFIG_VIDEO_FIMC=y @@ -2105,6 +2238,8 @@ CONFIG_LSI_HDMI_AUDIO_CH_EVENT=y CONFIG_VIDEO_MFC5X=y CONFIG_VIDEO_MFC_MAX_INSTANCE=4 # CONFIG_VIDEO_MFC5X_DEBUG is not set +# CONFIG_VIDEO_MALI400MP is not set +# CONFIG_VIDEO_UMP is not set CONFIG_VIDEO_FIMG2D=y # CONFIG_VIDEO_FIMG2D_DEBUG is not set CONFIG_VIDEO_FIMG2D4X=y @@ -2158,19 +2293,12 @@ CONFIG_SAMSUNG_WORKAROUND_HPD_GLANCE=y # # Graphics support # -# CONFIG_MALI_VER_BEFORE_R3P2 is not set # CONFIG_DRM is not set CONFIG_ION=y CONFIG_ION_EXYNOS=y CONFIG_ION_EXYNOS_CONTIGHEAP_SIZE=71680 # CONFIG_ION_EXYNOS_CONTIGHEAP_DEBUG is not set -CONFIG_MALI400=y -CONFIG_MALI_VER_R3P2=y -# CONFIG_MALI400_DEBUG is not set -# CONFIG_MALI400_PROFILING is not set -CONFIG_MALI_DVFS=y -CONFIG_MALI400_UMP=y -# CONFIG_MALI_SHARED_INTERRUPTS is not set +# CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set @@ -2222,6 +2350,7 @@ CONFIG_FB_S5P_S6E8AA0=y # CONFIG_FB_S5P_S6EVR02 is not set # CONFIG_FB_S5P_S6D6AA1 is not set # CONFIG_FB_S5P_S6E63M0 is not set +# CONFIG_FB_S5P_NT71391 is not set # CONFIG_S6E8AA0_AMS529HA01 is not set CONFIG_S6E8AA0_AMS480GYXX=y CONFIG_AID_DIMMING=y @@ -2231,10 +2360,13 @@ CONFIG_FB_S5P_EXTDSP=y CONFIG_FB_S5P_EXTDSP_NR_BUFFERS=3 # CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD is not set # CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_CARMINE_DRAM_EVAL is not set +# CONFIG_CARMINE_DRAM_CUSTOM is not set # CONFIG_FB_TMIO is not set # CONFIG_FB_UDL is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX_PCI_GDC is not set # CONFIG_FB_BROADSHEET is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y @@ -2256,6 +2388,7 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_BACKLIGHT_PWM is not set # CONFIG_BACKLIGHT_ADP8860 is not set # CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LP855X is not set # # Display device support @@ -2455,6 +2588,8 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_USB_HWA_HCD is not set # CONFIG_USB_S3C_OTG_HOST is not set # CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set # # USB Device Class drivers @@ -2486,7 +2621,6 @@ CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_KARMA is not set # CONFIG_USB_STORAGE_CYPRESS_ATACB is not set # CONFIG_USB_STORAGE_ENE_UB6250 is not set -# CONFIG_USB_UAS is not set # CONFIG_USB_LIBUSUAL is not set # @@ -2590,6 +2724,12 @@ CONFIG_USB_GADGET_SELECTED=y CONFIG_USB_GADGET_S3C_OTGD=y # CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_CI13XXX_PCI is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_EG20T is not set # CONFIG_USB_GADGET_DUMMY_HCD is not set # @@ -2619,6 +2759,8 @@ CONFIG_USB_DUN_SUPPORT=y # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set # CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_DBGP_PRINTK is not set +# CONFIG_USB_G_DBGP_SERIAL is not set # CONFIG_USB_G_WEBCAM is not set # @@ -2647,6 +2789,7 @@ CONFIG_MMC_BLOCK_BOUNCE=y # CONFIG_SDIO_UART is not set # CONFIG_MMC_TEST is not set CONFIG_MMC_SELECTIVE_PACKED_CMD_POLICY=y +# CONFIG_MMC_CPRM is not set # # MMC/SD/SDIO Host Controller Drivers @@ -2692,6 +2835,7 @@ CONFIG_LEDS_AAT1290A=y # # LED Triggers # +# CONFIG_LEDS_TRIGGER_NOTIFICATION is not set CONFIG_NFC_DEVICES=y # CONFIG_PN544_NFC is not set CONFIG_PN65N_NFC=y @@ -2806,14 +2950,19 @@ CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d CONFIG_ANDROID_TIMED_OUTPUT=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_LMK_ADJ_RBTREE=y # CONFIG_POHMELFS is not set # CONFIG_LINE6_USB is not set # CONFIG_USB_SERIAL_QUATECH2 is not set # CONFIG_USB_SERIAL_QUATECH_USB2 is not set # CONFIG_VT6656 is not set # CONFIG_IIO is not set -# CONFIG_XVMALLOC is not set -# CONFIG_ZRAM is not set +# CONFIG_LIS3L02DQ_BUF_KFIFO is not set +# CONFIG_LIS3L02DQ_BUF_RING_SW is not set +# CONFIG_AD2S1210_GPIO_INPUT is not set +# CONFIG_AD2S1210_GPIO_OUTPUT is not set +# CONFIG_AD2S1210_GPIO_NONE is not set # CONFIG_FB_SM7XX is not set # CONFIG_LIRC_STAGING is not set # CONFIG_EASYCAP is not set @@ -2853,10 +3002,15 @@ CONFIG_SENSORS_AK8975C=y CONFIG_SENSORS_CM36651=y # CONFIG_SENSORS_BH1721 is not set # CONFIG_SENSORS_AL3201 is not set +# CONFIG_SENSORS_K2DH is not set # CONFIG_SENSORS_K3DH is not set # CONFIG_SENSORS_K3G is not set CONFIG_SENSORS_LSM330DLC=y +CONFIG_SENSORS_LSM330DLC_USE_INPUT_DEV=y CONFIG_SENSORS_LPS331=y +# CONFIG_SENSORS_YAS532 is not set +# CONFIG_SENSORS_YAS_ORI is not set +CONFIG_INPUT_YAS_MAGNETOMETER_POSITION=0 # CONFIG_SENSORS_SYSFS is not set # CONFIG_SENSORS_SSP is not set # CONFIG_SENSORS_SSP_LSM330 is not set @@ -2866,6 +3020,20 @@ CONFIG_SENSORS_LPS331=y # CONFIG_SENSORS_SSP_AT32UC3L0128 is not set # CONFIG_SENSORS_SSP_SENSORHUB is not set # CONFIG_PM_DEVFREQ is not set + +# +# DEVFREQ Governors +# +# CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND is not set +# CONFIG_DEVFREQ_GOV_PERFORMANCE is not set +# CONFIG_DEVFREQ_GOV_POWERSAVE is not set +# CONFIG_DEVFREQ_GOV_USERSPACE is not set + +# +# DEVFREQ Drivers +# +# CONFIG_ARM_EXYNOS4_BUS_DEVFREQ is not set +# CONFIG_ARM_EXYNOS4_DISPLAY_DEVFREQ is not set # CONFIG_SAMSUNG_PHONE_SVNET is not set CONFIG_ACCESSORY=y # CONFIG_30PIN_CONN is not set @@ -2876,8 +3044,12 @@ CONFIG_ACCESSORY=y # CONFIG_IR_REMOCON is not set # CONFIG_EXTCON is not set # CONFIG_BARCODE_EMUL is not set +# CONFIG_SAMSUNG_PHONE_TTY is not set +# CONFIG_SAMSUNG_PHONE_TTY_RAFFAELLO is not set +# CONFIG_SAMSUNG_PHONE_TTY_RAFFAELLO_RECOVERY is not set CONFIG_MOBICORE_SUPPORT=y -# CONFIG_MOBICORE_DEBUG is not set +CONFIG_MOBICORE_DEBUG=y +CONFIG_MOBICORE_VERBOSE=y CONFIG_MOBICORE_API=y CONFIG_IOMMU_SUPPORT=y # CONFIG_FELICA is not set @@ -2961,6 +3133,10 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS 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_LOGFS is not set # CONFIG_CRAMFS is not set # CONFIG_SQUASHFS is not set @@ -2970,9 +3146,13 @@ CONFIG_WTL_ENCRYPTION_FILTER=y # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set +# CONFIG_ROMFS_BACKED_BY_BLOCK is not set +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set # CONFIG_PSTORE is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y @@ -3070,7 +3250,6 @@ CONFIG_NLS_UTF8=y # CONFIG_PRINTK_TIME=y CONFIG_PRINTK_CPU_ID=y -CONFIG_UID_CPUTIME=y # CONFIG_PRINTK_PID is not set CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y @@ -3100,11 +3279,12 @@ CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set # CONFIG_SLUB_STATS is not set # CONFIG_DEBUG_KMEMLEAK is not set -# CONFIG_DEBUG_PREEMPT is not set -# CONFIG_DEBUG_RT_MUTEXES is not set +CONFIG_DEBUG_PREEMPT=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_PI_LIST=y # CONFIG_RT_MUTEX_TESTER is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_MUTEXES is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y # CONFIG_DEBUG_LOCK_ALLOC is not set # CONFIG_PROVE_LOCKING is not set # CONFIG_SPARSE_RCU_POINTER is not set @@ -3153,6 +3333,9 @@ CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y CONFIG_TRACING_SUPPORT=y # CONFIG_FTRACE is not set +# CONFIG_BRANCH_PROFILE_NONE is not set +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set @@ -3191,6 +3374,9 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_SECURITY_APPARMOR is not set # CONFIG_IMA is not set CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set # CONFIG_DEFAULT_SECURITY_DAC is not set CONFIG_DEFAULT_SECURITY="selinux" CONFIG_CRYPTO=y @@ -3291,6 +3477,8 @@ CONFIG_CRYPTO_TWOFISH_COMMON=y CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set # CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set # # Random Number Generation -- cgit v1.1 From e6e07a287e0794324a633a2bab5440bc9c8a5b3b Mon Sep 17 00:00:00 2001 From: RGIB Date: Fri, 9 Dec 2016 16:17:28 +0100 Subject: smdk4412-kernel : kona wifi module is COB type Change-Id: I803de2a929dbb9fe470414ea55f2401da5637791 --- arch/arm/configs/cyanogenmod_n5100_defconfig | 1 + arch/arm/configs/cyanogenmod_n5110_defconfig | 1 + arch/arm/configs/cyanogenmod_n5120_defconfig | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/arm/configs/cyanogenmod_n5100_defconfig b/arch/arm/configs/cyanogenmod_n5100_defconfig index a2416bd..98e0b72 100644 --- a/arch/arm/configs/cyanogenmod_n5100_defconfig +++ b/arch/arm/configs/cyanogenmod_n5100_defconfig @@ -1442,6 +1442,7 @@ CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y CONFIG_WLAN_REGION_CODE=100 +CONFIG_WIFI_BROADCOM_COB=y # CONFIG_HOSTAP is not set # CONFIG_IWM is not set # CONFIG_LIBERTAS is not set diff --git a/arch/arm/configs/cyanogenmod_n5110_defconfig b/arch/arm/configs/cyanogenmod_n5110_defconfig index 8bd50a1..800b023 100644 --- a/arch/arm/configs/cyanogenmod_n5110_defconfig +++ b/arch/arm/configs/cyanogenmod_n5110_defconfig @@ -1442,6 +1442,7 @@ CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y CONFIG_WLAN_REGION_CODE=100 +CONFIG_WIFI_BROADCOM_COB=y # CONFIG_HOSTAP is not set # CONFIG_IWM is not set # CONFIG_LIBERTAS is not set diff --git a/arch/arm/configs/cyanogenmod_n5120_defconfig b/arch/arm/configs/cyanogenmod_n5120_defconfig index a2e02ce..445e41a 100644 --- a/arch/arm/configs/cyanogenmod_n5120_defconfig +++ b/arch/arm/configs/cyanogenmod_n5120_defconfig @@ -1393,6 +1393,7 @@ CONFIG_BCMDHD_FW_PATH="/system/etc/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/system/etc/wifi/bcmdhd.cal" CONFIG_BROADCOM_WIFI_RESERVED_MEM=y CONFIG_WLAN_REGION_CODE=100 +CONFIG_WIFI_BROADCOM_COB=y # CONFIG_HOSTAP is not set # CONFIG_IWM is not set # CONFIG_LIBERTAS is not set -- cgit v1.1 From 174f062a796db86159beed79039c77d46e6a7c8f Mon Sep 17 00:00:00 2001 From: RGIB Date: Fri, 9 Dec 2016 22:42:43 +0100 Subject: smdk4412-kernel : do not ignore COB wifi chips Change-Id: Ic3edddb6d0909df89a0d9551394891fa1fac707a --- drivers/net/wireless/bcmdhd/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig index d148725..f4f5b96 100644 --- a/drivers/net/wireless/bcmdhd/Kconfig +++ b/drivers/net/wireless/bcmdhd/Kconfig @@ -89,6 +89,12 @@ config BROADCOM_WIFI_RESERVED_MEM ---help--- This is a configuration for broadcom WLAN driver. +config WIFI_BROADCOM_COB + bool "BROADCOM WIFI COB" + depends on (BCM4334) + ---help--- + This is a configuration for broadcom WIFI COB Type. + config BCM4335BT bool "BROADCOM BTLOCK Enable" depends on BCM4335 -- cgit v1.1 From 55306f62c769cba0a97465b04790b8b9fbdce907 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 15 Jun 2012 14:31:32 +0800 Subject: BACKPORT: perf: Allow the PMU driver to choose the CPU on which to install events Allow the pmu->event_init callback to change event->cpu, so the PMU driver can choose the CPU on which to install events. Change-Id: I0f8b4310d306f4c87bc961f0359c2bdf65c129b6 Signed-off-by: Zheng Yan Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1339741902-8449-4-git-send-email-zheng.z.yan@intel.com Signed-off-by: Ingo Molnar --- kernel/events/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 3c75ec6..b9ed867 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6571,7 +6571,7 @@ SYSCALL_DEFINE5(perf_event_open, /* * Get the target context (task or percpu): */ - ctx = find_get_context(pmu, task, cpu); + ctx = find_get_context(pmu, task, event->cpu); if (IS_ERR(ctx)) { err = PTR_ERR(ctx); goto err_alloc; @@ -6651,16 +6651,16 @@ SYSCALL_DEFINE5(perf_event_open, mutex_lock(&ctx->mutex); if (move_group) { - perf_install_in_context(ctx, group_leader, cpu); + perf_install_in_context(ctx, group_leader, event->cpu); get_ctx(ctx); list_for_each_entry(sibling, &group_leader->sibling_list, group_entry) { - perf_install_in_context(ctx, sibling, cpu); + perf_install_in_context(ctx, sibling, event->cpu); get_ctx(ctx); } } - perf_install_in_context(ctx, event, cpu); + perf_install_in_context(ctx, event, event->cpu); ++ctx->generation; perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); -- cgit v1.1 From ab5cf14fb4432d2550896d37e77aa88c335ce9b8 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 15 Jun 2012 14:31:33 +0800 Subject: BACKPORT: perf: Introduce perf_pmu_migrate_context() Originally from Peter Zijlstra. The helper migrates perf events from one cpu to another cpu. Conflicts (perf: Fix race in removing an event): kernel/events/core.c Change-Id: I7885fe36c9e2803b10477d556163197085be3d19 Signed-off-by: Zheng Yan Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1339741902-8449-5-git-send-email-zheng.z.yan@intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 2 ++ kernel/events/core.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index e4d3640..42dc246 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -979,6 +979,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, struct task_struct *task, perf_overflow_handler_t callback); +extern void perf_pmu_migrate_context(struct pmu *pmu, + int src_cpu, int dst_cpu); extern u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running); diff --git a/kernel/events/core.c b/kernel/events/core.c index b9ed867..9d57031 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1580,6 +1580,8 @@ perf_install_in_context(struct perf_event_context *ctx, lockdep_assert_held(&ctx->mutex); event->ctx = ctx; + if (event->cpu != -1) + event->cpu = cpu; if (!task) { /* @@ -6651,6 +6653,7 @@ SYSCALL_DEFINE5(perf_event_open, mutex_lock(&ctx->mutex); if (move_group) { + synchronize_rcu(); perf_install_in_context(ctx, group_leader, event->cpu); get_ctx(ctx); list_for_each_entry(sibling, &group_leader->sibling_list, @@ -6750,6 +6753,39 @@ err: } EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); +void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) +{ + struct perf_event_context *src_ctx; + struct perf_event_context *dst_ctx; + struct perf_event *event, *tmp; + LIST_HEAD(events); + + src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; + dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; + + mutex_lock(&src_ctx->mutex); + list_for_each_entry_safe(event, tmp, &src_ctx->event_list, + event_entry) { + perf_remove_from_context(event, true); + put_ctx(src_ctx); + list_add(&event->event_entry, &events); + } + mutex_unlock(&src_ctx->mutex); + + synchronize_rcu(); + + mutex_lock(&dst_ctx->mutex); + list_for_each_entry_safe(event, tmp, &events, event_entry) { + list_del(&event->event_entry); + if (event->state >= PERF_EVENT_STATE_OFF) + event->state = PERF_EVENT_STATE_INACTIVE; + perf_install_in_context(dst_ctx, event, dst_cpu); + get_ctx(dst_ctx); + } + mutex_unlock(&dst_ctx->mutex); +} +EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); + static void sync_child_event(struct perf_event *child_event, struct task_struct *child) { -- cgit v1.1 From 699bf014212c71ebdb99850682947e8ad19a7499 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 24 Jan 2013 21:53:17 +0100 Subject: BACKPORT: lockdep: Silence warning if CONFIG_LOCKDEP isn't set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit c9a4962881929df7f1ef6e63e1b9da304faca4dd ("nfsd: make client_lock per net") compiling nfs4state.o without CONFIG_LOCKDEP set, triggers this GCC warning: fs/nfsd/nfs4state.c: In function ‘free_client’: fs/nfsd/nfs4state.c:1051:19: warning: unused variable ‘nn’ [-Wunused-variable] The cause of that warning is that lockdep_assert_held() compiles away if CONFIG_LOCKDEP is not set. Silence this warning by using the argument to lockdep_assert_held() as a nop if CONFIG_LOCKDEP is not set. Signed-off-by: Paul Bolle Cc: Peter Zijlstra Cc: Stanislav Kinsbursky Cc: J. Bruce Fields Link: http://lkml.kernel.org/r/1359060797.1325.33.camel@x61.thuisdomein Signed-off-by: Ingo Molnar -- include/linux/lockdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Change-Id: I4a4e78fd92dccffe5fc7c3a2617ef7d4cf59f738 --- include/linux/lockdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index ef820a3..ee4410a 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -390,7 +390,7 @@ struct lock_class_key { }; #define lockdep_depth(tsk) (0) -#define lockdep_assert_held(l) do { } while (0) +#define lockdep_assert_held(l) do { (void)(l); } while (0) #endif /* !LOCKDEP */ -- cgit v1.1 From 89634e6f964064a223fb81ddbce9a053a4f16686 Mon Sep 17 00:00:00 2001 From: Ariel Yin Date: Wed, 12 Oct 2016 17:48:19 -0700 Subject: BACKPORT: perf: Fix event->ctx locking There have been a few reported issues wrt. the lack of locking around changing event->ctx. This patch tries to address those. It avoids the whole rwsem thing; and while it appears to work, please give it some thought in review. What I did fail at is sensible runtime checks on the use of event->ctx, the RCU use makes it very hard. Signed-off-by: Peter Zijlstra (Intel) Cc: Paul E. McKenney Cc: Jiri Olsa Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20150123125834.209535886@infradead.org Signed-off-by: Ingo Molnar (cherry picked from commit f63a8daa5812afef4f06c962351687e1ff9ccb2b) Bug: 30955111 Bug: 31095224 Change-Id: I5bab713034e960fad467637e98e914440de5666d --- kernel/events/core.c | 241 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 206 insertions(+), 35 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 9d57031..5dba2cb 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -604,6 +604,77 @@ static void put_ctx(struct perf_event_context *ctx) } } +/* + * Because of perf_event::ctx migration in sys_perf_event_open::move_group and + * perf_pmu_migrate_context() we need some magic. + * + * Those places that change perf_event::ctx will hold both + * perf_event_ctx::mutex of the 'old' and 'new' ctx value. + * + * Lock ordering is by mutex address. There is one other site where + * perf_event_context::mutex nests and that is put_event(). But remember that + * that is a parent<->child context relation, and migration does not affect + * children, therefore these two orderings should not interact. + * + * The change in perf_event::ctx does not affect children (as claimed above) + * because the sys_perf_event_open() case will install a new event and break + * the ctx parent<->child relation, and perf_pmu_migrate_context() is only + * concerned with cpuctx and that doesn't have children. + * + * The places that change perf_event::ctx will issue: + * + * perf_remove_from_context(); + * synchronize_rcu(); + * perf_install_in_context(); + * + * to affect the change. The remove_from_context() + synchronize_rcu() should + * quiesce the event, after which we can install it in the new location. This + * means that only external vectors (perf_fops, prctl) can perturb the event + * while in transit. Therefore all such accessors should also acquire + * perf_event_context::mutex to serialize against this. + * + * However; because event->ctx can change while we're waiting to acquire + * ctx->mutex we must be careful and use the below perf_event_ctx_lock() + * function. + * + * Lock order: + * task_struct::perf_event_mutex + * perf_event_context::mutex + * perf_event_context::lock + * perf_event::child_mutex; + * perf_event::mmap_mutex + * mmap_sem + */ +static struct perf_event_context *perf_event_ctx_lock(struct perf_event *event) +{ + struct perf_event_context *ctx; + +again: + rcu_read_lock(); + ctx = ACCESS_ONCE(event->ctx); + if (!atomic_inc_not_zero(&ctx->refcount)) { + rcu_read_unlock(); + goto again; + } + rcu_read_unlock(); + + mutex_lock(&ctx->mutex); + if (event->ctx != ctx) { + mutex_unlock(&ctx->mutex); + put_ctx(ctx); + goto again; + } + + return ctx; +} + +static void perf_event_ctx_unlock(struct perf_event *event, + struct perf_event_context *ctx) +{ + mutex_unlock(&ctx->mutex); + put_ctx(ctx); +} + static void unclone_ctx(struct perf_event_context *ctx) { if (ctx->parent_ctx) { @@ -1245,7 +1316,7 @@ static int __perf_event_disable(void *info) * is the current context on this CPU and preemption is disabled, * hence we can't get into perf_event_task_sched_out for this context. */ -void perf_event_disable(struct perf_event *event) +static void _perf_event_disable(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; struct task_struct *task = ctx->task; @@ -1287,6 +1358,19 @@ retry: raw_spin_unlock_irq(&ctx->lock); } +/* + * Strictly speaking kernel users cannot create groups and therefore this + * interface does not need the perf_event_ctx_lock() magic. + */ +void perf_event_disable(struct perf_event *event) +{ + struct perf_event_context *ctx; + + ctx = perf_event_ctx_lock(event); + _perf_event_disable(event); + perf_event_ctx_unlock(event, ctx); +} + static void perf_set_shadow_time(struct perf_event *event, struct perf_event_context *ctx, u64 tstamp) @@ -1722,7 +1806,7 @@ unlock: * perf_event_for_each_child or perf_event_for_each as described * for perf_event_disable. */ -void perf_event_enable(struct perf_event *event) +static void _perf_event_enable(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; struct task_struct *task = ctx->task; @@ -1779,7 +1863,19 @@ out: raw_spin_unlock_irq(&ctx->lock); } -static int perf_event_refresh(struct perf_event *event, int refresh) +/* + * See perf_event_disable(); + */ +void perf_event_enable(struct perf_event *event) +{ + struct perf_event_context *ctx; + + ctx = perf_event_ctx_lock(event); + _perf_event_enable(event); + perf_event_ctx_unlock(event, ctx); +} + +static int _perf_event_refresh(struct perf_event *event, int refresh) { /* * not supported on inherited events @@ -1788,11 +1884,26 @@ static int perf_event_refresh(struct perf_event *event, int refresh) return -EINVAL; atomic_add(refresh, &event->event_limit); - perf_event_enable(event); + _perf_event_enable(event); return 0; } +/* + * See perf_event_disable() + */ +int perf_event_refresh(struct perf_event *event, int refresh) +{ + struct perf_event_context *ctx; + int ret; + + ctx = perf_event_ctx_lock(event); + ret = _perf_event_refresh(event, refresh); + perf_event_ctx_unlock(event, ctx); + + return ret; +} + static void ctx_sched_out(struct perf_event_context *ctx, struct perf_cpu_context *cpuctx, enum event_type_t event_type) @@ -3036,7 +3147,16 @@ static void put_event(struct perf_event *event) rcu_read_unlock(); if (owner) { - mutex_lock(&owner->perf_event_mutex); + /* + * If we're here through perf_event_exit_task() we're already + * holding ctx->mutex which would be an inversion wrt. the + * normal lock order. + * + * However we can safely take this lock because its the child + * ctx->mutex. + */ + mutex_lock_nested(&owner->perf_event_mutex, SINGLE_DEPTH_NESTING); + /* * We have to re-check the event->owner field, if it is cleared * we raced with perf_event_exit_task(), acquiring the mutex @@ -3088,12 +3208,13 @@ static int perf_event_read_group(struct perf_event *event, u64 read_format, char __user *buf) { struct perf_event *leader = event->group_leader, *sub; - int n = 0, size = 0, ret = -EFAULT; struct perf_event_context *ctx = leader->ctx; - u64 values[5]; + int n = 0, size = 0, ret; u64 count, enabled, running; + u64 values[5]; + + lockdep_assert_held(&ctx->mutex); - mutex_lock(&ctx->mutex); count = perf_event_read_value(leader, &enabled, &running); values[n++] = 1 + leader->nr_siblings; @@ -3108,7 +3229,7 @@ static int perf_event_read_group(struct perf_event *event, size = n * sizeof(u64); if (copy_to_user(buf, values, size)) - goto unlock; + return -EFAULT; ret = size; @@ -3122,14 +3243,12 @@ static int perf_event_read_group(struct perf_event *event, size = n * sizeof(u64); if (copy_to_user(buf + ret, values, size)) { - ret = -EFAULT; - goto unlock; + return -EFAULT; } ret += size; } -unlock: - mutex_unlock(&ctx->mutex); + return ret; } @@ -3188,8 +3307,14 @@ static ssize_t perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct perf_event *event = file->private_data; + struct perf_event_context *ctx; + int ret; + + ctx = perf_event_ctx_lock(event); + ret = perf_read_hw(event, buf, count); + perf_event_ctx_unlock(event, ctx); - return perf_read_hw(event, buf, count); + return ret; } static unsigned int perf_poll(struct file *file, poll_table *wait) @@ -3209,7 +3334,7 @@ static unsigned int perf_poll(struct file *file, poll_table *wait) return events; } -static void perf_event_reset(struct perf_event *event) +static void _perf_event_reset(struct perf_event *event) { (void)perf_event_read(event); local64_set(&event->count, 0); @@ -3308,25 +3433,24 @@ static int perf_event_set_output(struct perf_event *event, struct perf_event *output_event); static int perf_event_set_filter(struct perf_event *event, void __user *arg); -static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned long arg) { - struct perf_event *event = file->private_data; void (*func)(struct perf_event *); u32 flags = arg; switch (cmd) { case PERF_EVENT_IOC_ENABLE: - func = perf_event_enable; + func = _perf_event_enable; break; case PERF_EVENT_IOC_DISABLE: - func = perf_event_disable; + func = _perf_event_disable; break; case PERF_EVENT_IOC_RESET: - func = perf_event_reset; + func = _perf_event_reset; break; case PERF_EVENT_IOC_REFRESH: - return perf_event_refresh(event, arg); + return _perf_event_refresh(event, arg); case PERF_EVENT_IOC_PERIOD: return perf_event_period(event, (u64 __user *)arg); @@ -3367,13 +3491,30 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; } +static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct perf_event *event = file->private_data; + struct perf_event_context *ctx; + long ret; + + ctx = perf_event_ctx_lock(event); + ret = _perf_ioctl(event, cmd, arg); + perf_event_ctx_unlock(event, ctx); + + return ret; +} + int perf_event_task_enable(void) { + struct perf_event_context *ctx; struct perf_event *event; mutex_lock(¤t->perf_event_mutex); - list_for_each_entry(event, ¤t->perf_event_list, owner_entry) - perf_event_for_each_child(event, perf_event_enable); + list_for_each_entry(event, ¤t->perf_event_list, owner_entry) { + ctx = perf_event_ctx_lock(event); + perf_event_for_each_child(event, _perf_event_enable); + perf_event_ctx_unlock(event, ctx); + } mutex_unlock(¤t->perf_event_mutex); return 0; @@ -3381,11 +3522,15 @@ int perf_event_task_enable(void) int perf_event_task_disable(void) { + struct perf_event_context *ctx; struct perf_event *event; mutex_lock(¤t->perf_event_mutex); - list_for_each_entry(event, ¤t->perf_event_list, owner_entry) - perf_event_for_each_child(event, perf_event_disable); + list_for_each_entry(event, ¤t->perf_event_list, owner_entry) { + ctx = perf_event_ctx_lock(event); + perf_event_for_each_child(event, _perf_event_disable); + perf_event_ctx_unlock(event, ctx); + } mutex_unlock(¤t->perf_event_mutex); return 0; @@ -6445,6 +6590,15 @@ out: return ret; } +static void mutex_lock_double(struct mutex *a, struct mutex *b) +{ + if (b < a) + swap(a, b); + + mutex_lock(a); + mutex_lock_nested(b, SINGLE_DEPTH_NESTING); +} + /** * sys_perf_event_open - open a performance event, associate it to a task/cpu * @@ -6460,7 +6614,7 @@ SYSCALL_DEFINE5(perf_event_open, struct perf_event *group_leader = NULL, *output_event = NULL; struct perf_event *event, *sibling; struct perf_event_attr attr; - struct perf_event_context *ctx; + struct perf_event_context *ctx, *uninitialized_var(gctx); struct file *event_file = NULL; struct file *group_file = NULL; struct task_struct *task = NULL; @@ -6628,9 +6782,14 @@ SYSCALL_DEFINE5(perf_event_open, } if (move_group) { - struct perf_event_context *gctx = group_leader->ctx; + gctx = group_leader->ctx; + + /* + * See perf_event_ctx_lock() for comments on the details + * of swizzling perf_event::ctx. + */ + mutex_lock_double(&gctx->mutex, &ctx->mutex); - mutex_lock(&gctx->mutex); perf_remove_from_context(group_leader); /* @@ -6645,15 +6804,19 @@ SYSCALL_DEFINE5(perf_event_open, perf_event__state_init(sibling); put_ctx(gctx); } - mutex_unlock(&gctx->mutex); - put_ctx(gctx); + } else { + mutex_lock(&ctx->mutex); } WARN_ON_ONCE(ctx->parent_ctx); - mutex_lock(&ctx->mutex); if (move_group) { + /* + * Wait for everybody to stop referencing the events through + * the old lists, before installing it on new lists. + */ synchronize_rcu(); + perf_install_in_context(ctx, group_leader, event->cpu); get_ctx(ctx); list_for_each_entry(sibling, &group_leader->sibling_list, @@ -6666,6 +6829,11 @@ SYSCALL_DEFINE5(perf_event_open, perf_install_in_context(ctx, event, event->cpu); ++ctx->generation; perf_unpin_context(ctx); + + if (move_group) { + mutex_unlock(&gctx->mutex); + put_ctx(gctx); + } mutex_unlock(&ctx->mutex); event->owner = current; @@ -6763,18 +6931,20 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; - mutex_lock(&src_ctx->mutex); + /* + * See perf_event_ctx_lock() for comments on the details + * of swizzling perf_event::ctx. + */ + mutex_lock_double(&src_ctx->mutex, &dst_ctx->mutex); list_for_each_entry_safe(event, tmp, &src_ctx->event_list, event_entry) { - perf_remove_from_context(event, true); + perf_remove_from_context(event); put_ctx(src_ctx); list_add(&event->event_entry, &events); } - mutex_unlock(&src_ctx->mutex); synchronize_rcu(); - mutex_lock(&dst_ctx->mutex); list_for_each_entry_safe(event, tmp, &events, event_entry) { list_del(&event->event_entry); if (event->state >= PERF_EVENT_STATE_OFF) @@ -6783,6 +6953,7 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) get_ctx(dst_ctx); } mutex_unlock(&dst_ctx->mutex); + mutex_unlock(&src_ctx->mutex); } EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); -- cgit v1.1 From 12bf2ab391d33a49d74b123b66ef87c4ffa14e2e Mon Sep 17 00:00:00 2001 From: John Dias Date: Mon, 10 Oct 2016 14:44:30 -0700 Subject: perf: protect group_leader from races that cause ctx double-free When moving a group_leader perf event from a software-context to a hardware-context, there's a race in checking and updating that context. The existing locking solution doesn't work; note that it tries to grab a lock inside the group_leader's context object, which you can only get at by going through a pointer that should be protected from these races. To avoid that problem, and to produce a simple solution, we can just use a lock per group_leader to protect all checks on the group_leader's context. The new lock is grabbed and released when no context locks are held. RM-290 Bug: 30955111 Bug: 31095224 Change-Id: If37124c100ca6f4aa962559fba3bd5dbbec8e052 --- include/linux/perf_event.h | 6 ++++++ kernel/events/core.c | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 42dc246..82660a6 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -757,6 +757,12 @@ struct perf_event { int nr_siblings; int group_flags; struct perf_event *group_leader; + + /* + * Protect the pmu, attributes and context of a group leader. + * Note: does not protect the pointer to the group_leader. + */ + struct mutex group_leader_mutex; struct pmu *pmu; enum perf_event_active_state state; diff --git a/kernel/events/core.c b/kernel/events/core.c index 5dba2cb..466213f 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6362,6 +6362,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, if (!group_leader) group_leader = event; + mutex_init(&event->group_leader_mutex); mutex_init(&event->child_mutex); INIT_LIST_HEAD(&event->child_list); @@ -6668,6 +6669,16 @@ SYSCALL_DEFINE5(perf_event_open, group_leader = NULL; } + /* + * Take the group_leader's group_leader_mutex before observing + * anything in the group leader that leads to changes in ctx, + * many of which may be changing on another thread. + * In particular, we want to take this lock before deciding + * whether we need to move_group. + */ + if (group_leader) + mutex_lock(&group_leader->group_leader_mutex); + if (pid != -1 && !(flags & PERF_FLAG_PID_CGROUP)) { task = find_lively_task_by_vpid(pid); if (IS_ERR(task)) { @@ -6836,6 +6847,9 @@ SYSCALL_DEFINE5(perf_event_open, } mutex_unlock(&ctx->mutex); + if (group_leader) + mutex_unlock(&group_leader->group_leader_mutex); + event->owner = current; mutex_lock(¤t->perf_event_mutex); @@ -6867,6 +6881,8 @@ err_task: if (task) put_task_struct(task); err_group_fd: + if (group_leader) + mutex_unlock(&group_leader->group_leader_mutex); fput_light(group_file, fput_needed); err_fd: put_unused_fd(event_fd); -- cgit v1.1 From d7764316afed610cd1ce62a6f01ae465367bbf0a Mon Sep 17 00:00:00 2001 From: Siqi Lin Date: Thu, 13 Oct 2016 16:27:46 -0700 Subject: net: ping: Fix stack buffer overflow in ping_common_sendmsg() In ping_common_sendmsg(), when len < icmph_len, memcpy_fromiovec() will access invalid memory because msg->msg_iov only has 1 element and memcpy_fromiovec() attempts to increment it. KASAN report: BUG: KASAN: stack-out-of-bounds in memcpy_fromiovec+0x60/0x114 at addr ffffffc071077da0 Read of size 8 by task trinity-c2/9623 page:ffffffbe034b9a08 count:0 mapcount:0 mapping: (null) index:0x0 flags: 0x0() page dumped because: kasan: bad access detected CPU: 0 PID: 9623 Comm: trinity-c2 Tainted: G BU 3.18.0-dirty #15 Hardware name: Google Tegra210 Smaug Rev 1,3+ (DT) Call trace: [] dump_backtrace+0x0/0x1ac arch/arm64/kernel/traps.c:90 [] show_stack+0x10/0x1c arch/arm64/kernel/traps.c:171 [< inline >] __dump_stack lib/dump_stack.c:15 [] dump_stack+0x7c/0xd0 lib/dump_stack.c:50 [< inline >] print_address_description mm/kasan/report.c:147 [< inline >] kasan_report_error mm/kasan/report.c:236 [] kasan_report+0x380/0x4b8 mm/kasan/report.c:259 [< inline >] check_memory_region mm/kasan/kasan.c:264 [] __asan_load8+0x20/0x70 mm/kasan/kasan.c:507 [] memcpy_fromiovec+0x5c/0x114 lib/iovec.c:15 [< inline >] memcpy_from_msg include/linux/skbuff.h:2667 [] ping_common_sendmsg+0x50/0x108 net/ipv4/ping.c:674 [] ping_v4_sendmsg+0xd8/0x698 net/ipv4/ping.c:714 [] inet_sendmsg+0xe0/0x12c net/ipv4/af_inet.c:749 [< inline >] __sock_sendmsg_nosec net/socket.c:624 [< inline >] __sock_sendmsg net/socket.c:632 [] sock_sendmsg+0x124/0x164 net/socket.c:643 [< inline >] SYSC_sendto net/socket.c:1797 [] SyS_sendto+0x178/0x1d8 net/socket.c:1761 Memory state around the buggy address: ffffffc071077c80: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 f1 f1 ffffffc071077d00: f1 f1 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2 >ffffffc071077d80: f2 f2 00 00 f4 f4 f2 f2 f2 f2 00 00 00 00 00 00 ^ ffffffc071077e00: 00 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00 ffffffc071077e80: 00 00 00 00 00 00 f3 f3 f3 f3 00 00 00 00 00 00 RM-290 Bug: 31349935 Change-Id: Ib7385fc26dfe7e07e9bab42a10ff65a37cbaab54 Signed-off-by: Siqi Lin --- net/ipv4/ping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 93fbd72..02743c2 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -641,7 +641,7 @@ int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, void *user_icmph, size_t icmph_len) { u8 type, code; - if (len > 0xFFFF) + if (len > 0xFFFF || len < icmph_len) return -EMSGSIZE; /* -- cgit v1.1 From b4331df968b885b4df986753ae2361f47d09589c Mon Sep 17 00:00:00 2001 From: Pawan Kumar Date: Thu, 31 Oct 2013 16:19:44 +0530 Subject: fbmem: Check failure of FBIOPUTCMAP ioctl On FBIOPUTCMAP ioctl failure deallocate fb cmap. Put null check for cmap red, green, blue component. Change-Id: I10468ee30d0e76c256cf3d7a6ffe14db7fd4511b Signed-off-by: Pawan Kumar --- drivers/video/fbcmap.c | 30 +++++++++++++++++++++--------- drivers/video/fbmem.c | 4 ++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index 5c3960d..31e93a5 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -166,6 +166,9 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) int tooff = 0, fromoff = 0; int size; + if (!to || !from) + return -EINVAL; + if (to->start > from->start) fromoff = to->start - from->start; else @@ -177,9 +180,12 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) return -EINVAL; size *= sizeof(u16); - memcpy(to->red+tooff, from->red+fromoff, size); - memcpy(to->green+tooff, from->green+fromoff, size); - memcpy(to->blue+tooff, from->blue+fromoff, size); + if (from->red && to->red) + memcpy(to->red+tooff, from->red+fromoff, size); + if (from->green && to->green) + memcpy(to->green+tooff, from->green+fromoff, size); + if (from->blue && to->blue) + memcpy(to->blue+tooff, from->blue+fromoff, size); if (from->transp && to->transp) memcpy(to->transp+tooff, from->transp+fromoff, size); return 0; @@ -190,6 +196,9 @@ int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) int tooff = 0, fromoff = 0; int size; + if (!to || !from) + return -EINVAL; + if (to->start > from->start) fromoff = to->start - from->start; else @@ -201,12 +210,15 @@ int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) return -EINVAL; size *= sizeof(u16); - if (copy_to_user(to->red+tooff, from->red+fromoff, size)) - return -EFAULT; - if (copy_to_user(to->green+tooff, from->green+fromoff, size)) - return -EFAULT; - if (copy_to_user(to->blue+tooff, from->blue+fromoff, size)) - return -EFAULT; + if (from->red && to->red) + if (copy_to_user(to->red+tooff, from->red+fromoff, size)) + return -EFAULT; + if (from->green && to->green) + if (copy_to_user(to->green+tooff, from->green+fromoff, size)) + return -EFAULT; + if (from->blue && to->blue) + if (copy_to_user(to->blue+tooff, from->blue+fromoff, size)) + return -EFAULT; if (from->transp && to->transp) if (copy_to_user(to->transp+tooff, from->transp+fromoff, size)) return -EFAULT; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index fcc52d4..4716e62 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1108,6 +1108,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, if (copy_from_user(&cmap, argp, sizeof(cmap))) return -EFAULT; ret = fb_set_user_cmap(&cmap, info); + if (ret) { + if (info) + fb_dealloc_cmap(&info->cmap); + } break; case FBIOGETCMAP: if (copy_from_user(&cmap, argp, sizeof(cmap))) -- cgit v1.1 From f78455200fe1885ee2a9b1a1ae7176a51ff77aa9 Mon Sep 17 00:00:00 2001 From: Ping Li Date: Wed, 25 Feb 2015 18:39:36 -0800 Subject: fbcmap: Remove unnecessary condition check In fb_set_user_cmap function, cmap->start variable is an unsigned integer, it doesn't need a condition check with the sign. Change-Id: I355ddb7edcc085ee52e4054833b687640376eee3 Signed-off-by: Ping Li --- drivers/video/fbcmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index 31e93a5..f67cdcd 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -297,8 +297,8 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info) rc = -ENODEV; goto out; } - if (cmap->start < 0 || (!info->fbops->fb_setcolreg && - !info->fbops->fb_setcmap)) { + if (!info->fbops->fb_setcolreg && + !info->fbops->fb_setcmap) { rc = -EINVAL; goto out1; } -- cgit v1.1 From dda53dd9966fb1cd781f6aa12d866d777da0a5df Mon Sep 17 00:00:00 2001 From: Steve Pfetsch Date: Fri, 14 Oct 2016 15:36:59 -0700 Subject: drivers: video: Add bounds checking in fb_cmap_to_user Verify that unsigned int value will not become negative before cast to signed int. Bug: 31651010 Change-Id: I548a200f678762042617f11100b6966a405a3920 --- drivers/video/fbcmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index f67cdcd..b1d63b5 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -196,7 +196,7 @@ int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) int tooff = 0, fromoff = 0; int size; - if (!to || !from) + if (!to || !from || (int)(to->start) < 0) return -EINVAL; if (to->start > from->start) -- cgit v1.1 From a04b065c010280ed1806c73cb234a2bf657a5ce9 Mon Sep 17 00:00:00 2001 From: Min Chong Date: Thu, 13 Oct 2016 17:15:35 -0700 Subject: netfilter: Change %p to %pK in debug messages The format specifier %p can leak kernel addresses while not valuing the kptr_restrict system settings. Use %pK instead of %p, which also evaluates whether kptr_restrict is set. Bug: 31796940 Change-Id: Ia2946d6b493126d68281f97778faf578247f088e Signed-off-by: Min Chong --- net/netfilter/nf_conntrack_core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 3b44d0f..b4da958 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(nf_ct_invert_tuple); static void clean_from_lists(struct nf_conn *ct) { - pr_debug("clean_from_lists(%p)\n", ct); + pr_debug("clean_from_lists(%pK)\n", ct); hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode); @@ -192,7 +192,7 @@ destroy_conntrack(struct nf_conntrack *nfct) struct net *net = nf_ct_net(ct); struct nf_conntrack_l4proto *l4proto; - pr_debug("destroy_conntrack(%p)\n", ct); + pr_debug("destroy_conntrack(%pK)\n", ct); NF_CT_ASSERT(atomic_read(&nfct->use) == 0); NF_CT_ASSERT(!timer_pending(&ct->timeout)); @@ -225,7 +225,7 @@ destroy_conntrack(struct nf_conntrack *nfct) if (ct->master) nf_ct_put(ct->master); - pr_debug("destroy_conntrack: returning ct=%p to slab\n", ct); + pr_debug("destroy_conntrack: returning ct=%pK to slab\n", ct); nf_conntrack_free(ct); } @@ -470,7 +470,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) /* No external references means no one else could have confirmed us. */ NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); - pr_debug("Confirming conntrack %p\n", ct); + pr_debug("Confirming conntrack %pK\n", ct); spin_lock_bh(&nf_conntrack_lock); @@ -786,7 +786,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, spin_lock_bh(&nf_conntrack_lock); exp = nf_ct_find_expectation(net, zone, tuple); if (exp) { - pr_debug("conntrack: expectation arrives ct=%p exp=%p\n", + pr_debug("conntrack: expectation arrives ct=%pK exp=%pK\n", ct, exp); /* Welcome, Mr. Bond. We've been expecting you... */ __set_bit(IPS_EXPECTED_BIT, &ct->status); @@ -871,14 +871,14 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, } else { /* Once we've had two way comms, always ESTABLISHED. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { - pr_debug("nf_conntrack_in: normal packet for %p\n", ct); + pr_debug("nf_conntrack_in: normal packet for %pK\n", ct); *ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { - pr_debug("nf_conntrack_in: related packet for %p\n", + pr_debug("nf_conntrack_in: related packet for %pK\n", ct); *ctinfo = IP_CT_RELATED; } else { - pr_debug("nf_conntrack_in: new packet for %p\n", ct); + pr_debug("nf_conntrack_in: new packet for %pK\n", ct); *ctinfo = IP_CT_NEW; } *set_reply = 0; @@ -1016,7 +1016,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, /* Should be unconfirmed, so not in hash table yet */ NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); - pr_debug("Altering reply tuple of %p to ", ct); + pr_debug("Altering reply tuple of %pK to ", ct); nf_ct_dump_tuple(newreply); ct->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; @@ -1516,7 +1516,7 @@ static int nf_conntrack_init_net(struct net *net) goto err_stat; } - net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net); + net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%pK", net); if (!net->ct.slabname) { ret = -ENOMEM; goto err_slabname; -- cgit v1.1