diff options
| -rw-r--r-- | drivers/media/radio/radio-shark2.c | 122 |
1 files changed, 73 insertions, 49 deletions
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c index 217483ca1b18..7b4efdfaae28 100644 --- a/drivers/media/radio/radio-shark2.c +++ b/drivers/media/radio/radio-shark2.c | |||
| @@ -35,6 +35,11 @@ | |||
| 35 | #include <media/v4l2-device.h> | 35 | #include <media/v4l2-device.h> |
| 36 | #include "radio-tea5777.h" | 36 | #include "radio-tea5777.h" |
| 37 | 37 | ||
| 38 | #if defined(CONFIG_LEDS_CLASS) || \ | ||
| 39 | (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK2_MODULE)) | ||
| 40 | #define SHARK_USE_LEDS 1 | ||
| 41 | #endif | ||
| 42 | |||
| 38 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | 43 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); |
| 39 | MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver"); | 44 | MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver"); |
| 40 | MODULE_LICENSE("GPL"); | 45 | MODULE_LICENSE("GPL"); |
| @@ -43,7 +48,6 @@ static int debug; | |||
| 43 | module_param(debug, int, 0); | 48 | module_param(debug, int, 0); |
| 44 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); | 49 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); |
| 45 | 50 | ||
| 46 | |||
| 47 | #define SHARK_IN_EP 0x83 | 51 | #define SHARK_IN_EP 0x83 |
| 48 | #define SHARK_OUT_EP 0x05 | 52 | #define SHARK_OUT_EP 0x05 |
| 49 | 53 | ||
| @@ -54,36 +58,18 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); | |||
| 54 | 58 | ||
| 55 | enum { BLUE_LED, RED_LED, NO_LEDS }; | 59 | enum { BLUE_LED, RED_LED, NO_LEDS }; |
| 56 | 60 | ||
| 57 | static void shark_led_set_blue(struct led_classdev *led_cdev, | ||
| 58 | enum led_brightness value); | ||
| 59 | static void shark_led_set_red(struct led_classdev *led_cdev, | ||
| 60 | enum led_brightness value); | ||
| 61 | |||
| 62 | static const struct led_classdev shark_led_templates[NO_LEDS] = { | ||
| 63 | [BLUE_LED] = { | ||
| 64 | .name = "%s:blue:", | ||
| 65 | .brightness = LED_OFF, | ||
| 66 | .max_brightness = 127, | ||
| 67 | .brightness_set = shark_led_set_blue, | ||
| 68 | }, | ||
| 69 | [RED_LED] = { | ||
| 70 | .name = "%s:red:", | ||
| 71 | .brightness = LED_OFF, | ||
| 72 | .max_brightness = 1, | ||
| 73 | .brightness_set = shark_led_set_red, | ||
| 74 | }, | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct shark_device { | 61 | struct shark_device { |
| 78 | struct usb_device *usbdev; | 62 | struct usb_device *usbdev; |
| 79 | struct v4l2_device v4l2_dev; | 63 | struct v4l2_device v4l2_dev; |
| 80 | struct radio_tea5777 tea; | 64 | struct radio_tea5777 tea; |
| 81 | 65 | ||
| 66 | #ifdef SHARK_USE_LEDS | ||
| 82 | struct work_struct led_work; | 67 | struct work_struct led_work; |
| 83 | struct led_classdev leds[NO_LEDS]; | 68 | struct led_classdev leds[NO_LEDS]; |
| 84 | char led_names[NO_LEDS][32]; | 69 | char led_names[NO_LEDS][32]; |
| 85 | atomic_t brightness[NO_LEDS]; | 70 | atomic_t brightness[NO_LEDS]; |
| 86 | unsigned long brightness_new; | 71 | unsigned long brightness_new; |
| 72 | #endif | ||
| 87 | 73 | ||
| 88 | u8 *transfer_buffer; | 74 | u8 *transfer_buffer; |
| 89 | }; | 75 | }; |
| @@ -161,6 +147,7 @@ static struct radio_tea5777_ops shark_tea_ops = { | |||
| 161 | .read_reg = shark_read_reg, | 147 | .read_reg = shark_read_reg, |
| 162 | }; | 148 | }; |
| 163 | 149 | ||
| 150 | #ifdef SHARK_USE_LEDS | ||
| 164 | static void shark_led_work(struct work_struct *work) | 151 | static void shark_led_work(struct work_struct *work) |
| 165 | { | 152 | { |
| 166 | struct shark_device *shark = | 153 | struct shark_device *shark = |
| @@ -208,21 +195,72 @@ static void shark_led_set_red(struct led_classdev *led_cdev, | |||
| 208 | schedule_work(&shark->led_work); | 195 | schedule_work(&shark->led_work); |
| 209 | } | 196 | } |
| 210 | 197 | ||
| 198 | static const struct led_classdev shark_led_templates[NO_LEDS] = { | ||
| 199 | [BLUE_LED] = { | ||
| 200 | .name = "%s:blue:", | ||
| 201 | .brightness = LED_OFF, | ||
| 202 | .max_brightness = 127, | ||
| 203 | .brightness_set = shark_led_set_blue, | ||
| 204 | }, | ||
| 205 | [RED_LED] = { | ||
| 206 | .name = "%s:red:", | ||
| 207 | .brightness = LED_OFF, | ||
| 208 | .max_brightness = 1, | ||
| 209 | .brightness_set = shark_led_set_red, | ||
| 210 | }, | ||
| 211 | }; | ||
| 212 | |||
| 213 | static int shark_register_leds(struct shark_device *shark, struct device *dev) | ||
| 214 | { | ||
| 215 | int i, retval; | ||
| 216 | |||
| 217 | INIT_WORK(&shark->led_work, shark_led_work); | ||
| 218 | for (i = 0; i < NO_LEDS; i++) { | ||
| 219 | shark->leds[i] = shark_led_templates[i]; | ||
| 220 | snprintf(shark->led_names[i], sizeof(shark->led_names[0]), | ||
| 221 | shark->leds[i].name, shark->v4l2_dev.name); | ||
| 222 | shark->leds[i].name = shark->led_names[i]; | ||
| 223 | retval = led_classdev_register(dev, &shark->leds[i]); | ||
| 224 | if (retval) { | ||
| 225 | v4l2_err(&shark->v4l2_dev, | ||
| 226 | "couldn't register led: %s\n", | ||
| 227 | shark->led_names[i]); | ||
| 228 | return retval; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | static void shark_unregister_leds(struct shark_device *shark) | ||
| 235 | { | ||
| 236 | int i; | ||
| 237 | |||
| 238 | for (i = 0; i < NO_LEDS; i++) | ||
| 239 | led_classdev_unregister(&shark->leds[i]); | ||
| 240 | |||
| 241 | cancel_work_sync(&shark->led_work); | ||
| 242 | } | ||
| 243 | #else | ||
| 244 | static int shark_register_leds(struct shark_device *shark, struct device *dev) | ||
| 245 | { | ||
| 246 | v4l2_warn(&shark->v4l2_dev, | ||
| 247 | "CONFIG_LED_CLASS not enabled, LED support disabled\n"); | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | static inline void shark_unregister_leds(struct shark_device *shark) { } | ||
| 251 | #endif | ||
| 252 | |||
| 211 | static void usb_shark_disconnect(struct usb_interface *intf) | 253 | static void usb_shark_disconnect(struct usb_interface *intf) |
| 212 | { | 254 | { |
| 213 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); | 255 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); |
| 214 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); | 256 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
| 215 | int i; | ||
| 216 | 257 | ||
| 217 | mutex_lock(&shark->tea.mutex); | 258 | mutex_lock(&shark->tea.mutex); |
| 218 | v4l2_device_disconnect(&shark->v4l2_dev); | 259 | v4l2_device_disconnect(&shark->v4l2_dev); |
| 219 | radio_tea5777_exit(&shark->tea); | 260 | radio_tea5777_exit(&shark->tea); |
| 220 | mutex_unlock(&shark->tea.mutex); | 261 | mutex_unlock(&shark->tea.mutex); |
| 221 | 262 | ||
| 222 | for (i = 0; i < NO_LEDS; i++) | 263 | shark_unregister_leds(shark); |
| 223 | led_classdev_unregister(&shark->leds[i]); | ||
| 224 | |||
| 225 | cancel_work_sync(&shark->led_work); | ||
| 226 | 264 | ||
| 227 | v4l2_device_put(&shark->v4l2_dev); | 265 | v4l2_device_put(&shark->v4l2_dev); |
| 228 | } | 266 | } |
| @@ -240,7 +278,7 @@ static int usb_shark_probe(struct usb_interface *intf, | |||
| 240 | const struct usb_device_id *id) | 278 | const struct usb_device_id *id) |
| 241 | { | 279 | { |
| 242 | struct shark_device *shark; | 280 | struct shark_device *shark; |
| 243 | int i, retval = -ENOMEM; | 281 | int retval = -ENOMEM; |
| 244 | 282 | ||
| 245 | shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL); | 283 | shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL); |
| 246 | if (!shark) | 284 | if (!shark) |
| @@ -250,8 +288,13 @@ static int usb_shark_probe(struct usb_interface *intf, | |||
| 250 | if (!shark->transfer_buffer) | 288 | if (!shark->transfer_buffer) |
| 251 | goto err_alloc_buffer; | 289 | goto err_alloc_buffer; |
| 252 | 290 | ||
| 253 | shark->v4l2_dev.release = usb_shark_release; | ||
| 254 | v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance); | 291 | v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance); |
| 292 | |||
| 293 | retval = shark_register_leds(shark, &intf->dev); | ||
| 294 | if (retval) | ||
| 295 | goto err_reg_leds; | ||
| 296 | |||
| 297 | shark->v4l2_dev.release = usb_shark_release; | ||
| 255 | retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev); | 298 | retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev); |
| 256 | if (retval) { | 299 | if (retval) { |
| 257 | v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n"); | 300 | v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n"); |
| @@ -275,32 +318,13 @@ static int usb_shark_probe(struct usb_interface *intf, | |||
| 275 | goto err_init_tea; | 318 | goto err_init_tea; |
| 276 | } | 319 | } |
| 277 | 320 | ||
| 278 | INIT_WORK(&shark->led_work, shark_led_work); | ||
| 279 | for (i = 0; i < NO_LEDS; i++) { | ||
| 280 | shark->leds[i] = shark_led_templates[i]; | ||
| 281 | snprintf(shark->led_names[i], sizeof(shark->led_names[0]), | ||
| 282 | shark->leds[i].name, shark->v4l2_dev.name); | ||
| 283 | shark->leds[i].name = shark->led_names[i]; | ||
| 284 | /* | ||
| 285 | * We don't fail the probe if we fail to register the leds, | ||
| 286 | * because once we've called radio_tea5777_init, the /dev/radio0 | ||
| 287 | * node may be opened from userspace holding a reference to us! | ||
| 288 | * | ||
| 289 | * Note we cannot register the leds first instead as | ||
| 290 | * shark_led_work depends on the v4l2 mutex and registered bit. | ||
| 291 | */ | ||
| 292 | retval = led_classdev_register(&intf->dev, &shark->leds[i]); | ||
| 293 | if (retval) | ||
| 294 | v4l2_err(&shark->v4l2_dev, | ||
| 295 | "couldn't register led: %s\n", | ||
| 296 | shark->led_names[i]); | ||
| 297 | } | ||
| 298 | |||
| 299 | return 0; | 321 | return 0; |
| 300 | 322 | ||
| 301 | err_init_tea: | 323 | err_init_tea: |
| 302 | v4l2_device_unregister(&shark->v4l2_dev); | 324 | v4l2_device_unregister(&shark->v4l2_dev); |
| 303 | err_reg_dev: | 325 | err_reg_dev: |
| 326 | shark_unregister_leds(shark); | ||
| 327 | err_reg_leds: | ||
| 304 | kfree(shark->transfer_buffer); | 328 | kfree(shark->transfer_buffer); |
| 305 | err_alloc_buffer: | 329 | err_alloc_buffer: |
| 306 | kfree(shark); | 330 | kfree(shark); |
