diff options
author | Eric Lapuyade <eric.lapuyade@linux.intel.com> | 2013-04-29 11:13:27 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-06-13 18:26:08 -0400 |
commit | 9674da8759df0d6c0d24e1ede6e2a1acdef91e3c (patch) | |
tree | 97363dfcf38f1e7c6bfb8a9abd76302c617eb705 | |
parent | 1095e69f47926db6f1350a9d6a38626521580e87 (diff) |
NFC: Add firmware upload netlink command
As several NFC chipsets can have their firmwares upgraded and
reflashed, this patchset adds a new netlink command to trigger
that the driver loads or flashes a new firmware. This will allows
userspace triggered firmware upgrade through netlink.
The firmware name or hint is passed as a parameter, and the driver
will eventually fetch the firmware binary through the request_firmware
API.
The cmd can only be executed when the nfc dev is not in use. Actual
firmware loading/flashing is an asynchronous operation. Result of the
operation shall send a new event up to user space through the nfc dev
multicast socket. During operation, the nfc dev is not openable and
thus not usable.
Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r-- | include/net/nfc/nfc.h | 2 | ||||
-rw-r--r-- | include/uapi/linux/nfc.h | 6 | ||||
-rw-r--r-- | net/nfc/core.c | 46 | ||||
-rw-r--r-- | net/nfc/netlink.c | 63 | ||||
-rw-r--r-- | net/nfc/nfc.h | 5 |
5 files changed, 122 insertions, 0 deletions
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 5eb80bb3cbb2..3563dbdcaaf2 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h | |||
@@ -70,6 +70,7 @@ struct nfc_ops { | |||
70 | int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target); | 70 | int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target); |
71 | int (*enable_se)(struct nfc_dev *dev, u32 secure_element); | 71 | int (*enable_se)(struct nfc_dev *dev, u32 secure_element); |
72 | int (*disable_se)(struct nfc_dev *dev, u32 secure_element); | 72 | int (*disable_se)(struct nfc_dev *dev, u32 secure_element); |
73 | int (*fw_upload)(struct nfc_dev *dev, const char *firmware_name); | ||
73 | }; | 74 | }; |
74 | 75 | ||
75 | #define NFC_TARGET_IDX_ANY -1 | 76 | #define NFC_TARGET_IDX_ANY -1 |
@@ -104,6 +105,7 @@ struct nfc_dev { | |||
104 | int targets_generation; | 105 | int targets_generation; |
105 | struct device dev; | 106 | struct device dev; |
106 | bool dev_up; | 107 | bool dev_up; |
108 | bool fw_upload_in_progress; | ||
107 | u8 rf_mode; | 109 | u8 rf_mode; |
108 | bool polling; | 110 | bool polling; |
109 | struct nfc_target *active_target; | 111 | struct nfc_target *active_target; |
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 7c6f627a717d..b6cbd164f146 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h | |||
@@ -69,6 +69,8 @@ | |||
69 | * starting a poll from a device which has a secure element enabled means | 69 | * starting a poll from a device which has a secure element enabled means |
70 | * we want to do SE based card emulation. | 70 | * we want to do SE based card emulation. |
71 | * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element. | 71 | * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element. |
72 | * @NFC_CMD_FW_UPLOAD: Request to Load/flash firmware, or event to inform that | ||
73 | * some firmware was loaded | ||
72 | */ | 74 | */ |
73 | enum nfc_commands { | 75 | enum nfc_commands { |
74 | NFC_CMD_UNSPEC, | 76 | NFC_CMD_UNSPEC, |
@@ -92,6 +94,7 @@ enum nfc_commands { | |||
92 | NFC_CMD_DISABLE_SE, | 94 | NFC_CMD_DISABLE_SE, |
93 | NFC_CMD_LLC_SDREQ, | 95 | NFC_CMD_LLC_SDREQ, |
94 | NFC_EVENT_LLC_SDRES, | 96 | NFC_EVENT_LLC_SDRES, |
97 | NFC_CMD_FW_UPLOAD, | ||
95 | /* private: internal use only */ | 98 | /* private: internal use only */ |
96 | __NFC_CMD_AFTER_LAST | 99 | __NFC_CMD_AFTER_LAST |
97 | }; | 100 | }; |
@@ -121,6 +124,7 @@ enum nfc_commands { | |||
121 | * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter | 124 | * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter |
122 | * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter | 125 | * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter |
123 | * @NFC_ATTR_SE: Available Secure Elements | 126 | * @NFC_ATTR_SE: Available Secure Elements |
127 | * @NFC_ATTR_FIRMWARE_NAME: Free format firmware version | ||
124 | */ | 128 | */ |
125 | enum nfc_attrs { | 129 | enum nfc_attrs { |
126 | NFC_ATTR_UNSPEC, | 130 | NFC_ATTR_UNSPEC, |
@@ -143,6 +147,7 @@ enum nfc_attrs { | |||
143 | NFC_ATTR_LLC_PARAM_MIUX, | 147 | NFC_ATTR_LLC_PARAM_MIUX, |
144 | NFC_ATTR_SE, | 148 | NFC_ATTR_SE, |
145 | NFC_ATTR_LLC_SDP, | 149 | NFC_ATTR_LLC_SDP, |
150 | NFC_ATTR_FIRMWARE_NAME, | ||
146 | /* private: internal use only */ | 151 | /* private: internal use only */ |
147 | __NFC_ATTR_AFTER_LAST | 152 | __NFC_ATTR_AFTER_LAST |
148 | }; | 153 | }; |
@@ -162,6 +167,7 @@ enum nfc_sdp_attr { | |||
162 | #define NFC_SENSB_RES_MAXSIZE 12 | 167 | #define NFC_SENSB_RES_MAXSIZE 12 |
163 | #define NFC_SENSF_RES_MAXSIZE 18 | 168 | #define NFC_SENSF_RES_MAXSIZE 18 |
164 | #define NFC_GB_MAXSIZE 48 | 169 | #define NFC_GB_MAXSIZE 48 |
170 | #define NFC_FIRMWARE_NAME_MAXSIZE 32 | ||
165 | 171 | ||
166 | /* NFC protocols */ | 172 | /* NFC protocols */ |
167 | #define NFC_PROTO_JEWEL 1 | 173 | #define NFC_PROTO_JEWEL 1 |
diff --git a/net/nfc/core.c b/net/nfc/core.c index 40d2527693da..eb3cecf1764e 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c | |||
@@ -44,6 +44,47 @@ DEFINE_MUTEX(nfc_devlist_mutex); | |||
44 | /* NFC device ID bitmap */ | 44 | /* NFC device ID bitmap */ |
45 | static DEFINE_IDA(nfc_index_ida); | 45 | static DEFINE_IDA(nfc_index_ida); |
46 | 46 | ||
47 | int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name) | ||
48 | { | ||
49 | int rc = 0; | ||
50 | |||
51 | pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name); | ||
52 | |||
53 | device_lock(&dev->dev); | ||
54 | |||
55 | if (!device_is_registered(&dev->dev)) { | ||
56 | rc = -ENODEV; | ||
57 | goto error; | ||
58 | } | ||
59 | |||
60 | if (dev->dev_up) { | ||
61 | rc = -EBUSY; | ||
62 | goto error; | ||
63 | } | ||
64 | |||
65 | if (!dev->ops->fw_upload) { | ||
66 | rc = -EOPNOTSUPP; | ||
67 | goto error; | ||
68 | } | ||
69 | |||
70 | dev->fw_upload_in_progress = true; | ||
71 | rc = dev->ops->fw_upload(dev, firmware_name); | ||
72 | if (rc) | ||
73 | dev->fw_upload_in_progress = false; | ||
74 | |||
75 | error: | ||
76 | device_unlock(&dev->dev); | ||
77 | return rc; | ||
78 | } | ||
79 | |||
80 | int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name) | ||
81 | { | ||
82 | dev->fw_upload_in_progress = false; | ||
83 | |||
84 | return nfc_genl_fw_upload_done(dev, firmware_name); | ||
85 | } | ||
86 | EXPORT_SYMBOL(nfc_fw_upload_done); | ||
87 | |||
47 | /** | 88 | /** |
48 | * nfc_dev_up - turn on the NFC device | 89 | * nfc_dev_up - turn on the NFC device |
49 | * | 90 | * |
@@ -69,6 +110,11 @@ int nfc_dev_up(struct nfc_dev *dev) | |||
69 | goto error; | 110 | goto error; |
70 | } | 111 | } |
71 | 112 | ||
113 | if (dev->fw_upload_in_progress) { | ||
114 | rc = -EBUSY; | ||
115 | goto error; | ||
116 | } | ||
117 | |||
72 | if (dev->dev_up) { | 118 | if (dev->dev_up) { |
73 | rc = -EALREADY; | 119 | rc = -EALREADY; |
74 | goto error; | 120 | goto error; |
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index f0c4d61f37c0..1deadad9a285 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c | |||
@@ -56,6 +56,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { | |||
56 | [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 }, | 56 | [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 }, |
57 | [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 }, | 57 | [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 }, |
58 | [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED }, | 58 | [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED }, |
59 | [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING, | ||
60 | .len = NFC_FIRMWARE_NAME_MAXSIZE }, | ||
59 | }; | 61 | }; |
60 | 62 | ||
61 | static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { | 63 | static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { |
@@ -1025,6 +1027,62 @@ exit: | |||
1025 | return rc; | 1027 | return rc; |
1026 | } | 1028 | } |
1027 | 1029 | ||
1030 | static int nfc_genl_fw_upload(struct sk_buff *skb, struct genl_info *info) | ||
1031 | { | ||
1032 | struct nfc_dev *dev; | ||
1033 | int rc; | ||
1034 | u32 idx; | ||
1035 | char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; | ||
1036 | |||
1037 | if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) | ||
1038 | return -EINVAL; | ||
1039 | |||
1040 | idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); | ||
1041 | |||
1042 | dev = nfc_get_device(idx); | ||
1043 | if (!dev) | ||
1044 | return -ENODEV; | ||
1045 | |||
1046 | nla_strlcpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME], | ||
1047 | sizeof(firmware_name)); | ||
1048 | |||
1049 | rc = nfc_fw_upload(dev, firmware_name); | ||
1050 | |||
1051 | nfc_put_device(dev); | ||
1052 | return rc; | ||
1053 | } | ||
1054 | |||
1055 | int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name) | ||
1056 | { | ||
1057 | struct sk_buff *msg; | ||
1058 | void *hdr; | ||
1059 | |||
1060 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
1061 | if (!msg) | ||
1062 | return -ENOMEM; | ||
1063 | |||
1064 | hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, | ||
1065 | NFC_CMD_FW_UPLOAD); | ||
1066 | if (!hdr) | ||
1067 | goto free_msg; | ||
1068 | |||
1069 | if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) || | ||
1070 | nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) | ||
1071 | goto nla_put_failure; | ||
1072 | |||
1073 | genlmsg_end(msg, hdr); | ||
1074 | |||
1075 | genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); | ||
1076 | |||
1077 | return 0; | ||
1078 | |||
1079 | nla_put_failure: | ||
1080 | genlmsg_cancel(msg, hdr); | ||
1081 | free_msg: | ||
1082 | nlmsg_free(msg); | ||
1083 | return -EMSGSIZE; | ||
1084 | } | ||
1085 | |||
1028 | static struct genl_ops nfc_genl_ops[] = { | 1086 | static struct genl_ops nfc_genl_ops[] = { |
1029 | { | 1087 | { |
1030 | .cmd = NFC_CMD_GET_DEVICE, | 1088 | .cmd = NFC_CMD_GET_DEVICE, |
@@ -1084,6 +1142,11 @@ static struct genl_ops nfc_genl_ops[] = { | |||
1084 | .doit = nfc_genl_llc_sdreq, | 1142 | .doit = nfc_genl_llc_sdreq, |
1085 | .policy = nfc_genl_policy, | 1143 | .policy = nfc_genl_policy, |
1086 | }, | 1144 | }, |
1145 | { | ||
1146 | .cmd = NFC_CMD_FW_UPLOAD, | ||
1147 | .doit = nfc_genl_fw_upload, | ||
1148 | .policy = nfc_genl_policy, | ||
1149 | }, | ||
1087 | }; | 1150 | }; |
1088 | 1151 | ||
1089 | 1152 | ||
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index afa1f84ba040..cf0c48165996 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h | |||
@@ -120,6 +120,11 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter) | |||
120 | class_dev_iter_exit(iter); | 120 | class_dev_iter_exit(iter); |
121 | } | 121 | } |
122 | 122 | ||
123 | int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name); | ||
124 | int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name); | ||
125 | |||
126 | int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name); | ||
127 | |||
123 | int nfc_dev_up(struct nfc_dev *dev); | 128 | int nfc_dev_up(struct nfc_dev *dev); |
124 | 129 | ||
125 | int nfc_dev_down(struct nfc_dev *dev); | 130 | int nfc_dev_down(struct nfc_dev *dev); |