Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cypress_firmware.c
Go to the documentation of this file.
1 /* cypress_firmware.c is part of the DVB USB library.
2  *
3  * Copyright (C) 2004-6 Patrick Boettcher ([email protected])
4  * see dvb-usb-init.c for copyright information.
5  *
6  * This file contains functions for downloading the firmware to Cypress FX 1
7  * and 2 based devices.
8  *
9  */
10 
11 #include "dvb_usb.h"
12 #include "cypress_firmware.h"
13 
15  u8 id;
16  const char *name; /* name of the usb controller */
17  u16 cs_reg; /* needs to be restarted,
18  * when the firmware has been downloaded */
19 };
20 
21 static const struct usb_cypress_controller cypress[] = {
22  { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 },
23  { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 },
24  { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 },
25 };
26 
27 /*
28  * load a firmware packet to the device
29  */
30 static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data,
31  u8 len)
32 {
34  0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len);
35 
36  return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
37  0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
38 }
39 
40 int usbv2_cypress_load_firmware(struct usb_device *udev,
41  const struct firmware *fw, int type)
42 {
43  struct hexline *hx;
44  int ret, pos = 0;
45 
46  hx = kmalloc(sizeof(struct hexline), GFP_KERNEL);
47  if (!hx) {
48  dev_err(&udev->dev, "%s: kmalloc() failed\n", KBUILD_MODNAME);
49  return -ENOMEM;
50  }
51 
52  /* stop the CPU */
53  hx->data[0] = 1;
54  ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
55  if (ret != 1) {
56  dev_err(&udev->dev, "%s: CPU stop failed=%d\n",
57  KBUILD_MODNAME, ret);
58  ret = -EIO;
59  goto err_kfree;
60  }
61 
62  /* write firmware to memory */
63  for (;;) {
64  ret = dvb_usbv2_get_hexline(fw, hx, &pos);
65  if (ret < 0)
66  goto err_kfree;
67  else if (ret == 0)
68  break;
69 
70  ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
71  if (ret < 0) {
72  goto err_kfree;
73  } else if (ret != hx->len) {
74  dev_err(&udev->dev, "%s: error while transferring " \
75  "firmware (transferred size=%d, " \
76  "block size=%d)\n",
77  KBUILD_MODNAME, ret, hx->len);
78  ret = -EIO;
79  goto err_kfree;
80  }
81  }
82 
83  /* start the CPU */
84  hx->data[0] = 0;
85  ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
86  if (ret != 1) {
87  dev_err(&udev->dev, "%s: CPU start failed=%d\n",
88  KBUILD_MODNAME, ret);
89  ret = -EIO;
90  goto err_kfree;
91  }
92 
93  ret = 0;
94 err_kfree:
95  kfree(hx);
96  return ret;
97 }
99 
100 int dvb_usbv2_get_hexline(const struct firmware *fw, struct hexline *hx,
101  int *pos)
102 {
103  u8 *b = (u8 *) &fw->data[*pos];
104  int data_offs = 4;
105 
106  if (*pos >= fw->size)
107  return 0;
108 
109  memset(hx, 0, sizeof(struct hexline));
110  hx->len = b[0];
111 
112  if ((*pos + hx->len + 4) >= fw->size)
113  return -EINVAL;
114 
115  hx->addr = b[1] | (b[2] << 8);
116  hx->type = b[3];
117 
118  if (hx->type == 0x04) {
119  /* b[4] and b[5] are the Extended linear address record data
120  * field */
121  hx->addr |= (b[4] << 24) | (b[5] << 16);
122  }
123 
124  memcpy(hx->data, &b[data_offs], hx->len);
125  hx->chk = b[hx->len + data_offs];
126  *pos += hx->len + 5;
127 
128  return *pos;
129 }
131 
132 MODULE_AUTHOR("Antti Palosaari <[email protected]>");
133 MODULE_DESCRIPTION("Cypress firmware download");
134 MODULE_LICENSE("GPL");