IgH EtherCAT Master  1.5.2
sii_firmware.c
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * $Id$
4  *
5  * Copyright (C) 2016 Gavin Lambert
6  *
7  * This file is part of the IgH EtherCAT Master.
8  *
9  * The IgH EtherCAT Master is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License version 2, as
11  * published by the Free Software Foundation.
12  *
13  * The IgH EtherCAT Master is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with the IgH EtherCAT Master; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21  *
22  * ---
23  *
24  * The license mentioned above concerns the source code only. Using the
25  * EtherCAT technology and brand is only permitted in compliance with the
26  * industrial property and similar rights of Beckhoff Automation GmbH.
27  *
28  *****************************************************************************/
29 
35 /*****************************************************************************/
36 
37 #include <linux/version.h>
38 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
39 #include <linux/export.h>
40 #endif
41 #include <linux/fs.h>
42 #include <linux/file.h>
43 #include <linux/firmware.h>
44 #include <asm/processor.h>
45 
46 #include "master.h"
47 #include "slave.h"
48 
49 #include "sii_firmware.h"
50 
51 /*****************************************************************************/
52 #ifdef EC_SII_OVERRIDE
53 
54 /*****************************************************************************/
55 #ifdef EC_SII_DIR
56 
61 static int request_firmware_direct(
62  ec_slave_t *slave,
63  const char *filename,
64  struct firmware **out_firmware
65  )
66 {
67  // firmware files must be readable but not
68  // executable, a directory, or sticky
69  const int permreqd = S_IROTH;
70  const int permforbid = S_IFDIR | S_ISVTX | S_IXOTH | S_IXGRP | S_IXUSR;
71 
72  int retval = 0;
73  struct file *filp;
74  struct firmware *firmware;
75  umode_t permission;
76  mm_segment_t old_fs;
77  loff_t pos;
78  char pathname[strlen(filename) + sizeof(EC_SII_DIR)];
79 
80  if (filename == NULL)
81  return -EFAULT;
82  if (strlen(filename) + 14 >= 256) // Sanity check.
83  return -EFAULT;
84 
85  EC_SLAVE_DBG(slave, 1, "request_firmware_direct: %s.\n", filename);
86  sprintf(pathname, EC_SII_DIR "/%s", filename);
87 
88  // does the file exist?
89  filp = filp_open(pathname, 0, O_RDONLY);
90  if ((IS_ERR(filp)) || (filp == NULL) || (filp->f_dentry == NULL)) {
91  retval = -ENOENT;
92  goto out;
93  }
94 
95  // must have correct permissions
96  permission = filp->f_dentry->d_inode->i_mode;
97  if ((permission & permreqd) != permreqd) {
98  EC_SLAVE_WARN(slave, "Firmware %s not readable.\n", filename);
99  retval = -EPERM;
100  goto error_file;
101  }
102  if ((permission & permforbid) != 0) {
103  EC_SLAVE_WARN(slave, "Firmware %s incorrect perms.\n", filename);
104  retval = -EPERM;
105  goto error_file;
106  }
107 
108  *out_firmware = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
109  if (!firmware) {
110  EC_SLAVE_ERR(slave, "Failed to allocate memory (struct firmware).\n");
111  retval = -ENOMEM;
112  goto error_file;
113  }
114  firmware->size = filp->f_dentry->d_inode->i_size;
115 
116  if (!(firmware->data = kmalloc(firmware->size, GFP_KERNEL))) {
117  EC_SLAVE_ERR(slave, "Failed to allocate memory (firmware data).\n");
118  retval = -ENOMEM;
119  goto error_firmware;
120  }
121 
122  // read the firmware (need to temporarily allow access to kernel mem)
123  old_fs = get_fs();
124  set_fs(KERNEL_DS);
125 
126  pos = 0;
127  while (pos < firmware->size) {
128  retval = vfs_read(filp, (char __user *) firmware->data + pos,
129  firmware->size - pos, &pos);
130  if (retval < 0) {
131  set_fs(old_fs);
132  EC_SLAVE_ERR(slave, "Failed to read firmware (%d).\n", retval);
133  goto error_firmware_data;
134  }
135  }
136 
137  set_fs(old_fs);
138 
139  EC_SLAVE_INFO(slave, "SII firmware loaded from file %s.\n", filename);
140  filp_close(filp, NULL);
141  return 0;
142 
143 error_firmware_data:
144  kfree(firmware->data);
145 error_firmware:
146  kfree(firmware);
147  *out_firmware = NULL;
148 error_file:
149  filp_close(filp, NULL);
150 out:
151  return retval;
152 }
153 #endif
154 
155 /*****************************************************************************/
156 
157 struct firmware_request_context
158 {
159 #ifdef EC_SII_DIR
160  struct work_struct work;
161  const char *filename;
162 #else
163  struct device fw_device;
164  bool device_inited;
165 #endif
166  ec_slave_t *slave;
167  void (*cont)(const struct firmware *, void *);
168  void *context;
169  char filename_vendor_product[48];
170  char filename_vendor_product_revision[48];
171  bool fallback;
172 };
173 
174 static void firmware_request_complete(const struct firmware *, void *);
175 
176 /*****************************************************************************/
177 #ifdef EC_SII_DIR
178 
179 static int request_firmware_direct_work_func(void *arg)
180 {
181  struct firmware_request_context *ctx = arg;
182  struct firmware *fw = NULL;
183 
184  if (request_firmware_direct(ctx->slave, ctx->filename, &fw) == 0) {
185  firmware_request_complete(fw, ctx);
186  } else {
187  firmware_request_complete(NULL, ctx);
188  }
189  return 0;
190 }
191 
192 /*****************************************************************************/
193 
194 static int start_request(
195  struct firmware_request_context *ctx,
196  const char *filename
197  )
198 {
199  struct task_struct *task;
200 
201  ctx->filename = filename;
202  task = kthread_run(request_firmware_direct_work_func, ctx,
203  "firmware/%s", filename);
204  if (IS_ERR(task)) {
205  firmware_request_complete(NULL, ctx);
206  return PTR_ERR(task);
207  }
208  return 0;
209 }
210 
211 static void clear_request(
212  struct firmware_request_context *ctx
213  )
214 {
215  kfree(ctx);
216 }
217 
218 /*****************************************************************************/
219 #else
220 
221 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
222 #error "SII override requires kernel 2.6.33 or later; use --disable-sii-override instead."
223 #endif
224 
225 static void fw_device_release(struct device *dev)
226 {
227  struct firmware_request_context *ctx =
228  container_of(dev, struct firmware_request_context, fw_device);
229  kfree(ctx);
230 }
231 
232 static int start_request(
233  struct firmware_request_context *ctx,
234  const char *filename
235  )
236 {
237  if (!ctx->device_inited) {
238  int error;
239 
240  // register a child device for the slave, because only one
241  // firmware request can be in flight per device.
242  device_initialize(&ctx->fw_device);
243  dev_set_name(&ctx->fw_device, "addr%u", ctx->slave->station_address);
244  dev_set_uevent_suppress(&ctx->fw_device, true);
245  ctx->fw_device.parent = ctx->slave->master->class_device;
246  ctx->fw_device.release = fw_device_release;
247 
248  error = device_add(&ctx->fw_device);
249  if (error) {
250  EC_SLAVE_ERR(ctx->slave, "Unable to register child device for firmware.\n");
251  put_device(&ctx->fw_device);
252  return error;
253  }
254  ctx->device_inited = true;
255  }
256 
257  return request_firmware_nowait(THIS_MODULE, 1, filename,
258  &ctx->fw_device, GFP_KERNEL, ctx, firmware_request_complete);
259 }
260 
261 static void clear_request(
262  struct firmware_request_context *ctx
263  )
264 {
265  if (ctx->device_inited) {
266  device_del(&ctx->fw_device);
267  put_device(&ctx->fw_device);
268  // ctx freed via fw_device_release
269  } else {
270  kfree(ctx);
271  }
272 }
273 
274 #endif
275 
276 /*****************************************************************************/
277 
278 static void firmware_request_complete(
279  const struct firmware *firmware,
280  void *context
281  )
282 {
283  struct firmware_request_context *ctx = context;
284 
285  if (!firmware && !ctx->fallback) {
286  ctx->fallback = true;
287  if (start_request(ctx, ctx->filename_vendor_product) == 0) {
288  return;
289  }
290  }
291 
292  ctx->cont(firmware, ctx->context);
293  clear_request(ctx);
294 }
295 
296 /*****************************************************************************/
297 
302 void ec_request_sii_firmware(
303  ec_slave_t *slave,
304  void *context,
305  void (*cont)(const struct firmware *, void *)
306  )
307 {
308  struct firmware_request_context *ctx;
309 
310  if (!(ctx = kzalloc(sizeof(*ctx), GFP_KERNEL))) {
311  EC_SLAVE_ERR(slave, "Unable to allocate firmware request context.\n");
312  goto out_error;
313  }
314 
315  sprintf(ctx->filename_vendor_product, "ethercat/ec_%08x_%08x.bin",
316  slave->sii_image->sii.vendor_id,
317  slave->sii_image->sii.product_code);
318  sprintf(ctx->filename_vendor_product_revision, "ethercat/ec_%08x_%08x_%08x.bin",
319  slave->sii_image->sii.vendor_id,
320  slave->sii_image->sii.product_code,
321  slave->sii_image->sii.revision_number);
322  ctx->slave = slave;
323  ctx->cont = cont;
324  ctx->context = context;
325 
326  // request by vendor_product_revision first,
327  // then try more generic vendor_product
328  EC_SLAVE_DBG(slave, 1, "Trying to load SII firmware: %s\n", ctx->filename_vendor_product_revision);
329  EC_SLAVE_DBG(slave, 1, " then: %s\n", ctx->filename_vendor_product);
330 
331  if (start_request(ctx, ctx->filename_vendor_product_revision) == 0) {
332  return;
333  }
334 
335 out_error:
336  cont(NULL, context);
337 }
338 
339 /*****************************************************************************/
340 
345 void ec_release_sii_firmware(const struct firmware *firmware )
346 {
347 #ifdef EC_SII_DIR
348  if (firmware) {
349  kfree(firmware->data);
350  kfree(firmware);
351  }
352 #else
353  release_firmware(firmware);
354 #endif
355 }
356 
357 /*****************************************************************************/
358 #endif //EC_SII_OVERRIDE
359 
360 /*****************************************************************************/
uint32_t revision_number
Revision number.
Definition: slave.h:162
#define EC_SLAVE_DBG(slave, level, fmt, args...)
Convenience macro for printing slave-specific debug messages to syslog.
Definition: slave.h:108
EtherCAT slave structure.
#define EC_SLAVE_WARN(slave, fmt, args...)
Convenience macro for printing slave-specific warnings to syslog.
Definition: slave.h:91
EtherCAT master structure.
EtherCAT slave.
Definition: slave.h:214
ec_sii_image_t * sii_image
Current complete SII image.
Definition: slave.h:267
#define EC_SLAVE_ERR(slave, fmt, args...)
Convenience macro for printing slave-specific errors to syslog.
Definition: slave.h:77
#define EC_SLAVE_INFO(slave, fmt, args...)
Convenience macro for printing slave-specific information to syslog.
Definition: slave.h:63
SII "firmware" loader.
ec_sii_t sii
Extracted SII data.
Definition: slave.h:207
uint32_t product_code
Vendor-specific product code.
Definition: slave.h:161
uint32_t vendor_id
Vendor ID.
Definition: slave.h:160