1 // SPDX-License-Identifier: GPL-2.0 2 /* -*- linux-c -*- 3 * Cypress USB Thermometer driver 4 * 5 * Copyright (c) 2004 Erik Rigtorp <erkki@linux.nu> <erik@rigtorp.com> 6 * 7 * This driver works with Elektor magazine USB Interface as published in 8 * issue #291. It should also work with the original starter kit/demo board 9 * from Cypress. 10 */ 11 12 13 #include <linux/kernel.h> 14 #include <linux/errno.h> 15 #include <linux/slab.h> 16 #include <linux/module.h> 17 #include <linux/usb.h> 18 19 #define DRIVER_AUTHOR "Erik Rigtorp" 20 #define DRIVER_DESC "Cypress USB Thermometer driver" 21 22 #define USB_SKEL_VENDOR_ID 0x04b4 23 #define USB_SKEL_PRODUCT_ID 0x0002 24 25 static const struct usb_device_id id_table[] = { 26 { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, 27 { } 28 }; 29 MODULE_DEVICE_TABLE (usb, id_table); 30 31 /* Structure to hold all of our device specific stuff */ 32 struct usb_cytherm { 33 struct usb_device *udev; /* save off the usb device pointer */ 34 struct usb_interface *interface; /* the interface for this device */ 35 int brightness; 36 }; 37 38 39 /* local function prototypes */ 40 static int cytherm_probe(struct usb_interface *interface, 41 const struct usb_device_id *id); 42 static void cytherm_disconnect(struct usb_interface *interface); 43 44 45 /* usb specific object needed to register this driver with the usb subsystem */ 46 static struct usb_driver cytherm_driver = { 47 .name = "cytherm", 48 .probe = cytherm_probe, 49 .disconnect = cytherm_disconnect, 50 .id_table = id_table, 51 }; 52 53 /* Vendor requests */ 54 /* They all operate on one byte at a time */ 55 #define PING 0x00 56 #define READ_ROM 0x01 /* Reads form ROM, value = address */ 57 #define READ_RAM 0x02 /* Reads form RAM, value = address */ 58 #define WRITE_RAM 0x03 /* Write to RAM, value = address, index = data */ 59 #define READ_PORT 0x04 /* Reads from port, value = address */ 60 #define WRITE_PORT 0x05 /* Write to port, value = address, index = data */ 61 62 63 /* Send a vendor command to device */ 64 static int vendor_command(struct usb_device *dev, unsigned char request, 65 unsigned char value, unsigned char index, 66 void *buf, int size) 67 { 68 return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 69 request, 70 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, 71 value, 72 index, buf, size, 73 USB_CTRL_GET_TIMEOUT); 74 } 75 76 77 78 #define BRIGHTNESS 0x2c /* RAM location for brightness value */ 79 #define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */ 80 81 static ssize_t show_brightness(struct device *dev, struct device_attribute *attr, char *buf) 82 { 83 struct usb_interface *intf = to_usb_interface(dev); 84 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 85 86 return sprintf(buf, "%i", cytherm->brightness); 87 } 88 89 static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, const char *buf, 90 size_t count) 91 { 92 struct usb_interface *intf = to_usb_interface(dev); 93 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 94 95 unsigned char *buffer; 96 int retval; 97 98 buffer = kmalloc(8, GFP_KERNEL); 99 if (!buffer) 100 return 0; 101 102 cytherm->brightness = simple_strtoul(buf, NULL, 10); 103 104 if (cytherm->brightness > 0xFF) 105 cytherm->brightness = 0xFF; 106 else if (cytherm->brightness < 0) 107 cytherm->brightness = 0; 108 109 /* Set brightness */ 110 retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS, 111 cytherm->brightness, buffer, 8); 112 if (retval) 113 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 114 /* Inform µC that we have changed the brightness setting */ 115 retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS_SEM, 116 0x01, buffer, 8); 117 if (retval) 118 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 119 120 kfree(buffer); 121 122 return count; 123 } 124 125 static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP, 126 show_brightness, set_brightness); 127 128 129 #define TEMP 0x33 /* RAM location for temperature */ 130 #define SIGN 0x34 /* RAM location for temperature sign */ 131 132 static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) 133 { 134 135 struct usb_interface *intf = to_usb_interface(dev); 136 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 137 138 int retval; 139 unsigned char *buffer; 140 141 int temp, sign; 142 143 buffer = kmalloc(8, GFP_KERNEL); 144 if (!buffer) 145 return 0; 146 147 /* read temperature */ 148 retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8); 149 if (retval) 150 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 151 temp = buffer[1]; 152 153 /* read sign */ 154 retval = vendor_command(cytherm->udev, READ_RAM, SIGN, 0, buffer, 8); 155 if (retval) 156 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 157 sign = buffer[1]; 158 159 kfree(buffer); 160 161 return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1, 162 5*(temp - ((temp >> 1) << 1))); 163 } 164 165 166 static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 167 { 168 return count; 169 } 170 171 static DEVICE_ATTR(temp, S_IRUGO, show_temp, set_temp); 172 173 174 #define BUTTON 0x7a 175 176 static ssize_t show_button(struct device *dev, struct device_attribute *attr, char *buf) 177 { 178 179 struct usb_interface *intf = to_usb_interface(dev); 180 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 181 182 int retval; 183 unsigned char *buffer; 184 185 buffer = kmalloc(8, GFP_KERNEL); 186 if (!buffer) 187 return 0; 188 189 /* check button */ 190 retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8); 191 if (retval) 192 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 193 194 retval = buffer[1]; 195 196 kfree(buffer); 197 198 if (retval) 199 return sprintf(buf, "1"); 200 else 201 return sprintf(buf, "0"); 202 } 203 204 205 static ssize_t set_button(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 206 { 207 return count; 208 } 209 210 static DEVICE_ATTR(button, S_IRUGO, show_button, set_button); 211 212 213 static ssize_t show_port0(struct device *dev, struct device_attribute *attr, char *buf) 214 { 215 struct usb_interface *intf = to_usb_interface(dev); 216 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 217 218 int retval; 219 unsigned char *buffer; 220 221 buffer = kmalloc(8, GFP_KERNEL); 222 if (!buffer) 223 return 0; 224 225 retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8); 226 if (retval) 227 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 228 229 retval = buffer[1]; 230 231 kfree(buffer); 232 233 return sprintf(buf, "%d", retval); 234 } 235 236 237 static ssize_t set_port0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 238 { 239 struct usb_interface *intf = to_usb_interface(dev); 240 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 241 242 unsigned char *buffer; 243 int retval; 244 int tmp; 245 246 buffer = kmalloc(8, GFP_KERNEL); 247 if (!buffer) 248 return 0; 249 250 tmp = simple_strtoul(buf, NULL, 10); 251 252 if (tmp > 0xFF) 253 tmp = 0xFF; 254 else if (tmp < 0) 255 tmp = 0; 256 257 retval = vendor_command(cytherm->udev, WRITE_PORT, 0, 258 tmp, buffer, 8); 259 if (retval) 260 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 261 262 kfree(buffer); 263 264 return count; 265 } 266 267 static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR | S_IWGRP, show_port0, set_port0); 268 269 static ssize_t show_port1(struct device *dev, struct device_attribute *attr, char *buf) 270 { 271 struct usb_interface *intf = to_usb_interface(dev); 272 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 273 274 int retval; 275 unsigned char *buffer; 276 277 buffer = kmalloc(8, GFP_KERNEL); 278 if (!buffer) 279 return 0; 280 281 retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8); 282 if (retval) 283 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 284 285 retval = buffer[1]; 286 287 kfree(buffer); 288 289 return sprintf(buf, "%d", retval); 290 } 291 292 293 static ssize_t set_port1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 294 { 295 struct usb_interface *intf = to_usb_interface(dev); 296 struct usb_cytherm *cytherm = usb_get_intfdata(intf); 297 298 unsigned char *buffer; 299 int retval; 300 int tmp; 301 302 buffer = kmalloc(8, GFP_KERNEL); 303 if (!buffer) 304 return 0; 305 306 tmp = simple_strtoul(buf, NULL, 10); 307 308 if (tmp > 0xFF) 309 tmp = 0xFF; 310 else if (tmp < 0) 311 tmp = 0; 312 313 retval = vendor_command(cytherm->udev, WRITE_PORT, 1, 314 tmp, buffer, 8); 315 if (retval) 316 dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval); 317 318 kfree(buffer); 319 320 return count; 321 } 322 323 static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR | S_IWGRP, show_port1, set_port1); 324 325 326 327 static int cytherm_probe(struct usb_interface *interface, 328 const struct usb_device_id *id) 329 { 330 struct usb_device *udev = interface_to_usbdev(interface); 331 struct usb_cytherm *dev = NULL; 332 int retval = -ENOMEM; 333 334 dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL); 335 if (!dev) 336 goto error_mem; 337 338 dev->udev = usb_get_dev(udev); 339 340 usb_set_intfdata (interface, dev); 341 342 dev->brightness = 0xFF; 343 344 retval = device_create_file(&interface->dev, &dev_attr_brightness); 345 if (retval) 346 goto error; 347 retval = device_create_file(&interface->dev, &dev_attr_temp); 348 if (retval) 349 goto error; 350 retval = device_create_file(&interface->dev, &dev_attr_button); 351 if (retval) 352 goto error; 353 retval = device_create_file(&interface->dev, &dev_attr_port0); 354 if (retval) 355 goto error; 356 retval = device_create_file(&interface->dev, &dev_attr_port1); 357 if (retval) 358 goto error; 359 360 dev_info (&interface->dev, 361 "Cypress thermometer device now attached\n"); 362 return 0; 363 error: 364 device_remove_file(&interface->dev, &dev_attr_brightness); 365 device_remove_file(&interface->dev, &dev_attr_temp); 366 device_remove_file(&interface->dev, &dev_attr_button); 367 device_remove_file(&interface->dev, &dev_attr_port0); 368 device_remove_file(&interface->dev, &dev_attr_port1); 369 usb_set_intfdata (interface, NULL); 370 usb_put_dev(dev->udev); 371 kfree(dev); 372 error_mem: 373 return retval; 374 } 375 376 static void cytherm_disconnect(struct usb_interface *interface) 377 { 378 struct usb_cytherm *dev; 379 380 dev = usb_get_intfdata (interface); 381 382 device_remove_file(&interface->dev, &dev_attr_brightness); 383 device_remove_file(&interface->dev, &dev_attr_temp); 384 device_remove_file(&interface->dev, &dev_attr_button); 385 device_remove_file(&interface->dev, &dev_attr_port0); 386 device_remove_file(&interface->dev, &dev_attr_port1); 387 388 /* first remove the files, then NULL the pointer */ 389 usb_set_intfdata (interface, NULL); 390 391 usb_put_dev(dev->udev); 392 393 kfree(dev); 394 395 dev_info(&interface->dev, "Cypress thermometer now disconnected\n"); 396 } 397 398 module_usb_driver(cytherm_driver); 399 400 MODULE_AUTHOR(DRIVER_AUTHOR); 401 MODULE_DESCRIPTION(DRIVER_DESC); 402 MODULE_LICENSE("GPL"); 403