aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/atm/cxacru.c
diff options
context:
space:
mode:
authorSimon Arlott <simon@fire.lp0.eu>2007-04-26 03:38:05 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-04-27 16:28:41 -0400
commit6a02c996bce297a782432e29c69268356e97fadd (patch)
treea68fc295a2a133b0cfdc7b3c2724a1cb0c9db9c0 /drivers/usb/atm/cxacru.c
parent7d5e1dd40bd5ced457be178e4f0b1267a3df2142 (diff)
USB: cxacru: ADSL state management
The device has commands to start/stop the ADSL function, so this adds a sysfs attribute to allow it to be started/stopped/restarted. It also stops polling the device for status when the ADSL function is disabled. There are no problems with sending multiple start or stop commands, even with a fast loop of them the device still works. There is no need to protect the restart process from further user actions while it's waiting for 1.5s. Signed-off-by: Simon Arlott <simon@fire.lp0.eu> Cc: Duncan Sands <duncan.sands@math.u-psud.fr> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/atm/cxacru.c')
-rw-r--r--drivers/usb/atm/cxacru.c236
1 files changed, 227 insertions, 9 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index cdcdfed9449d..30b7bfbc985a 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -4,6 +4,7 @@
4 * 4 *
5 * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan 5 * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
6 * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) 6 * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
7 * Copyright (C) 2007 Simon Arlott
7 * 8 *
8 * This program is free software; you can redistribute it and/or modify it 9 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free 10 * under the terms of the GNU General Public License as published by the Free
@@ -146,6 +147,13 @@ enum cxacru_info_idx {
146 CXINF_MAX = 0x1c, 147 CXINF_MAX = 0x1c,
147}; 148};
148 149
150enum cxacru_poll_state {
151 CXPOLL_STOPPING,
152 CXPOLL_STOPPED,
153 CXPOLL_POLLING,
154 CXPOLL_SHUTDOWN
155};
156
149struct cxacru_modem_type { 157struct cxacru_modem_type {
150 u32 pll_f_clk; 158 u32 pll_f_clk;
151 u32 pll_b_clk; 159 u32 pll_b_clk;
@@ -158,8 +166,12 @@ struct cxacru_data {
158 const struct cxacru_modem_type *modem_type; 166 const struct cxacru_modem_type *modem_type;
159 167
160 int line_status; 168 int line_status;
169 struct mutex adsl_state_serialize;
170 int adsl_status;
161 struct delayed_work poll_work; 171 struct delayed_work poll_work;
162 u32 card_info[CXINF_MAX]; 172 u32 card_info[CXINF_MAX];
173 struct mutex poll_state_serialize;
174 int poll_state;
163 175
164 /* contol handles */ 176 /* contol handles */
165 struct mutex cm_serialize; 177 struct mutex cm_serialize;
@@ -171,10 +183,18 @@ struct cxacru_data {
171 struct completion snd_done; 183 struct completion snd_done;
172}; 184};
173 185
186static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
187 u8 *wdata, int wsize, u8 *rdata, int rsize);
188static void cxacru_poll_status(struct work_struct *work);
189
174/* Card info exported through sysfs */ 190/* Card info exported through sysfs */
175#define CXACRU__ATTR_INIT(_name) \ 191#define CXACRU__ATTR_INIT(_name) \
176static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL) 192static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
177 193
194#define CXACRU_CMD_INIT(_name) \
195static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
196 cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
197
178#define CXACRU_ATTR_INIT(_value, _type, _name) \ 198#define CXACRU_ATTR_INIT(_value, _type, _name) \
179static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \ 199static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
180 struct device_attribute *attr, char *buf) \ 200 struct device_attribute *attr, char *buf) \
@@ -187,9 +207,11 @@ static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
187CXACRU__ATTR_INIT(_name) 207CXACRU__ATTR_INIT(_name)
188 208
189#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name) 209#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
210#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
190#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) 211#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
191 212
192#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name) 213#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
214#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
193#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) 215#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
194 216
195static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf) 217static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
@@ -278,6 +300,119 @@ static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
278 atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]); 300 atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
279} 301}
280 302
303static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
304 struct device_attribute *attr, char *buf)
305{
306 struct usb_interface *intf = to_usb_interface(dev);
307 struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
308 struct cxacru_data *instance = usbatm_instance->driver_data;
309 u32 value = instance->card_info[CXINF_LINE_STARTABLE];
310
311 switch (value) {
312 case 0: return snprintf(buf, PAGE_SIZE, "running\n");
313 case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
314 default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
315 }
316}
317
318static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
319 struct device_attribute *attr, const char *buf, size_t count)
320{
321 struct usb_interface *intf = to_usb_interface(dev);
322 struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
323 struct cxacru_data *instance = usbatm_instance->driver_data;
324 int ret;
325 int poll = -1;
326 char str_cmd[8];
327 int len = strlen(buf);
328
329 if (!capable(CAP_NET_ADMIN))
330 return -EACCES;
331
332 ret = sscanf(buf, "%7s", str_cmd);
333 if (ret != 1)
334 return -EINVAL;
335 ret = 0;
336
337 if (mutex_lock_interruptible(&instance->adsl_state_serialize))
338 return -ERESTARTSYS;
339
340 if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
341 ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
342 if (ret < 0) {
343 atm_err(usbatm_instance, "change adsl state:"
344 " CHIP_ADSL_LINE_STOP returned %d\n", ret);
345
346 ret = -EIO;
347 } else {
348 ret = len;
349 poll = CXPOLL_STOPPED;
350 }
351 }
352
353 /* Line status is only updated every second
354 * and the device appears to only react to
355 * START/STOP every second too. Wait 1.5s to
356 * be sure that restart will have an effect. */
357 if (!strcmp(str_cmd, "restart"))
358 msleep(1500);
359
360 if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
361 ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
362 if (ret < 0) {
363 atm_err(usbatm_instance, "change adsl state:"
364 " CHIP_ADSL_LINE_START returned %d\n", ret);
365
366 ret = -EIO;
367 } else {
368 ret = len;
369 poll = CXPOLL_POLLING;
370 }
371 }
372
373 if (!strcmp(str_cmd, "poll")) {
374 ret = len;
375 poll = CXPOLL_POLLING;
376 }
377
378 if (ret == 0) {
379 ret = -EINVAL;
380 poll = -1;
381 }
382
383 if (poll == CXPOLL_POLLING) {
384 mutex_lock(&instance->poll_state_serialize);
385 switch (instance->poll_state) {
386 case CXPOLL_STOPPED:
387 /* start polling */
388 instance->poll_state = CXPOLL_POLLING;
389 break;
390
391 case CXPOLL_STOPPING:
392 /* abort stop request */
393 instance->poll_state = CXPOLL_POLLING;
394 case CXPOLL_POLLING:
395 case CXPOLL_SHUTDOWN:
396 /* don't start polling */
397 poll = -1;
398 }
399 mutex_unlock(&instance->poll_state_serialize);
400 } else if (poll == CXPOLL_STOPPED) {
401 mutex_lock(&instance->poll_state_serialize);
402 /* request stop */
403 if (instance->poll_state == CXPOLL_POLLING)
404 instance->poll_state = CXPOLL_STOPPING;
405 mutex_unlock(&instance->poll_state_serialize);
406 }
407
408 mutex_unlock(&instance->adsl_state_serialize);
409
410 if (poll == CXPOLL_POLLING)
411 cxacru_poll_status(&instance->poll_work.work);
412
413 return ret;
414}
415
281/* 416/*
282 * All device attributes are included in CXACRU_ALL_FILES 417 * All device attributes are included in CXACRU_ALL_FILES
283 * so that the same list can be used multiple times: 418 * so that the same list can be used multiple times:
@@ -312,7 +447,8 @@ CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \
312CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \ 447CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
313CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \ 448CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \
314CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \ 449CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \
315CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); 450CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \
451CXACRU_CMD_##_action( adsl_state);
316 452
317CXACRU_ALL_FILES(INIT); 453CXACRU_ALL_FILES(INIT);
318 454
@@ -493,8 +629,6 @@ static int cxacru_card_status(struct cxacru_data *instance)
493 return 0; 629 return 0;
494} 630}
495 631
496static void cxacru_poll_status(struct work_struct *work);
497
498static int cxacru_atm_start(struct usbatm_data *usbatm_instance, 632static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
499 struct atm_dev *atm_dev) 633 struct atm_dev *atm_dev)
500{ 634{
@@ -503,6 +637,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
503 struct atm_dev *atm_dev = usbatm_instance->atm_dev; 637 struct atm_dev *atm_dev = usbatm_instance->atm_dev;
504 */ 638 */
505 int ret; 639 int ret;
640 int start_polling = 1;
506 641
507 dbg("cxacru_atm_start"); 642 dbg("cxacru_atm_start");
508 643
@@ -515,14 +650,35 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
515 } 650 }
516 651
517 /* start ADSL */ 652 /* start ADSL */
653 mutex_lock(&instance->adsl_state_serialize);
518 ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); 654 ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
519 if (ret < 0) { 655 if (ret < 0) {
520 atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret); 656 atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
657 mutex_unlock(&instance->adsl_state_serialize);
521 return ret; 658 return ret;
522 } 659 }
523 660
524 /* Start status polling */ 661 /* Start status polling */
525 cxacru_poll_status(&instance->poll_work.work); 662 mutex_lock(&instance->poll_state_serialize);
663 switch (instance->poll_state) {
664 case CXPOLL_STOPPED:
665 /* start polling */
666 instance->poll_state = CXPOLL_POLLING;
667 break;
668
669 case CXPOLL_STOPPING:
670 /* abort stop request */
671 instance->poll_state = CXPOLL_POLLING;
672 case CXPOLL_POLLING:
673 case CXPOLL_SHUTDOWN:
674 /* don't start polling */
675 start_polling = 0;
676 }
677 mutex_unlock(&instance->poll_state_serialize);
678 mutex_unlock(&instance->adsl_state_serialize);
679
680 if (start_polling)
681 cxacru_poll_status(&instance->poll_work.work);
526 return 0; 682 return 0;
527} 683}
528 684
@@ -533,16 +689,46 @@ static void cxacru_poll_status(struct work_struct *work)
533 u32 buf[CXINF_MAX] = {}; 689 u32 buf[CXINF_MAX] = {};
534 struct usbatm_data *usbatm = instance->usbatm; 690 struct usbatm_data *usbatm = instance->usbatm;
535 struct atm_dev *atm_dev = usbatm->atm_dev; 691 struct atm_dev *atm_dev = usbatm->atm_dev;
692 int keep_polling = 1;
536 int ret; 693 int ret;
537 694
538 ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX); 695 ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
539 if (ret < 0) { 696 if (ret < 0) {
540 atm_warn(usbatm, "poll status: error %d\n", ret); 697 if (ret != -ESHUTDOWN)
698 atm_warn(usbatm, "poll status: error %d\n", ret);
699
700 mutex_lock(&instance->poll_state_serialize);
701 if (instance->poll_state != CXPOLL_SHUTDOWN) {
702 instance->poll_state = CXPOLL_STOPPED;
703
704 if (ret != -ESHUTDOWN)
705 atm_warn(usbatm, "polling disabled, set adsl_state"
706 " to 'start' or 'poll' to resume\n");
707 }
708 mutex_unlock(&instance->poll_state_serialize);
541 goto reschedule; 709 goto reschedule;
542 } 710 }
543 711
544 memcpy(instance->card_info, buf, sizeof(instance->card_info)); 712 memcpy(instance->card_info, buf, sizeof(instance->card_info));
545 713
714 if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
715 instance->adsl_status = buf[CXINF_LINE_STARTABLE];
716
717 switch (instance->adsl_status) {
718 case 0:
719 atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
720 break;
721
722 case 1:
723 atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
724 break;
725
726 default:
727 atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
728 break;
729 }
730 }
731
546 if (instance->line_status == buf[CXINF_LINE_STATUS]) 732 if (instance->line_status == buf[CXINF_LINE_STATUS])
547 goto reschedule; 733 goto reschedule;
548 734
@@ -597,8 +783,20 @@ static void cxacru_poll_status(struct work_struct *work)
597 break; 783 break;
598 } 784 }
599reschedule: 785reschedule:
600 schedule_delayed_work(&instance->poll_work, 786
601 round_jiffies_relative(msecs_to_jiffies(POLL_INTERVAL*1000))); 787 mutex_lock(&instance->poll_state_serialize);
788 if (instance->poll_state == CXPOLL_STOPPING &&
789 instance->adsl_status == 1 && /* stopped */
790 instance->line_status == 0) /* down */
791 instance->poll_state = CXPOLL_STOPPED;
792
793 if (instance->poll_state == CXPOLL_STOPPED)
794 keep_polling = 0;
795 mutex_unlock(&instance->poll_state_serialize);
796
797 if (keep_polling)
798 schedule_delayed_work(&instance->poll_work,
799 round_jiffies_relative(POLL_INTERVAL*HZ));
602} 800}
603 801
604static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, 802static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
@@ -835,6 +1033,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
835 instance->modem_type = (struct cxacru_modem_type *) id->driver_info; 1033 instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
836 memset(instance->card_info, 0, sizeof(instance->card_info)); 1034 memset(instance->card_info, 0, sizeof(instance->card_info));
837 1035
1036 mutex_init(&instance->poll_state_serialize);
1037 instance->poll_state = CXPOLL_STOPPED;
1038 instance->line_status = -1;
1039 instance->adsl_status = -1;
1040
1041 mutex_init(&instance->adsl_state_serialize);
1042
838 instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); 1043 instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
839 if (!instance->rcv_buf) { 1044 if (!instance->rcv_buf) {
840 dbg("cxacru_bind: no memory for rcv_buf"); 1045 dbg("cxacru_bind: no memory for rcv_buf");
@@ -909,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
909 struct usb_interface *intf) 1114 struct usb_interface *intf)
910{ 1115{
911 struct cxacru_data *instance = usbatm_instance->driver_data; 1116 struct cxacru_data *instance = usbatm_instance->driver_data;
1117 int is_polling = 1;
912 1118
913 dbg("cxacru_unbind entered"); 1119 dbg("cxacru_unbind entered");
914 1120
@@ -917,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
917 return; 1123 return;
918 } 1124 }
919 1125
920 while (!cancel_delayed_work(&instance->poll_work)) 1126 mutex_lock(&instance->poll_state_serialize);
921 flush_scheduled_work(); 1127 BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
1128
1129 /* ensure that status polling continues unless
1130 * it has already stopped */
1131 if (instance->poll_state == CXPOLL_STOPPED)
1132 is_polling = 0;
1133
1134 /* stop polling from being stopped or started */
1135 instance->poll_state = CXPOLL_SHUTDOWN;
1136 mutex_unlock(&instance->poll_state_serialize);
1137
1138 if (is_polling)
1139 cancel_rearming_delayed_work(&instance->poll_work);
922 1140
923 usb_kill_urb(instance->snd_urb); 1141 usb_kill_urb(instance->snd_urb);
924 usb_kill_urb(instance->rcv_urb); 1142 usb_kill_urb(instance->rcv_urb);