diff options
| -rw-r--r-- | drivers/usb/atm/cxacru.c | 181 |
1 files changed, 176 insertions, 5 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 3dfa3e40e148..cdcdfed9449d 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c | |||
| @@ -34,14 +34,14 @@ | |||
| 34 | #include <linux/errno.h> | 34 | #include <linux/errno.h> |
| 35 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
| 36 | #include <linux/init.h> | 36 | #include <linux/init.h> |
| 37 | #include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */ | 37 | #include <linux/device.h> |
| 38 | #include <linux/firmware.h> | 38 | #include <linux/firmware.h> |
| 39 | #include <linux/mutex.h> | 39 | #include <linux/mutex.h> |
| 40 | 40 | ||
| 41 | #include "usbatm.h" | 41 | #include "usbatm.h" |
| 42 | 42 | ||
| 43 | #define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands" | 43 | #define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott" |
| 44 | #define DRIVER_VERSION "0.2" | 44 | #define DRIVER_VERSION "0.3" |
| 45 | #define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" | 45 | #define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" |
| 46 | 46 | ||
| 47 | static const char cxacru_driver_name[] = "cxacru"; | 47 | static const char cxacru_driver_name[] = "cxacru"; |
| @@ -64,7 +64,7 @@ static const char cxacru_driver_name[] = "cxacru"; | |||
| 64 | #define SDRAM_ENA 0x1 | 64 | #define SDRAM_ENA 0x1 |
| 65 | 65 | ||
| 66 | #define CMD_TIMEOUT 2000 /* msecs */ | 66 | #define CMD_TIMEOUT 2000 /* msecs */ |
| 67 | #define POLL_INTERVAL 5000 /* msecs */ | 67 | #define POLL_INTERVAL 1 /* secs */ |
| 68 | 68 | ||
| 69 | /* commands for interaction with the modem through the control channel before | 69 | /* commands for interaction with the modem through the control channel before |
| 70 | * firmware is loaded */ | 70 | * firmware is loaded */ |
| @@ -159,6 +159,7 @@ struct cxacru_data { | |||
| 159 | 159 | ||
| 160 | int line_status; | 160 | int line_status; |
| 161 | struct delayed_work poll_work; | 161 | struct delayed_work poll_work; |
| 162 | u32 card_info[CXINF_MAX]; | ||
| 162 | 163 | ||
| 163 | /* contol handles */ | 164 | /* contol handles */ |
| 164 | struct mutex cm_serialize; | 165 | struct mutex cm_serialize; |
| @@ -170,6 +171,151 @@ struct cxacru_data { | |||
| 170 | struct completion snd_done; | 171 | struct completion snd_done; |
| 171 | }; | 172 | }; |
| 172 | 173 | ||
| 174 | /* Card info exported through sysfs */ | ||
| 175 | #define CXACRU__ATTR_INIT(_name) \ | ||
| 176 | static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL) | ||
| 177 | |||
| 178 | #define CXACRU_ATTR_INIT(_value, _type, _name) \ | ||
| 179 | static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \ | ||
| 180 | struct device_attribute *attr, char *buf) \ | ||
| 181 | { \ | ||
| 182 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
| 183 | struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \ | ||
| 184 | struct cxacru_data *instance = usbatm_instance->driver_data; \ | ||
| 185 | return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \ | ||
| 186 | } \ | ||
| 187 | CXACRU__ATTR_INIT(_name) | ||
| 188 | |||
| 189 | #define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name) | ||
| 190 | #define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) | ||
| 191 | |||
| 192 | #define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name) | ||
| 193 | #define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) | ||
| 194 | |||
| 195 | static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf) | ||
| 196 | { | ||
| 197 | return snprintf(buf, PAGE_SIZE, "%u\n", value); | ||
| 198 | } | ||
| 199 | |||
| 200 | static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf) | ||
| 201 | { | ||
| 202 | return snprintf(buf, PAGE_SIZE, "%d\n", value); | ||
| 203 | } | ||
| 204 | |||
| 205 | static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf) | ||
| 206 | { | ||
| 207 | if (unlikely(value < 0)) { | ||
| 208 | return snprintf(buf, PAGE_SIZE, "%d.%02u\n", | ||
| 209 | value / 100, -value % 100); | ||
| 210 | } else { | ||
| 211 | return snprintf(buf, PAGE_SIZE, "%d.%02u\n", | ||
| 212 | value / 100, value % 100); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf) | ||
| 217 | { | ||
| 218 | switch (value) { | ||
| 219 | case 0: return snprintf(buf, PAGE_SIZE, "no\n"); | ||
| 220 | case 1: return snprintf(buf, PAGE_SIZE, "yes\n"); | ||
| 221 | default: return 0; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf) | ||
| 226 | { | ||
| 227 | switch (value) { | ||
| 228 | case 1: return snprintf(buf, PAGE_SIZE, "not connected\n"); | ||
| 229 | case 2: return snprintf(buf, PAGE_SIZE, "connected\n"); | ||
| 230 | case 3: return snprintf(buf, PAGE_SIZE, "lost\n"); | ||
| 231 | default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf) | ||
| 236 | { | ||
| 237 | switch (value) { | ||
| 238 | case 0: return snprintf(buf, PAGE_SIZE, "down\n"); | ||
| 239 | case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n"); | ||
| 240 | case 2: return snprintf(buf, PAGE_SIZE, "training\n"); | ||
| 241 | case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n"); | ||
| 242 | case 4: return snprintf(buf, PAGE_SIZE, "exchange\n"); | ||
| 243 | case 5: return snprintf(buf, PAGE_SIZE, "up\n"); | ||
| 244 | case 6: return snprintf(buf, PAGE_SIZE, "waiting\n"); | ||
| 245 | case 7: return snprintf(buf, PAGE_SIZE, "initialising\n"); | ||
| 246 | default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf) | ||
| 251 | { | ||
| 252 | switch (value) { | ||
| 253 | case 0: return 0; | ||
| 254 | case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n"); | ||
| 255 | case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n"); | ||
| 256 | case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n"); | ||
| 257 | default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since | ||
| 263 | * this data is already in atm_dev there's no point. | ||
| 264 | * | ||
| 265 | * MAC_ADDRESS_HIGH = 0x????5544 | ||
| 266 | * MAC_ADDRESS_LOW = 0x33221100 | ||
| 267 | * Where 00-55 are bytes 0-5 of the MAC. | ||
| 268 | */ | ||
| 269 | static ssize_t cxacru_sysfs_show_mac_address(struct device *dev, | ||
| 270 | struct device_attribute *attr, char *buf) | ||
| 271 | { | ||
| 272 | struct usb_interface *intf = to_usb_interface(dev); | ||
| 273 | struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); | ||
| 274 | struct atm_dev *atm_dev = usbatm_instance->atm_dev; | ||
| 275 | |||
| 276 | return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", | ||
| 277 | atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2], | ||
| 278 | atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]); | ||
| 279 | } | ||
| 280 | |||
| 281 | /* | ||
| 282 | * All device attributes are included in CXACRU_ALL_FILES | ||
| 283 | * so that the same list can be used multiple times: | ||
| 284 | * INIT (define the device attributes) | ||
| 285 | * CREATE (create all the device files) | ||
| 286 | * REMOVE (remove all the device files) | ||
| 287 | * | ||
| 288 | * With the last two being defined as needed in the functions | ||
| 289 | * they are used in before calling CXACRU_ALL_FILES() | ||
| 290 | */ | ||
| 291 | #define CXACRU_ALL_FILES(_action) \ | ||
| 292 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \ | ||
| 293 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \ | ||
| 294 | CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \ | ||
| 295 | CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \ | ||
| 296 | CXACRU__ATTR_##_action( mac_address); \ | ||
| 297 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \ | ||
| 298 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \ | ||
| 299 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \ | ||
| 300 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \ | ||
| 301 | CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \ | ||
| 302 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \ | ||
| 303 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \ | ||
| 304 | CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \ | ||
| 305 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \ | ||
| 306 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \ | ||
| 307 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \ | ||
| 308 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \ | ||
| 309 | CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \ | ||
| 310 | CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \ | ||
| 311 | CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \ | ||
| 312 | CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \ | ||
| 313 | CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \ | ||
| 314 | CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \ | ||
| 315 | CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); | ||
| 316 | |||
| 317 | CXACRU_ALL_FILES(INIT); | ||
| 318 | |||
| 173 | /* the following three functions are stolen from drivers/usb/core/message.c */ | 319 | /* the following three functions are stolen from drivers/usb/core/message.c */ |
| 174 | static void cxacru_blocking_completion(struct urb *urb) | 320 | static void cxacru_blocking_completion(struct urb *urb) |
| 175 | { | 321 | { |
| @@ -395,6 +541,8 @@ static void cxacru_poll_status(struct work_struct *work) | |||
| 395 | goto reschedule; | 541 | goto reschedule; |
| 396 | } | 542 | } |
| 397 | 543 | ||
| 544 | memcpy(instance->card_info, buf, sizeof(instance->card_info)); | ||
| 545 | |||
| 398 | if (instance->line_status == buf[CXINF_LINE_STATUS]) | 546 | if (instance->line_status == buf[CXINF_LINE_STATUS]) |
| 399 | goto reschedule; | 547 | goto reschedule; |
| 400 | 548 | ||
| @@ -449,7 +597,8 @@ static void cxacru_poll_status(struct work_struct *work) | |||
| 449 | break; | 597 | break; |
| 450 | } | 598 | } |
| 451 | reschedule: | 599 | reschedule: |
| 452 | schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL)); | 600 | schedule_delayed_work(&instance->poll_work, |
| 601 | round_jiffies_relative(msecs_to_jiffies(POLL_INTERVAL*1000))); | ||
| 453 | } | 602 | } |
| 454 | 603 | ||
| 455 | static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, | 604 | static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, |
| @@ -684,6 +833,7 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, | |||
| 684 | 833 | ||
| 685 | instance->usbatm = usbatm_instance; | 834 | instance->usbatm = usbatm_instance; |
| 686 | instance->modem_type = (struct cxacru_modem_type *) id->driver_info; | 835 | instance->modem_type = (struct cxacru_modem_type *) id->driver_info; |
| 836 | memset(instance->card_info, 0, sizeof(instance->card_info)); | ||
| 687 | 837 | ||
| 688 | instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); | 838 | instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); |
| 689 | if (!instance->rcv_buf) { | 839 | if (!instance->rcv_buf) { |
| @@ -710,6 +860,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, | |||
| 710 | goto fail; | 860 | goto fail; |
| 711 | } | 861 | } |
| 712 | 862 | ||
| 863 | #define CXACRU_DEVICE_CREATE_FILE(_name) \ | ||
| 864 | ret = device_create_file(&intf->dev, &dev_attr_##_name); \ | ||
| 865 | if (unlikely(ret)) \ | ||
| 866 | goto fail_sysfs; | ||
| 867 | CXACRU_ALL_FILES(CREATE); | ||
| 868 | #undef CXACRU_DEVICE_CREATE_FILE | ||
| 869 | |||
| 713 | usb_fill_int_urb(instance->rcv_urb, | 870 | usb_fill_int_urb(instance->rcv_urb, |
| 714 | usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), | 871 | usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), |
| 715 | instance->rcv_buf, PAGE_SIZE, | 872 | instance->rcv_buf, PAGE_SIZE, |
| @@ -730,6 +887,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, | |||
| 730 | 887 | ||
| 731 | return 0; | 888 | return 0; |
| 732 | 889 | ||
| 890 | fail_sysfs: | ||
| 891 | dbg("cxacru_bind: device_create_file failed (%d)\n", ret); | ||
| 892 | |||
| 893 | #define CXACRU_DEVICE_REMOVE_FILE(_name) \ | ||
| 894 | device_remove_file(&intf->dev, &dev_attr_##_name); | ||
| 895 | CXACRU_ALL_FILES(REMOVE); | ||
| 896 | #undef CXACRU_DEVICE_REVOVE_FILE | ||
| 897 | |||
| 733 | fail: | 898 | fail: |
| 734 | free_page((unsigned long) instance->snd_buf); | 899 | free_page((unsigned long) instance->snd_buf); |
| 735 | free_page((unsigned long) instance->rcv_buf); | 900 | free_page((unsigned long) instance->rcv_buf); |
| @@ -762,6 +927,12 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, | |||
| 762 | 927 | ||
| 763 | free_page((unsigned long) instance->snd_buf); | 928 | free_page((unsigned long) instance->snd_buf); |
| 764 | free_page((unsigned long) instance->rcv_buf); | 929 | free_page((unsigned long) instance->rcv_buf); |
| 930 | |||
| 931 | #define CXACRU_DEVICE_REMOVE_FILE(_name) \ | ||
| 932 | device_remove_file(&intf->dev, &dev_attr_##_name); | ||
| 933 | CXACRU_ALL_FILES(REMOVE); | ||
| 934 | #undef CXACRU_DEVICE_REVOVE_FILE | ||
| 935 | |||
| 765 | kfree(instance); | 936 | kfree(instance); |
| 766 | 937 | ||
| 767 | usbatm_instance->driver_data = NULL; | 938 | usbatm_instance->driver_data = NULL; |
