root/drivers/usb/misc/appledisplay.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. appledisplay_complete
  2. appledisplay_bl_update_status
  3. appledisplay_bl_get_brightness
  4. appledisplay_work
  5. appledisplay_probe
  6. appledisplay_disconnect
  7. appledisplay_init
  8. appledisplay_exit

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Apple Cinema Display driver
   4  *
   5  * Copyright (C) 2006  Michael Hanselmann (linux-kernel@hansmi.ch)
   6  *
   7  * Thanks to Caskey L. Dickson for his work with acdctl.
   8  */
   9 
  10 #include <linux/kernel.h>
  11 #include <linux/errno.h>
  12 #include <linux/init.h>
  13 #include <linux/module.h>
  14 #include <linux/slab.h>
  15 #include <linux/usb.h>
  16 #include <linux/backlight.h>
  17 #include <linux/timer.h>
  18 #include <linux/workqueue.h>
  19 #include <linux/atomic.h>
  20 
  21 #define APPLE_VENDOR_ID         0x05AC
  22 
  23 #define USB_REQ_GET_REPORT      0x01
  24 #define USB_REQ_SET_REPORT      0x09
  25 
  26 #define ACD_USB_TIMEOUT         250
  27 
  28 #define ACD_USB_EDID            0x0302
  29 #define ACD_USB_BRIGHTNESS      0x0310
  30 
  31 #define ACD_BTN_NONE            0
  32 #define ACD_BTN_BRIGHT_UP       3
  33 #define ACD_BTN_BRIGHT_DOWN     4
  34 
  35 #define ACD_URB_BUFFER_LEN      2
  36 #define ACD_MSG_BUFFER_LEN      2
  37 
  38 #define APPLEDISPLAY_DEVICE(prod)                               \
  39         .match_flags = USB_DEVICE_ID_MATCH_DEVICE |             \
  40                        USB_DEVICE_ID_MATCH_INT_CLASS |          \
  41                        USB_DEVICE_ID_MATCH_INT_PROTOCOL,        \
  42         .idVendor = APPLE_VENDOR_ID,                            \
  43         .idProduct = (prod),                                    \
  44         .bInterfaceClass = USB_CLASS_HID,                       \
  45         .bInterfaceProtocol = 0x00
  46 
  47 /* table of devices that work with this driver */
  48 static const struct usb_device_id appledisplay_table[] = {
  49         { APPLEDISPLAY_DEVICE(0x9218) },
  50         { APPLEDISPLAY_DEVICE(0x9219) },
  51         { APPLEDISPLAY_DEVICE(0x921c) },
  52         { APPLEDISPLAY_DEVICE(0x921d) },
  53         { APPLEDISPLAY_DEVICE(0x9222) },
  54         { APPLEDISPLAY_DEVICE(0x9226) },
  55         { APPLEDISPLAY_DEVICE(0x9236) },
  56 
  57         /* Terminating entry */
  58         { }
  59 };
  60 MODULE_DEVICE_TABLE(usb, appledisplay_table);
  61 
  62 /* Structure to hold all of our device specific stuff */
  63 struct appledisplay {
  64         struct usb_device *udev;        /* usb device */
  65         struct urb *urb;                /* usb request block */
  66         struct backlight_device *bd;    /* backlight device */
  67         u8 *urbdata;                    /* interrupt URB data buffer */
  68         u8 *msgdata;                    /* control message data buffer */
  69 
  70         struct delayed_work work;
  71         int button_pressed;
  72         struct mutex sysfslock;         /* concurrent read and write */
  73 };
  74 
  75 static atomic_t count_displays = ATOMIC_INIT(0);
  76 
  77 static void appledisplay_complete(struct urb *urb)
  78 {
  79         struct appledisplay *pdata = urb->context;
  80         struct device *dev = &pdata->udev->dev;
  81         int status = urb->status;
  82         int retval;
  83 
  84         switch (status) {
  85         case 0:
  86                 /* success */
  87                 break;
  88         case -EOVERFLOW:
  89                 dev_err(dev,
  90                         "OVERFLOW with data length %d, actual length is %d\n",
  91                         ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
  92                 /* fall through */
  93         case -ECONNRESET:
  94         case -ENOENT:
  95         case -ESHUTDOWN:
  96                 /* This urb is terminated, clean up */
  97                 dev_dbg(dev, "%s - urb shuttingdown with status: %d\n",
  98                         __func__, status);
  99                 return;
 100         default:
 101                 dev_dbg(dev, "%s - nonzero urb status received: %d\n",
 102                         __func__, status);
 103                 goto exit;
 104         }
 105 
 106         switch(pdata->urbdata[1]) {
 107         case ACD_BTN_BRIGHT_UP:
 108         case ACD_BTN_BRIGHT_DOWN:
 109                 pdata->button_pressed = 1;
 110                 schedule_delayed_work(&pdata->work, 0);
 111                 break;
 112         case ACD_BTN_NONE:
 113         default:
 114                 pdata->button_pressed = 0;
 115                 break;
 116         }
 117 
 118 exit:
 119         retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);
 120         if (retval) {
 121                 dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
 122                         __func__, retval);
 123         }
 124 }
 125 
 126 static int appledisplay_bl_update_status(struct backlight_device *bd)
 127 {
 128         struct appledisplay *pdata = bl_get_data(bd);
 129         int retval;
 130 
 131         mutex_lock(&pdata->sysfslock);
 132         pdata->msgdata[0] = 0x10;
 133         pdata->msgdata[1] = bd->props.brightness;
 134 
 135         retval = usb_control_msg(
 136                 pdata->udev,
 137                 usb_sndctrlpipe(pdata->udev, 0),
 138                 USB_REQ_SET_REPORT,
 139                 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 140                 ACD_USB_BRIGHTNESS,
 141                 0,
 142                 pdata->msgdata, 2,
 143                 ACD_USB_TIMEOUT);
 144         mutex_unlock(&pdata->sysfslock);
 145 
 146         if (retval < 0)
 147                 return retval;
 148         else
 149                 return 0;
 150 }
 151 
 152 static int appledisplay_bl_get_brightness(struct backlight_device *bd)
 153 {
 154         struct appledisplay *pdata = bl_get_data(bd);
 155         int retval, brightness;
 156 
 157         mutex_lock(&pdata->sysfslock);
 158         retval = usb_control_msg(
 159                 pdata->udev,
 160                 usb_rcvctrlpipe(pdata->udev, 0),
 161                 USB_REQ_GET_REPORT,
 162                 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 163                 ACD_USB_BRIGHTNESS,
 164                 0,
 165                 pdata->msgdata, 2,
 166                 ACD_USB_TIMEOUT);
 167         if (retval < 2) {
 168                 if (retval >= 0)
 169                         retval = -EMSGSIZE;
 170         } else {
 171                 brightness = pdata->msgdata[1];
 172         }
 173         mutex_unlock(&pdata->sysfslock);
 174 
 175         if (retval < 0)
 176                 return retval;
 177         else
 178                 return brightness;
 179 }
 180 
 181 static const struct backlight_ops appledisplay_bl_data = {
 182         .get_brightness = appledisplay_bl_get_brightness,
 183         .update_status  = appledisplay_bl_update_status,
 184 };
 185 
 186 static void appledisplay_work(struct work_struct *work)
 187 {
 188         struct appledisplay *pdata =
 189                 container_of(work, struct appledisplay, work.work);
 190         int retval;
 191 
 192         retval = appledisplay_bl_get_brightness(pdata->bd);
 193         if (retval >= 0)
 194                 pdata->bd->props.brightness = retval;
 195 
 196         /* Poll again in about 125ms if there's still a button pressed */
 197         if (pdata->button_pressed)
 198                 schedule_delayed_work(&pdata->work, HZ / 8);
 199 }
 200 
 201 static int appledisplay_probe(struct usb_interface *iface,
 202         const struct usb_device_id *id)
 203 {
 204         struct backlight_properties props;
 205         struct appledisplay *pdata;
 206         struct usb_device *udev = interface_to_usbdev(iface);
 207         struct usb_endpoint_descriptor *endpoint;
 208         int int_in_endpointAddr = 0;
 209         int retval, brightness;
 210         char bl_name[20];
 211 
 212         /* set up the endpoint information */
 213         /* use only the first interrupt-in endpoint */
 214         retval = usb_find_int_in_endpoint(iface->cur_altsetting, &endpoint);
 215         if (retval) {
 216                 dev_err(&iface->dev, "Could not find int-in endpoint\n");
 217                 return retval;
 218         }
 219 
 220         int_in_endpointAddr = endpoint->bEndpointAddress;
 221 
 222         /* allocate memory for our device state and initialize it */
 223         pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
 224         if (!pdata) {
 225                 retval = -ENOMEM;
 226                 goto error;
 227         }
 228 
 229         pdata->udev = udev;
 230 
 231         INIT_DELAYED_WORK(&pdata->work, appledisplay_work);
 232         mutex_init(&pdata->sysfslock);
 233 
 234         /* Allocate buffer for control messages */
 235         pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
 236         if (!pdata->msgdata) {
 237                 retval = -ENOMEM;
 238                 goto error;
 239         }
 240 
 241         /* Allocate interrupt URB */
 242         pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
 243         if (!pdata->urb) {
 244                 retval = -ENOMEM;
 245                 goto error;
 246         }
 247 
 248         /* Allocate buffer for interrupt data */
 249         pdata->urbdata = usb_alloc_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
 250                 GFP_KERNEL, &pdata->urb->transfer_dma);
 251         if (!pdata->urbdata) {
 252                 retval = -ENOMEM;
 253                 dev_err(&iface->dev, "Allocating URB buffer failed\n");
 254                 goto error;
 255         }
 256 
 257         /* Configure interrupt URB */
 258         usb_fill_int_urb(pdata->urb, udev,
 259                 usb_rcvintpipe(udev, int_in_endpointAddr),
 260                 pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
 261                 pdata, 1);
 262         pdata->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
 263         if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
 264                 retval = -EIO;
 265                 dev_err(&iface->dev, "Submitting URB failed\n");
 266                 goto error;
 267         }
 268 
 269         /* Register backlight device */
 270         snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
 271                 atomic_inc_return(&count_displays) - 1);
 272         memset(&props, 0, sizeof(struct backlight_properties));
 273         props.type = BACKLIGHT_RAW;
 274         props.max_brightness = 0xff;
 275         pdata->bd = backlight_device_register(bl_name, NULL, pdata,
 276                                               &appledisplay_bl_data, &props);
 277         if (IS_ERR(pdata->bd)) {
 278                 dev_err(&iface->dev, "Backlight registration failed\n");
 279                 retval = PTR_ERR(pdata->bd);
 280                 goto error;
 281         }
 282 
 283         /* Try to get brightness */
 284         brightness = appledisplay_bl_get_brightness(pdata->bd);
 285 
 286         if (brightness < 0) {
 287                 retval = brightness;
 288                 dev_err(&iface->dev,
 289                         "Error while getting initial brightness: %d\n", retval);
 290                 goto error;
 291         }
 292 
 293         /* Set brightness in backlight device */
 294         pdata->bd->props.brightness = brightness;
 295 
 296         /* save our data pointer in the interface device */
 297         usb_set_intfdata(iface, pdata);
 298 
 299         printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n");
 300 
 301         return 0;
 302 
 303 error:
 304         if (pdata) {
 305                 if (pdata->urb) {
 306                         usb_kill_urb(pdata->urb);
 307                         cancel_delayed_work_sync(&pdata->work);
 308                         if (pdata->urbdata)
 309                                 usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
 310                                         pdata->urbdata, pdata->urb->transfer_dma);
 311                         usb_free_urb(pdata->urb);
 312                 }
 313                 if (!IS_ERR(pdata->bd))
 314                         backlight_device_unregister(pdata->bd);
 315                 kfree(pdata->msgdata);
 316         }
 317         usb_set_intfdata(iface, NULL);
 318         kfree(pdata);
 319         return retval;
 320 }
 321 
 322 static void appledisplay_disconnect(struct usb_interface *iface)
 323 {
 324         struct appledisplay *pdata = usb_get_intfdata(iface);
 325 
 326         if (pdata) {
 327                 usb_kill_urb(pdata->urb);
 328                 cancel_delayed_work_sync(&pdata->work);
 329                 backlight_device_unregister(pdata->bd);
 330                 usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
 331                         pdata->urbdata, pdata->urb->transfer_dma);
 332                 usb_free_urb(pdata->urb);
 333                 kfree(pdata->msgdata);
 334                 kfree(pdata);
 335         }
 336 
 337         printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n");
 338 }
 339 
 340 static struct usb_driver appledisplay_driver = {
 341         .name           = "appledisplay",
 342         .probe          = appledisplay_probe,
 343         .disconnect     = appledisplay_disconnect,
 344         .id_table       = appledisplay_table,
 345 };
 346 
 347 static int __init appledisplay_init(void)
 348 {
 349         return usb_register(&appledisplay_driver);
 350 }
 351 
 352 static void __exit appledisplay_exit(void)
 353 {
 354         usb_deregister(&appledisplay_driver);
 355 }
 356 
 357 MODULE_AUTHOR("Michael Hanselmann");
 358 MODULE_DESCRIPTION("Apple Cinema Display driver");
 359 MODULE_LICENSE("GPL");
 360 
 361 module_init(appledisplay_init);
 362 module_exit(appledisplay_exit);

/* [<][>][^][v][top][bottom][index][help] */