1 /* 2 * socket_sysfs.c -- most of socket-related sysfs output 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * (C) 2003 - 2004 Dominik Brodowski 9 */ 10 11 #include <linux/module.h> 12 #include <linux/moduleparam.h> 13 #include <linux/init.h> 14 #include <linux/kernel.h> 15 #include <linux/string.h> 16 #include <linux/major.h> 17 #include <linux/errno.h> 18 #include <linux/slab.h> 19 #include <linux/mm.h> 20 #include <linux/interrupt.h> 21 #include <linux/timer.h> 22 #include <linux/ioport.h> 23 #include <linux/delay.h> 24 #include <linux/pm.h> 25 #include <linux/device.h> 26 #include <linux/mutex.h> 27 #include <asm/system.h> 28 #include <asm/irq.h> 29 30 #include <pcmcia/cs_types.h> 31 #include <pcmcia/ss.h> 32 #include <pcmcia/cs.h> 33 #include <pcmcia/cistpl.h> 34 #include <pcmcia/cisreg.h> 35 #include <pcmcia/ds.h> 36 #include "cs_internal.h" 37 38 #define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev) 39 40 static ssize_t pccard_show_type(struct device *dev, struct device_attribute *attr, 41 char *buf) 42 { 43 struct pcmcia_socket *s = to_socket(dev); 44 45 if (!(s->state & SOCKET_PRESENT)) 46 return -ENODEV; 47 if (s->state & SOCKET_CARDBUS) 48 return sprintf(buf, "32-bit\n"); 49 return sprintf(buf, "16-bit\n"); 50 } 51 static DEVICE_ATTR(card_type, 0444, pccard_show_type, NULL); 52 53 static ssize_t pccard_show_voltage(struct device *dev, struct device_attribute *attr, 54 char *buf) 55 { 56 struct pcmcia_socket *s = to_socket(dev); 57 58 if (!(s->state & SOCKET_PRESENT)) 59 return -ENODEV; 60 if (s->socket.Vcc) 61 return sprintf(buf, "%d.%dV\n", s->socket.Vcc / 10, 62 s->socket.Vcc % 10); 63 return sprintf(buf, "X.XV\n"); 64 } 65 static DEVICE_ATTR(card_voltage, 0444, pccard_show_voltage, NULL); 66 67 static ssize_t pccard_show_vpp(struct device *dev, struct device_attribute *attr, 68 char *buf) 69 { 70 struct pcmcia_socket *s = to_socket(dev); 71 if (!(s->state & SOCKET_PRESENT)) 72 return -ENODEV; 73 return sprintf(buf, "%d.%dV\n", s->socket.Vpp / 10, s->socket.Vpp % 10); 74 } 75 static DEVICE_ATTR(card_vpp, 0444, pccard_show_vpp, NULL); 76 77 static ssize_t pccard_show_vcc(struct device *dev, struct device_attribute *attr, 78 char *buf) 79 { 80 struct pcmcia_socket *s = to_socket(dev); 81 if (!(s->state & SOCKET_PRESENT)) 82 return -ENODEV; 83 return sprintf(buf, "%d.%dV\n", s->socket.Vcc / 10, s->socket.Vcc % 10); 84 } 85 static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL); 86 87 88 static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr, 89 const char *buf, size_t count) 90 { 91 ssize_t ret; 92 struct pcmcia_socket *s = to_socket(dev); 93 94 if (!count) 95 return -EINVAL; 96 97 ret = pcmcia_insert_card(s); 98 99 return ret ? ret : count; 100 } 101 static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert); 102 103 104 static ssize_t pccard_show_card_pm_state(struct device *dev, 105 struct device_attribute *attr, 106 char *buf) 107 { 108 struct pcmcia_socket *s = to_socket(dev); 109 return sprintf(buf, "%s\n", s->state & SOCKET_SUSPEND ? "off" : "on"); 110 } 111 112 static ssize_t pccard_store_card_pm_state(struct device *dev, 113 struct device_attribute *attr, 114 const char *buf, size_t count) 115 { 116 ssize_t ret = -EINVAL; 117 struct pcmcia_socket *s = to_socket(dev); 118 119 if (!count) 120 return -EINVAL; 121 122 if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3)) 123 ret = pcmcia_suspend_card(s); 124 else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2)) 125 ret = pcmcia_resume_card(s); 126 127 return ret ? -ENODEV : count; 128 } 129 static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state); 130 131 static ssize_t pccard_store_eject(struct device *dev, 132 struct device_attribute *attr, 133 const char *buf, size_t count) 134 { 135 ssize_t ret; 136 struct pcmcia_socket *s = to_socket(dev); 137 138 if (!count) 139 return -EINVAL; 140 141 ret = pcmcia_eject_card(s); 142 143 return ret ? ret : count; 144 } 145 static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject); 146 147 148 static ssize_t pccard_show_irq_mask(struct device *dev, 149 struct device_attribute *attr, 150 char *buf) 151 { 152 struct pcmcia_socket *s = to_socket(dev); 153 return sprintf(buf, "0x%04x\n", s->irq_mask); 154 } 155 156 static ssize_t pccard_store_irq_mask(struct device *dev, 157 struct device_attribute *attr, 158 const char *buf, size_t count) 159 { 160 ssize_t ret; 161 struct pcmcia_socket *s = to_socket(dev); 162 u32 mask; 163 164 if (!count) 165 return -EINVAL; 166 167 ret = sscanf (buf, "0x%x\n", &mask); 168 169 if (ret == 1) { 170 s->irq_mask &= mask; 171 ret = 0; 172 } 173 174 return ret ? ret : count; 175 } 176 static DEVICE_ATTR(card_irq_mask, 0600, pccard_show_irq_mask, pccard_store_irq_mask); 177 178 179 static ssize_t pccard_show_resource(struct device *dev, 180 struct device_attribute *attr, char *buf) 181 { 182 struct pcmcia_socket *s = to_socket(dev); 183 return sprintf(buf, "%s\n", s->resource_setup_done ? "yes" : "no"); 184 } 185 186 static ssize_t pccard_store_resource(struct device *dev, 187 struct device_attribute *attr, 188 const char *buf, size_t count) 189 { 190 unsigned long flags; 191 struct pcmcia_socket *s = to_socket(dev); 192 193 if (!count) 194 return -EINVAL; 195 196 spin_lock_irqsave(&s->lock, flags); 197 if (!s->resource_setup_done) 198 s->resource_setup_done = 1; 199 spin_unlock_irqrestore(&s->lock, flags); 200 201 mutex_lock(&s->skt_mutex); 202 if ((s->callback) && 203 (s->state & SOCKET_PRESENT) && 204 !(s->state & SOCKET_CARDBUS)) { 205 if (try_module_get(s->callback->owner)) { 206 s->callback->requery(s, 0); 207 module_put(s->callback->owner); 208 } 209 } 210 mutex_unlock(&s->skt_mutex); 211 212 return count; 213 } 214 static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); 215 216 217 static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count) 218 { 219 tuple_t tuple; 220 int status, i; 221 loff_t pointer = 0; 222 ssize_t ret = 0; 223 u_char *tuplebuffer; 224 u_char *tempbuffer; 225 226 tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); 227 if (!tuplebuffer) 228 return -ENOMEM; 229 230 tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); 231 if (!tempbuffer) { 232 ret = -ENOMEM; 233 goto free_tuple; 234 } 235 236 memset(&tuple, 0, sizeof(tuple_t)); 237 238 tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; 239 tuple.DesiredTuple = RETURN_FIRST_TUPLE; 240 tuple.TupleOffset = 0; 241 242 status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); 243 while (!status) { 244 tuple.TupleData = tuplebuffer; 245 tuple.TupleDataMax = 255; 246 memset(tuplebuffer, 0, sizeof(u_char) * 255); 247 248 status = pccard_get_tuple_data(s, &tuple); 249 if (status) 250 break; 251 252 if (off < (pointer + 2 + tuple.TupleDataLen)) { 253 tempbuffer[0] = tuple.TupleCode & 0xff; 254 tempbuffer[1] = tuple.TupleLink & 0xff; 255 for (i = 0; i < tuple.TupleDataLen; i++) 256 tempbuffer[i + 2] = tuplebuffer[i] & 0xff; 257 258 for (i = 0; i < (2 + tuple.TupleDataLen); i++) { 259 if (((i + pointer) >= off) && 260 (i + pointer) < (off + count)) { 261 buf[ret] = tempbuffer[i]; 262 ret++; 263 } 264 } 265 } 266 267 pointer += 2 + tuple.TupleDataLen; 268 269 if (pointer >= (off + count)) 270 break; 271 272 if (tuple.TupleCode == CISTPL_END) 273 break; 274 status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); 275 } 276 277 kfree(tempbuffer); 278 free_tuple: 279 kfree(tuplebuffer); 280 281 return (ret); 282 } 283 284 static ssize_t pccard_show_cis(struct kobject *kobj, 285 struct bin_attribute *bin_attr, 286 char *buf, loff_t off, size_t count) 287 { 288 unsigned int size = 0x200; 289 290 if (off >= size) 291 count = 0; 292 else { 293 struct pcmcia_socket *s; 294 unsigned int chains; 295 296 if (off + count > size) 297 count = size - off; 298 299 s = to_socket(container_of(kobj, struct device, kobj)); 300 301 if (!(s->state & SOCKET_PRESENT)) 302 return -ENODEV; 303 if (pccard_validate_cis(s, &chains)) 304 return -EIO; 305 if (!chains) 306 return -ENODATA; 307 308 count = pccard_extract_cis(s, buf, off, count); 309 } 310 311 return (count); 312 } 313 314 static ssize_t pccard_store_cis(struct kobject *kobj, 315 struct bin_attribute *bin_attr, 316 char *buf, loff_t off, size_t count) 317 { 318 struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj)); 319 int error; 320 321 if (off) 322 return -EINVAL; 323 324 if (count >= CISTPL_MAX_CIS_SIZE) 325 return -EINVAL; 326 327 if (!(s->state & SOCKET_PRESENT)) 328 return -ENODEV; 329 330 error = pcmcia_replace_cis(s, buf, count); 331 if (error) 332 return -EIO; 333 334 mutex_lock(&s->skt_mutex); 335 if ((s->callback) && (s->state & SOCKET_PRESENT) && 336 !(s->state & SOCKET_CARDBUS)) { 337 if (try_module_get(s->callback->owner)) { 338 s->callback->requery(s, 1); 339 module_put(s->callback->owner); 340 } 341 } 342 mutex_unlock(&s->skt_mutex); 343 344 return count; 345 } 346 347 348 static struct attribute *pccard_socket_attributes[] = { 349 &dev_attr_card_type.attr, 350 &dev_attr_card_voltage.attr, 351 &dev_attr_card_vpp.attr, 352 &dev_attr_card_vcc.attr, 353 &dev_attr_card_insert.attr, 354 &dev_attr_card_pm_state.attr, 355 &dev_attr_card_eject.attr, 356 &dev_attr_card_irq_mask.attr, 357 &dev_attr_available_resources_setup_done.attr, 358 NULL, 359 }; 360 361 static const struct attribute_group socket_attrs = { 362 .attrs = pccard_socket_attributes, 363 }; 364 365 static struct bin_attribute pccard_cis_attr = { 366 .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, 367 .size = 0x200, 368 .read = pccard_show_cis, 369 .write = pccard_store_cis, 370 }; 371 372 int pccard_sysfs_add_socket(struct device *dev) 373 { 374 int ret = 0; 375 376 ret = sysfs_create_group(&dev->kobj, &socket_attrs); 377 if (!ret) { 378 ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr); 379 if (ret) 380 sysfs_remove_group(&dev->kobj, &socket_attrs); 381 } 382 return ret; 383 } 384 385 void pccard_sysfs_remove_socket(struct device *dev) 386 { 387 sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr); 388 sysfs_remove_group(&dev->kobj, &socket_attrs); 389 } 390