diff options
Diffstat (limited to 'drivers/w1')
33 files changed, 2865 insertions, 1190 deletions
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index fd2c7bd9dfb..6743bde038c 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -16,7 +16,7 @@ config W1_CON depends on CONNECTOR bool "Userspace communication over connector" default y - --- help --- + ---help--- This allows to communicate with userspace using connector. For more information see <file:Documentation/connector/connector.txt>. There are three types of messages between w1 core and userspace: diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 979d6eed9a0..1708b2300c7 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -26,7 +26,7 @@ config W1_MASTER_DS2490 config W1_MASTER_DS2482 tristate "Maxim DS2482 I2C to 1-Wire bridge" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Maxim DS2482 I2C to 1-Wire bridge. @@ -36,13 +36,12 @@ config W1_MASTER_DS2482 config W1_MASTER_MXC tristate "Freescale MXC 1-wire busmaster" - depends on W1 && ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST help Say Y here to enable MXC 1-wire host config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" - depends on W1 && GENERIC_HARDIRQS help Say Y here to enable the DS1WM 1-wire driver, such as that in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like @@ -50,7 +49,7 @@ config W1_MASTER_DS1WM config W1_MASTER_GPIO tristate "GPIO 1-wire busmaster" - depends on GENERIC_GPIO + depends on GPIOLIB help Say Y here if you want to communicate with your 1-wire devices using GPIO pins. This driver uses the GPIO API to control the wire. @@ -60,7 +59,7 @@ config W1_MASTER_GPIO config HDQ_MASTER_OMAP tristate "OMAP HDQ driver" - depends on SOC_OMAP2430 || ARCH_OMAP3 + depends on ARCH_OMAP help Say Y here if you want support for the 1-wire or HDQ Interface on an OMAP processor. diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index a0c8965c1a7..02df3b1381d 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/irq.h> #include <linux/pm.h> #include <linux/platform_device.h> @@ -254,17 +255,17 @@ static int ds1wm_find_divisor(int gclk) static void ds1wm_up(struct ds1wm_data *ds1wm_data) { int divisor; - struct ds1wm_driver_data *plat = ds1wm_data->pdev->dev.platform_data; + struct device *dev = &ds1wm_data->pdev->dev; + struct ds1wm_driver_data *plat = dev_get_platdata(dev); if (ds1wm_data->cell->enable) ds1wm_data->cell->enable(ds1wm_data->pdev); divisor = ds1wm_find_divisor(plat->clock_rate); - dev_dbg(&ds1wm_data->pdev->dev, - "found divisor 0x%x for clock %d\n", divisor, plat->clock_rate); + dev_dbg(dev, "found divisor 0x%x for clock %d\n", + divisor, plat->clock_rate); if (divisor == 0) { - dev_err(&ds1wm_data->pdev->dev, - "no suitable divisor for %dHz clock\n", + dev_err(dev, "no suitable divisor for %dHz clock\n", plat->clock_rate); return; } @@ -334,7 +335,9 @@ static void ds1wm_search(void *data, struct w1_master *master_dev, return; } + mutex_lock(&master_dev->bus_mutex); if (ds1wm_reset(ds1wm_data)) { + mutex_unlock(&master_dev->bus_mutex); dev_dbg(&ds1wm_data->pdev->dev, "pass: %d reset error (or no slaves)\n", pass); break; @@ -347,7 +350,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev, "pass: %d entering ASM\n", pass); ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); dev_dbg(&ds1wm_data->pdev->dev, - "pass: %d begining nibble loop\n", pass); + "pass: %d beginning nibble loop\n", pass); r_prime = 0; d = 0; @@ -387,6 +390,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev, } if (ds1wm_data->read_error) { + mutex_unlock(&master_dev->bus_mutex); dev_err(&ds1wm_data->pdev->dev, "pass: %d read error, retrying\n", pass); break; @@ -400,6 +404,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev, dev_dbg(&ds1wm_data->pdev->dev, "pass: %d resetting bus\n", pass); ds1wm_reset(ds1wm_data); + mutex_unlock(&master_dev->bus_mutex); if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) { dev_err(&ds1wm_data->pdev->dev, "pass: %d bus error, retrying\n", pass); @@ -455,43 +460,34 @@ static int ds1wm_probe(struct platform_device *pdev) if (!pdev) return -ENODEV; - ds1wm_data = kzalloc(sizeof(*ds1wm_data), GFP_KERNEL); + ds1wm_data = devm_kzalloc(&pdev->dev, sizeof(*ds1wm_data), GFP_KERNEL); if (!ds1wm_data) return -ENOMEM; platform_set_drvdata(pdev, ds1wm_data); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENXIO; - goto err0; - } - ds1wm_data->map = ioremap(res->start, resource_size(res)); - if (!ds1wm_data->map) { - ret = -ENOMEM; - goto err0; - } + if (!res) + return -ENXIO; + ds1wm_data->map = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!ds1wm_data->map) + return -ENOMEM; /* calculate bus shift from mem resource */ ds1wm_data->bus_shift = resource_size(res) >> 3; ds1wm_data->pdev = pdev; ds1wm_data->cell = mfd_get_cell(pdev); - if (!ds1wm_data->cell) { - ret = -ENODEV; - goto err1; - } - plat = pdev->dev.platform_data; - if (!plat) { - ret = -ENODEV; - goto err1; - } + if (!ds1wm_data->cell) + return -ENODEV; + plat = dev_get_platdata(&pdev->dev); + if (!plat) + return -ENODEV; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - ret = -ENXIO; - goto err1; - } + if (!res) + return -ENXIO; ds1wm_data->irq = res->start; ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0); ds1wm_data->reset_recover_delay = plat->reset_recover_delay; @@ -501,10 +497,10 @@ static int ds1wm_probe(struct platform_device *pdev) if (res->flags & IORESOURCE_IRQ_LOWEDGE) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); - ret = request_irq(ds1wm_data->irq, ds1wm_isr, - IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data); + ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, + IRQF_SHARED, "ds1wm", ds1wm_data); if (ret) - goto err1; + return ret; ds1wm_up(ds1wm_data); @@ -512,17 +508,12 @@ static int ds1wm_probe(struct platform_device *pdev) ret = w1_add_master_device(&ds1wm_master); if (ret) - goto err2; + goto err; return 0; -err2: +err: ds1wm_down(ds1wm_data); - free_irq(ds1wm_data->irq, ds1wm_data); -err1: - iounmap(ds1wm_data->map); -err0: - kfree(ds1wm_data); return ret; } @@ -556,9 +547,6 @@ static int ds1wm_remove(struct platform_device *pdev) w1_remove_master_device(&ds1wm_master); ds1wm_down(ds1wm_data); - free_irq(ds1wm_data->irq, ds1wm_data); - iounmap(ds1wm_data->map); - kfree(ds1wm_data); return 0; } diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e5f74416d4b..e033491fe30 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -51,10 +51,10 @@ * The top 4 bits always read 0. * To write, the top nibble must be the 1's compl. of the low nibble. */ -#define DS2482_REG_CFG_1WS 0x08 -#define DS2482_REG_CFG_SPU 0x04 -#define DS2482_REG_CFG_PPM 0x02 -#define DS2482_REG_CFG_APU 0x01 +#define DS2482_REG_CFG_1WS 0x08 /* 1-wire speed */ +#define DS2482_REG_CFG_SPU 0x04 /* strong pull-up */ +#define DS2482_REG_CFG_PPM 0x02 /* presence pulse masking */ +#define DS2482_REG_CFG_APU 0x01 /* active pull-up */ /** @@ -132,6 +132,17 @@ struct ds2482_data { /** + * Helper to calculate values for configuration register + * @param conf the raw config value + * @return the value w/ complements that can be written to register + */ +static inline u8 ds2482_calculate_config(u8 conf) +{ + return conf | ((~conf & 0x0f) << 4); +} + + +/** * Sets the read pointer. * @param pdev The ds2482 client pointer * @param read_ptr see DS2482_PTR_CODE_xxx above @@ -399,7 +410,7 @@ static u8 ds2482_w1_reset_bus(void *data) /* If the chip did reset since detect, re-config it */ if (err & DS2482_REG_STS_RST) ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, - 0xF0); + ds2482_calculate_config(0x00)); } mutex_unlock(&pdev->access_lock); @@ -407,6 +418,32 @@ static u8 ds2482_w1_reset_bus(void *data) return retval; } +static u8 ds2482_w1_set_pullup(void *data, int delay) +{ + struct ds2482_w1_chan *pchan = data; + struct ds2482_data *pdev = pchan->pdev; + u8 retval = 1; + + /* if delay is non-zero activate the pullup, + * the strong pullup will be automatically deactivated + * by the master, so do not explicitly deactive it + */ + if (delay) { + /* both waits are crucial, otherwise devices might not be + * powered long enough, causing e.g. a w1_therm sensor to + * provide wrong conversion results + */ + ds2482_wait_1wire_idle(pdev); + /* note: it seems like both SPU and APU have to be set! */ + retval = ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, + ds2482_calculate_config(DS2482_REG_CFG_SPU | + DS2482_REG_CFG_APU)); + ds2482_wait_1wire_idle(pdev); + } + + return retval; +} + static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -452,7 +489,8 @@ static int ds2482_probe(struct i2c_client *client, data->w1_count = 8; /* Set all config items to 0 (off) */ - ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, 0xF0); + ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, + ds2482_calculate_config(0x00)); mutex_init(&data->access_lock); @@ -468,6 +506,7 @@ static int ds2482_probe(struct i2c_client *client, data->w1_ch[idx].w1_bm.touch_bit = ds2482_w1_touch_bit; data->w1_ch[idx].w1_bm.triplet = ds2482_w1_triplet; data->w1_ch[idx].w1_bm.reset_bus = ds2482_w1_reset_bus; + data->w1_ch[idx].w1_bm.set_pullup = ds2482_w1_set_pullup; err = w1_add_master_device(&data->w1_ch[idx].w1_bm); if (err) { @@ -505,19 +544,8 @@ static int ds2482_remove(struct i2c_client *client) return 0; } -static int __init sensors_ds2482_init(void) -{ - return i2c_add_driver(&ds2482_driver); -} - -static void __exit sensors_ds2482_exit(void) -{ - i2c_del_driver(&ds2482_driver); -} +module_i2c_driver(ds2482_driver); MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); MODULE_DESCRIPTION("DS2482 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_ds2482_init); -module_exit(sensors_ds2482_exit); diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 4f7e1d770f8..7404ad3062b 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -1,5 +1,5 @@ /* - * dscore.c + * ds2490.c USB to one wire bridge * * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> * @@ -28,6 +28,10 @@ #include "../w1_int.h" #include "../w1.h" +/* USB Standard */ +/* USB Control request vendor type */ +#define VENDOR 0x40 + /* COMMAND TYPE CODES */ #define CONTROL_CMD 0x00 #define COMM_CMD 0x01 @@ -107,6 +111,8 @@ #define ST_HALT 0x10 /* DS2490 is currently halted */ #define ST_IDLE 0x20 /* DS2490 is currently idle */ #define ST_EPOF 0x80 +/* Status transfer size, 16 bytes status, 16 byte result flags */ +#define ST_SIZE 0x20 /* Result Register flags */ #define RR_DETECT 0xA5 /* New device detected */ @@ -198,7 +204,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) int err; err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), - CONTROL_CMD, 0x40, value, index, NULL, 0, 1000); + CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000); if (err < 0) { printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n", value, index, err); @@ -213,7 +219,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) int err; err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), - MODE_CMD, 0x40, value, index, NULL, 0, 1000); + MODE_CMD, VENDOR, value, index, NULL, 0, 1000); if (err < 0) { printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n", value, index, err); @@ -228,7 +234,7 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) int err; err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), - COMM_CMD, 0x40, value, index, NULL, 0, 1000); + COMM_CMD, VENDOR, value, index, NULL, 0, 1000); if (err < 0) { printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n", value, index, err); @@ -246,7 +252,8 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, memset(st, 0, sizeof(*st)); count = 0; - err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_STATUS]), buf, size, &count, 100); + err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, + dev->ep[EP_STATUS]), buf, size, &count, 100); if (err < 0) { printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err); return err; @@ -353,7 +360,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), buf, size, &count, 1000); if (err < 0) { - u8 buf[0x20]; + u8 buf[ST_SIZE]; int count; printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); @@ -398,7 +405,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit) { struct ds_status st; int count = 0, err = 0; - u8 buf[0x20]; + u8 buf[ST_SIZE]; do { err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); @@ -450,10 +457,11 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) static int ds_wait_status(struct ds_device *dev, struct ds_status *st) { - u8 buf[0x20]; + u8 buf[ST_SIZE]; int err, count = 0; do { + st->status = 0; err = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); #if 0 if (err >= 0) { @@ -464,7 +472,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) printk("\n"); } #endif - } while (!(buf[0x08] & ST_IDLE) && !(err < 0) && ++count < 100); + } while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100); if (err >= 16 && st->status & ST_EPOF) { printk(KERN_INFO "Resetting device after ST_EPOF.\n"); @@ -690,37 +698,106 @@ static int ds_write_block(struct ds_device *dev, u8 *buf, int len) return !(err == len); } -#if 0 - -static int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search) +static void ds9490r_search(void *data, struct w1_master *master, + u8 search_type, w1_slave_found_callback callback) { + /* When starting with an existing id, the first id returned will + * be that device (if it is still on the bus most likely). + * + * If the number of devices found is less than or equal to the + * search_limit, that number of IDs will be returned. If there are + * more, search_limit IDs will be returned followed by a non-zero + * discrepency value. + */ + struct ds_device *dev = data; int err; u16 value, index; struct ds_status st; + u8 st_buf[ST_SIZE]; + int search_limit; + int found = 0; + int i; - memset(buf, 0, sizeof(buf)); + /* DS18b20 spec, 13.16 ms per device, 75 per second, sleep for + * discovering 8 devices (1 bulk transfer and 1/2 FIFO size) at a time. + */ + const unsigned long jtime = msecs_to_jiffies(1000*8/75); + /* FIFO 128 bytes, bulk packet size 64, read a multiple of the + * packet size. + */ + u64 buf[2*64/8]; - err = ds_send_data(ds_dev, (unsigned char *)&init, 8); - if (err) - return err; + mutex_lock(&master->bus_mutex); - ds_wait_status(ds_dev, &st); + /* address to start searching at */ + if (ds_send_data(dev, (u8 *)&master->search_id, 8) < 0) + goto search_out; + master->search_id = 0; - value = COMM_SEARCH_ACCESS | COMM_IM | COMM_SM | COMM_F | COMM_RTS; - index = (conditional_search ? 0xEC : 0xF0) | (id_number << 8); - err = ds_send_control(ds_dev, value, index); - if (err) - return err; + value = COMM_SEARCH_ACCESS | COMM_IM | COMM_RST | COMM_SM | COMM_F | + COMM_RTS; + search_limit = master->max_slave_count; + if (search_limit > 255) + search_limit = 0; + index = search_type | (search_limit << 8); + if (ds_send_control(dev, value, index) < 0) + goto search_out; - ds_wait_status(ds_dev, &st); + do { + schedule_timeout(jtime); - err = ds_recv_data(ds_dev, (unsigned char *)buf, 8*id_number); - if (err < 0) - return err; + if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < + sizeof(st)) { + break; + } - return err/8; + if (st.data_in_buffer_status) { + /* Bulk in can receive partial ids, but when it does + * they fail crc and will be discarded anyway. + * That has only been seen when status in buffer + * is 0 and bulk is read anyway, so don't read + * bulk without first checking if status says there + * is data to read. + */ + err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); + if (err < 0) + break; + for (i = 0; i < err/8; ++i) { + ++found; + if (found <= search_limit) + callback(master, buf[i]); + /* can't know if there will be a discrepancy + * value after until the next id */ + if (found == search_limit) + master->search_id = buf[i]; + } + } + + if (test_bit(W1_ABORT_SEARCH, &master->flags)) + break; + } while (!(st.status & (ST_IDLE | ST_HALT))); + + /* only continue the search if some weren't found */ + if (found <= search_limit) { + master->search_id = 0; + } else if (!test_bit(W1_WARN_MAX_COUNT, &master->flags)) { + /* Only max_slave_count will be scanned in a search, + * but it will start where it left off next search + * until all ids are identified and then it will start + * over. A continued search will report the previous + * last id as the first id (provided it is still on the + * bus). + */ + dev_info(&dev->udev->dev, "%s: max_slave_count %d reached, " + "will continue next search.\n", __func__, + master->max_slave_count); + set_bit(W1_WARN_MAX_COUNT, &master->flags); + } +search_out: + mutex_unlock(&master->bus_mutex); } +#if 0 static int ds_match_access(struct ds_device *dev, u64 init) { int err; @@ -894,6 +971,7 @@ static int ds_w1_init(struct ds_device *dev) dev->master.write_block = &ds9490r_write_block; dev->master.reset_bus = &ds9490r_reset; dev->master.set_pullup = &ds9490r_set_pullup; + dev->master.search = &ds9490r_search; return w1_add_master_device(&dev->master); } @@ -910,15 +988,13 @@ static int ds_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *endpoint; struct usb_host_interface *iface_desc; struct ds_device *dev; - int i, err; + int i, err, alt; - dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL); + dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL); if (!dev) { printk(KERN_INFO "Failed to allocate new DS9490R structure.\n"); return -ENOMEM; } - dev->spu_sleep = 0; - dev->spu_bit = 0; dev->udev = usb_get_dev(udev); if (!dev->udev) { err = -ENOMEM; @@ -928,20 +1004,25 @@ static int ds_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); - err = usb_set_interface(dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3); + err = usb_reset_configuration(dev->udev); if (err) { - printk(KERN_ERR "Failed to set alternative setting 3 for %d interface: err=%d.\n", - intf->altsetting[0].desc.bInterfaceNumber, err); + dev_err(&dev->udev->dev, + "Failed to reset configuration: err=%d.\n", err); goto err_out_clear; } - err = usb_reset_configuration(dev->udev); + /* alternative 3, 1ms interrupt (greatly speeds search), 64 byte bulk */ + alt = 3; + err = usb_set_interface(dev->udev, + intf->altsetting[alt].desc.bInterfaceNumber, alt); if (err) { - printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err); + dev_err(&dev->udev->dev, "Failed to set alternative setting %d " + "for %d interface: err=%d.\n", alt, + intf->altsetting[alt].desc.bInterfaceNumber, err); goto err_out_clear; } - iface_desc = &intf->altsetting[0]; + iface_desc = &intf->altsetting[alt]; if (iface_desc->desc.bNumEndpoints != NUM_EP-1) { printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints); err = -EINVAL; diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index f667c26b219..d8667b0212d 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -48,14 +48,14 @@ static struct pci_device_id matrox_w1_tbl[] = { }; MODULE_DEVICE_TABLE(pci, matrox_w1_tbl); -static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *); -static void __devexit matrox_w1_remove(struct pci_dev *); +static int matrox_w1_probe(struct pci_dev *, const struct pci_device_id *); +static void matrox_w1_remove(struct pci_dev *); static struct pci_driver matrox_w1_pci_driver = { .name = "matrox_w1", .id_table = matrox_w1_tbl, .probe = matrox_w1_probe, - .remove = __devexit_p(matrox_w1_remove), + .remove = matrox_w1_remove, }; /* @@ -152,7 +152,7 @@ static void matrox_w1_hw_init(struct matrox_device *dev) matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00); } -static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct matrox_device *dev; int err; @@ -220,7 +220,7 @@ err_out_free_device: return err; } -static void __devexit matrox_w1_remove(struct pci_dev *pdev) +static void matrox_w1_remove(struct pci_dev *pdev) { struct matrox_device *dev = pci_get_drvdata(pdev); diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index a3b6a74c67a..a5df5e89d45 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -10,24 +10,16 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * */ -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> #include <linux/clk.h> -#include <linux/slab.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include "../w1.h" #include "../w1_int.h" -#include "../w1_log.h" /* According to the mx27 Datasheet the reset procedure should take up to about * 1350us. We set the timeout to 500*100us = 50ms for sure */ @@ -36,17 +28,16 @@ /* * MXC W1 Register offsets */ -#define MXC_W1_CONTROL 0x00 -#define MXC_W1_TIME_DIVIDER 0x02 -#define MXC_W1_RESET 0x04 -#define MXC_W1_COMMAND 0x06 -#define MXC_W1_TXRX 0x08 -#define MXC_W1_INTERRUPT 0x0A -#define MXC_W1_INTERRUPT_EN 0x0C +#define MXC_W1_CONTROL 0x00 +# define MXC_W1_CONTROL_RDST BIT(3) +# define MXC_W1_CONTROL_WR(x) BIT(5 - (x)) +# define MXC_W1_CONTROL_PST BIT(6) +# define MXC_W1_CONTROL_RPP BIT(7) +#define MXC_W1_TIME_DIVIDER 0x02 +#define MXC_W1_RESET 0x04 struct mxc_w1_device { void __iomem *regs; - unsigned int clkdiv; struct clk *clk; struct w1_bus_master bus_master; }; @@ -62,12 +53,12 @@ static u8 mxc_w1_ds2_reset_bus(void *data) unsigned int timeout_cnt = 0; struct mxc_w1_device *dev = data; - __raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); + writeb(MXC_W1_CONTROL_RPP, (dev->regs + MXC_W1_CONTROL)); while (1) { - reg_val = __raw_readb(dev->regs + MXC_W1_CONTROL); + reg_val = readb(dev->regs + MXC_W1_CONTROL); - if (((reg_val >> 7) & 0x1) == 0 || + if (!(reg_val & MXC_W1_CONTROL_RPP) || timeout_cnt > MXC_W1_RESET_TIMEOUT) break; else @@ -75,7 +66,7 @@ static u8 mxc_w1_ds2_reset_bus(void *data) udelay(100); } - return (reg_val >> 7) & 0x1; + return !(reg_val & MXC_W1_CONTROL_PST); } /* @@ -91,121 +82,100 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) * datasheet. */ - __raw_writeb((1 << (5 - bit)), ctrl_addr); + writeb(MXC_W1_CONTROL_WR(bit), ctrl_addr); while (timeout_cnt--) { - if (!((__raw_readb(ctrl_addr) >> (5 - bit)) & 0x1)) + if (!(readb(ctrl_addr) & MXC_W1_CONTROL_WR(bit))) break; udelay(1); } - return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; + return !!(readb(ctrl_addr) & MXC_W1_CONTROL_RDST); } -static int __devinit mxc_w1_probe(struct platform_device *pdev) +static int mxc_w1_probe(struct platform_device *pdev) { struct mxc_w1_device *mdev; + unsigned long clkrate; struct resource *res; - int err = 0; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; + unsigned int clkdiv; + int err; - mdev = kzalloc(sizeof(struct mxc_w1_device), GFP_KERNEL); + mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device), + GFP_KERNEL); if (!mdev) return -ENOMEM; - mdev->clk = clk_get(&pdev->dev, "owire"); - if (!mdev->clk) { - err = -ENODEV; - goto failed_clk; - } + mdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mdev->clk)) + return PTR_ERR(mdev->clk); - mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; + clkrate = clk_get_rate(mdev->clk); + if (clkrate < 10000000) + dev_warn(&pdev->dev, + "Low clock frequency causes improper function\n"); - res = request_mem_region(res->start, resource_size(res), - "mxc_w1"); - if (!res) { - err = -EBUSY; - goto failed_req; - } + clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000); + clkrate /= clkdiv; + if ((clkrate < 980000) || (clkrate > 1020000)) + dev_warn(&pdev->dev, + "Incorrect time base frequency %lu Hz\n", clkrate); - mdev->regs = ioremap(res->start, resource_size(res)); - if (!mdev->regs) { - printk(KERN_ERR "Cannot map frame buffer registers\n"); - goto failed_ioremap; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdev->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdev->regs)) + return PTR_ERR(mdev->regs); - clk_enable(mdev->clk); - __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); + err = clk_prepare_enable(mdev->clk); + if (err) + return err; + + writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); mdev->bus_master.data = mdev; mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus; mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit; - err = w1_add_master_device(&mdev->bus_master); + platform_set_drvdata(pdev, mdev); + err = w1_add_master_device(&mdev->bus_master); if (err) - goto failed_add; + clk_disable_unprepare(mdev->clk); - platform_set_drvdata(pdev, mdev); - return 0; - -failed_add: - iounmap(mdev->regs); -failed_ioremap: - release_mem_region(res->start, resource_size(res)); -failed_req: - clk_put(mdev->clk); -failed_clk: - kfree(mdev); return err; } /* * disassociate the w1 device from the driver */ -static int __devexit mxc_w1_remove(struct platform_device *pdev) +static int mxc_w1_remove(struct platform_device *pdev) { struct mxc_w1_device *mdev = platform_get_drvdata(pdev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); w1_remove_master_device(&mdev->bus_master); - iounmap(mdev->regs); - release_mem_region(res->start, resource_size(res)); - clk_disable(mdev->clk); - clk_put(mdev->clk); - - platform_set_drvdata(pdev, NULL); + clk_disable_unprepare(mdev->clk); return 0; } +static struct of_device_id mxc_w1_dt_ids[] = { + { .compatible = "fsl,imx21-owire" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids); + static struct platform_driver mxc_w1_driver = { .driver = { - .name = "mxc_w1", + .name = "mxc_w1", + .owner = THIS_MODULE, + .of_match_table = mxc_w1_dt_ids, }, .probe = mxc_w1_probe, .remove = mxc_w1_remove, }; - -static int __init mxc_w1_init(void) -{ - return platform_driver_register(&mxc_w1_driver); -} - -static void mxc_w1_exit(void) -{ - platform_driver_unregister(&mxc_w1_driver); -} - -module_init(mxc_w1_init); -module_exit(mxc_w1_exit); +module_platform_driver(mxc_w1_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Freescale Semiconductors Inc"); diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 5ef385bfed1..9900e8ec739 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -1,7 +1,7 @@ /* * drivers/w1/masters/omap_hdq.c * - * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2007,2012 Texas Instruments, Inc. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any @@ -14,12 +14,9 @@ #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/err.h> -#include <linux/clk.h> #include <linux/io.h> #include <linux/sched.h> - -#include <asm/irq.h> -#include <mach/hardware.h> +#include <linux/pm_runtime.h> #include "../w1.h" #include "../w1_int.h" @@ -61,8 +58,6 @@ struct hdq_data { /* lock status update */ struct mutex hdq_mutex; int hdq_usecount; - struct clk *hdq_ick; - struct clk *hdq_fck; u8 hdq_irqstatus; /* device lock */ spinlock_t hdq_spinlock; @@ -74,7 +69,7 @@ struct hdq_data { int init_trans; }; -static int __devinit omap_hdq_probe(struct platform_device *pdev); +static int omap_hdq_probe(struct platform_device *pdev); static int omap_hdq_remove(struct platform_device *pdev); static struct platform_driver omap_hdq_driver = { @@ -102,20 +97,20 @@ static struct w1_bus_master omap_w1_master = { /* HDQ register I/O routines */ static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset) { - return __raw_readb(hdq_data->hdq_base + offset); + return __raw_readl(hdq_data->hdq_base + offset); } static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val) { - __raw_writeb(val, hdq_data->hdq_base + offset); + __raw_writel(val, hdq_data->hdq_base + offset); } static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset, u8 val, u8 mask) { - u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask) + u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask) | (val & mask); - __raw_writeb(new_val, hdq_data->hdq_base + offset); + __raw_writel(new_val, hdq_data->hdq_base + offset); return new_val; } @@ -180,6 +175,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); if (ret == 0) { dev_dbg(hdq_data->dev, "TX wait elapsed\n"); + ret = -ETIMEDOUT; goto out; } @@ -187,7 +183,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) /* check irqstatus */ if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) { dev_dbg(hdq_data->dev, "timeout waiting for" - "TXCOMPLETE/RXCOMPLETE, %x", *status); + " TXCOMPLETE/RXCOMPLETE, %x", *status); ret = -ETIMEDOUT; goto out; } @@ -198,7 +194,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) OMAP_HDQ_FLAG_CLEAR, &tmp_status); if (ret) { dev_dbg(hdq_data->dev, "timeout waiting GO bit" - "return to zero, %x", tmp_status); + " return to zero, %x", tmp_status); } out: @@ -341,7 +337,7 @@ static int omap_hdq_break(struct hdq_data *hdq_data) &tmp_status); if (ret) dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits" - "return to zero, %x", tmp_status); + " return to zero, %x", tmp_status); out: mutex_unlock(&hdq_data->hdq_mutex); @@ -353,7 +349,6 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) { int ret = 0; u8 status; - unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); if (ret < 0) { @@ -371,22 +366,20 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); /* - * The RX comes immediately after TX. It - * triggers another interrupt before we - * sleep. So we have to wait for RXCOMPLETE bit. + * The RX comes immediately after TX. */ - while (!(hdq_data->hdq_irqstatus - & OMAP_HDQ_INT_STATUS_RXCOMPLETE) - && time_before(jiffies, timeout)) { - schedule_timeout_uninterruptible(1); - } + wait_event_timeout(hdq_wait_queue, + (hdq_data->hdq_irqstatus + & OMAP_HDQ_INT_STATUS_RXCOMPLETE), + OMAP_HDQ_TIMEOUT); + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0, OMAP_HDQ_CTRL_STATUS_DIR); status = hdq_data->hdq_irqstatus; /* check irqstatus */ if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { dev_dbg(hdq_data->dev, "timeout waiting for" - "RXCOMPLETE, %x", status); + " RXCOMPLETE, %x", status); ret = -ETIMEDOUT; goto out; } @@ -396,7 +389,7 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) out: mutex_unlock(&hdq_data->hdq_mutex); rtn: - return 0; + return ret; } @@ -419,17 +412,8 @@ static int omap_hdq_get(struct hdq_data *hdq_data) hdq_data->hdq_usecount++; try_module_get(THIS_MODULE); if (1 == hdq_data->hdq_usecount) { - if (clk_enable(hdq_data->hdq_ick)) { - dev_dbg(hdq_data->dev, "Can not enable ick\n"); - ret = -ENODEV; - goto clk_err; - } - if (clk_enable(hdq_data->hdq_fck)) { - dev_dbg(hdq_data->dev, "Can not enable fck\n"); - clk_disable(hdq_data->hdq_ick); - ret = -ENODEV; - goto clk_err; - } + + pm_runtime_get_sync(hdq_data->dev); /* make sure HDQ is out of reset */ if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) & @@ -450,9 +434,6 @@ static int omap_hdq_get(struct hdq_data *hdq_data) } } -clk_err: - clk_put(hdq_data->hdq_ick); - clk_put(hdq_data->hdq_fck); out: mutex_unlock(&hdq_data->hdq_mutex); rtn: @@ -470,15 +451,13 @@ static int omap_hdq_put(struct hdq_data *hdq_data) if (0 == hdq_data->hdq_usecount) { dev_dbg(hdq_data->dev, "attempt to decrement use count" - "when it is zero"); + " when it is zero"); ret = -EINVAL; } else { hdq_data->hdq_usecount--; module_put(THIS_MODULE); - if (0 == hdq_data->hdq_usecount) { - clk_disable(hdq_data->hdq_ick); - clk_disable(hdq_data->hdq_fck); - } + if (0 == hdq_data->hdq_usecount) + pm_runtime_put_sync(hdq_data->dev); } mutex_unlock(&hdq_data->hdq_mutex); @@ -540,7 +519,7 @@ static void omap_w1_write_byte(void *_hdq, u8 byte) mutex_unlock(&hdq_data->hdq_mutex); ret = hdq_write_byte(hdq_data, byte, &status); - if (ret == 0) { + if (ret < 0) { dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status); return; } @@ -556,70 +535,35 @@ static void omap_w1_write_byte(void *_hdq, u8 byte) hdq_data->init_trans = 0; mutex_unlock(&hdq_data->hdq_mutex); } - - return; } -static int __devinit omap_hdq_probe(struct platform_device *pdev) +static int omap_hdq_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct hdq_data *hdq_data; struct resource *res; int ret, irq; u8 rev; - hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL); + hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL); if (!hdq_data) { dev_dbg(&pdev->dev, "unable to allocate memory\n"); - ret = -ENOMEM; - goto err_kmalloc; + return -ENOMEM; } - hdq_data->dev = &pdev->dev; + hdq_data->dev = dev; platform_set_drvdata(pdev, hdq_data); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_dbg(&pdev->dev, "unable to get resource\n"); - ret = -ENXIO; - goto err_resource; - } - - hdq_data->hdq_base = ioremap(res->start, SZ_4K); - if (!hdq_data->hdq_base) { - dev_dbg(&pdev->dev, "ioremap failed\n"); - ret = -EINVAL; - goto err_ioremap; - } - - /* get interface & functional clock objects */ - hdq_data->hdq_ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(hdq_data->hdq_ick)) { - dev_dbg(&pdev->dev, "Can't get HDQ ick clock object\n"); - ret = PTR_ERR(hdq_data->hdq_ick); - goto err_ick; - } - - hdq_data->hdq_fck = clk_get(&pdev->dev, "fck"); - if (IS_ERR(hdq_data->hdq_fck)) { - dev_dbg(&pdev->dev, "Can't get HDQ fck clock object\n"); - ret = PTR_ERR(hdq_data->hdq_fck); - goto err_fck; - } + hdq_data->hdq_base = devm_ioremap_resource(dev, res); + if (IS_ERR(hdq_data->hdq_base)) + return PTR_ERR(hdq_data->hdq_base); hdq_data->hdq_usecount = 0; mutex_init(&hdq_data->hdq_mutex); - if (clk_enable(hdq_data->hdq_ick)) { - dev_dbg(&pdev->dev, "Can not enable ick\n"); - ret = -ENODEV; - goto err_intfclk; - } - - if (clk_enable(hdq_data->hdq_fck)) { - dev_dbg(&pdev->dev, "Can not enable fck\n"); - ret = -ENODEV; - goto err_fnclk; - } + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", @@ -633,7 +577,7 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev) goto err_irq; } - ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data); + ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data); if (ret < 0) { dev_dbg(&pdev->dev, "could not request irq\n"); goto err_irq; @@ -641,9 +585,7 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev) omap_hdq_break(hdq_data); - /* don't clock the HDQ until it is needed */ - clk_disable(hdq_data->hdq_ick); - clk_disable(hdq_data->hdq_fck); + pm_runtime_put_sync(&pdev->dev); omap_w1_master.data = hdq_data; @@ -655,30 +597,12 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev) return 0; -err_w1: err_irq: - clk_disable(hdq_data->hdq_fck); - -err_fnclk: - clk_disable(hdq_data->hdq_ick); - -err_intfclk: - clk_put(hdq_data->hdq_fck); - -err_fck: - clk_put(hdq_data->hdq_ick); - -err_ick: - iounmap(hdq_data->hdq_base); - -err_ioremap: -err_resource: - platform_set_drvdata(pdev, NULL); - kfree(hdq_data); + pm_runtime_put_sync(&pdev->dev); +err_w1: + pm_runtime_disable(&pdev->dev); -err_kmalloc: return ret; - } static int omap_hdq_remove(struct platform_device *pdev) @@ -696,29 +620,12 @@ static int omap_hdq_remove(struct platform_device *pdev) mutex_unlock(&hdq_data->hdq_mutex); /* remove module dependency */ - clk_put(hdq_data->hdq_ick); - clk_put(hdq_data->hdq_fck); - free_irq(INT_24XX_HDQ_IRQ, hdq_data); - platform_set_drvdata(pdev, NULL); - iounmap(hdq_data->hdq_base); - kfree(hdq_data); + pm_runtime_disable(&pdev->dev); return 0; } -static int __init -omap_hdq_init(void) -{ - return platform_driver_register(&omap_hdq_driver); -} -module_init(omap_hdq_init); - -static void __exit -omap_hdq_exit(void) -{ - platform_driver_unregister(&omap_hdq_driver); -} -module_exit(omap_hdq_exit); +module_platform_driver(omap_hdq_driver); module_param(w1_id, int, S_IRUSR); MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection"); diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index fcbe742188a..1d111e56c8c 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -13,11 +13,35 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/w1-gpio.h> +#include <linux/gpio.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/delay.h> #include "../w1.h" #include "../w1_int.h" -#include <asm/gpio.h> +static u8 w1_gpio_set_pullup(void *data, int delay) +{ + struct w1_gpio_platform_data *pdata = data; + + if (delay) { + pdata->pullup_duration = delay; + } else { + if (pdata->pullup_duration) { + gpio_direction_output(pdata->pin, 1); + + msleep(pdata->pullup_duration); + + gpio_direction_input(pdata->pin); + } + pdata->pullup_duration = 0; + } + + return 0; +} static void w1_gpio_write_bit_dir(void *data, u8 bit) { @@ -43,22 +67,91 @@ static u8 w1_gpio_read_bit(void *data) return gpio_get_value(pdata->pin) ? 1 : 0; } -static int __init w1_gpio_probe(struct platform_device *pdev) +#if defined(CONFIG_OF) +static struct of_device_id w1_gpio_dt_ids[] = { + { .compatible = "w1-gpio" }, + {} +}; +MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); +#endif + +static int w1_gpio_probe_dt(struct platform_device *pdev) +{ + struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + int gpio; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_get_property(np, "linux,open-drain", NULL)) + pdata->is_open_drain = 1; + + gpio = of_get_gpio(np, 0); + if (gpio < 0) { + if (gpio != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to parse gpio property for data pin (%d)\n", + gpio); + + return gpio; + } + pdata->pin = gpio; + + gpio = of_get_gpio(np, 1); + if (gpio == -EPROBE_DEFER) + return gpio; + /* ignore other errors as the pullup gpio is optional */ + pdata->ext_pullup_enable_pin = gpio; + + pdev->dev.platform_data = pdata; + + return 0; +} + +static int w1_gpio_probe(struct platform_device *pdev) { struct w1_bus_master *master; - struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + struct w1_gpio_platform_data *pdata; int err; - if (!pdata) + if (of_have_populated_dt()) { + err = w1_gpio_probe_dt(pdev); + if (err < 0) + return err; + } + + pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) { + dev_err(&pdev->dev, "No configuration data\n"); return -ENXIO; + } - master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL); - if (!master) + master = devm_kzalloc(&pdev->dev, sizeof(struct w1_bus_master), + GFP_KERNEL); + if (!master) { + dev_err(&pdev->dev, "Out of memory\n"); return -ENOMEM; + } - err = gpio_request(pdata->pin, "w1"); - if (err) - goto free_master; + err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); + if (err) { + dev_err(&pdev->dev, "gpio_request (pin) failed\n"); + return err; + } + + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) { + err = devm_gpio_request_one(&pdev->dev, + pdata->ext_pullup_enable_pin, GPIOF_INIT_LOW, + "w1 pullup"); + if (err < 0) { + dev_err(&pdev->dev, "gpio_request_one " + "(ext_pullup_enable_pin) failed\n"); + return err; + } + } master->data = pdata; master->read_bit = w1_gpio_read_bit; @@ -69,38 +162,38 @@ static int __init w1_gpio_probe(struct platform_device *pdev) } else { gpio_direction_input(pdata->pin); master->write_bit = w1_gpio_write_bit_dir; + master->set_pullup = w1_gpio_set_pullup; } err = w1_add_master_device(master); - if (err) - goto free_gpio; + if (err) { + dev_err(&pdev->dev, "w1_add_master device failed\n"); + return err; + } if (pdata->enable_external_pullup) pdata->enable_external_pullup(1); + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_set_value(pdata->ext_pullup_enable_pin, 1); + platform_set_drvdata(pdev, master); return 0; - - free_gpio: - gpio_free(pdata->pin); - free_master: - kfree(master); - - return err; } -static int __exit w1_gpio_remove(struct platform_device *pdev) +static int w1_gpio_remove(struct platform_device *pdev) { struct w1_bus_master *master = platform_get_drvdata(pdev); - struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); if (pdata->enable_external_pullup) pdata->enable_external_pullup(0); + if (gpio_is_valid(pdata->ext_pullup_enable_pin)) + gpio_set_value(pdata->ext_pullup_enable_pin, 0); + w1_remove_master_device(master); - gpio_free(pdata->pin); - kfree(master); return 0; } @@ -109,7 +202,7 @@ static int __exit w1_gpio_remove(struct platform_device *pdev) static int w1_gpio_suspend(struct platform_device *pdev, pm_message_t state) { - struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); if (pdata->enable_external_pullup) pdata->enable_external_pullup(0); @@ -119,7 +212,7 @@ static int w1_gpio_suspend(struct platform_device *pdev, pm_message_t state) static int w1_gpio_resume(struct platform_device *pdev) { - struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); if (pdata->enable_external_pullup) pdata->enable_external_pullup(1); @@ -136,24 +229,15 @@ static struct platform_driver w1_gpio_driver = { .driver = { .name = "w1-gpio", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(w1_gpio_dt_ids), }, - .remove = __exit_p(w1_gpio_remove), + .probe = w1_gpio_probe, + .remove = w1_gpio_remove, .suspend = w1_gpio_suspend, .resume = w1_gpio_resume, }; -static int __init w1_gpio_init(void) -{ - return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe); -} - -static void __exit w1_gpio_exit(void) -{ - platform_driver_unregister(&w1_gpio_driver); -} - -module_init(w1_gpio_init); -module_exit(w1_gpio_exit); +module_platform_driver(w1_gpio_driver); MODULE_DESCRIPTION("GPIO w1 bus master driver"); MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index d0cb01b4201..1cdce80b6ab 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -17,11 +17,26 @@ config W1_SLAVE_SMEM simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. config W1_SLAVE_DS2408 - tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" - help - Say Y here if you want to use a 1-wire + tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" + help + Say Y here if you want to use a 1-wire + DS2408 8-Channel Addressable Switch device support + +config W1_SLAVE_DS2408_READBACK + bool "Read-back values written to DS2408's output register" + depends on W1_SLAVE_DS2408 + default y + help + Enabling this will cause the driver to read back the values written + to the chip's output register in order to detect errors. + + This is slower but useful when debugging chips and/or busses. - DS2408 8-Channel Addressable Switch device support +config W1_SLAVE_DS2413 + tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)" + help + Say Y here if you want to use a 1-wire + DS2413 Dual Channel Addressable Switch device support config W1_SLAVE_DS2423 tristate "Counter 1-wire device (DS2423)" @@ -57,7 +72,6 @@ config W1_SLAVE_DS2433_CRC config W1_SLAVE_DS2760 tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)" - depends on W1 help If you enable this you will have the DS2760 battery monitor chip support. @@ -70,7 +84,6 @@ config W1_SLAVE_DS2760 config W1_SLAVE_DS2780 tristate "Dallas 2780 battery monitor chip" - depends on W1 help If you enable this you will have the DS2780 battery monitor chip support. @@ -81,9 +94,32 @@ config W1_SLAVE_DS2780 If you are unsure, say N. +config W1_SLAVE_DS2781 + tristate "Dallas 2781 battery monitor chip" + help + If you enable this you will have the DS2781 battery monitor + chip support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + +config W1_SLAVE_DS28E04 + tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)" + select CRC16 + help + If you enable this you will have the DS28E04-100 + chip support. + + Say Y here if you want to use a 1-wire + 4kb EEPROM with PIO family device (DS28E04). + + If you are unsure, say N. + config W1_SLAVE_BQ27000 tristate "BQ27000 slave support" - depends on W1 help Say Y here if you want to use a hdq bq27000 slave support. diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 1f31e9fb0b2..06529f3157a 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,10 +4,13 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o -obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o +obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o +obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 8f4c91f6c68..afbefed5f2c 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -15,6 +15,7 @@ #include <linux/types.h> #include <linux/platform_device.h> #include <linux/mutex.h> +#include <linux/power/bq27x00_battery.h> #include "../w1.h" #include "../w1_int.h" @@ -25,46 +26,39 @@ static int F_ID; -void w1_bq27000_write(struct device *dev, u8 buf, u8 reg) -{ - struct w1_slave *sl = container_of(dev, struct w1_slave, dev); - - if (!dev) { - pr_info("Could not obtain slave dev ptr\n"); - return; - } - - w1_write_8(sl->master, HDQ_CMD_WRITE | reg); - w1_write_8(sl->master, buf); -} -EXPORT_SYMBOL(w1_bq27000_write); - -int w1_bq27000_read(struct device *dev, u8 reg) +static int w1_bq27000_read(struct device *dev, unsigned int reg) { u8 val; - struct w1_slave *sl = container_of(dev, struct w1_slave, dev); - - if (!dev) - return 0; + struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); + mutex_lock(&sl->master->bus_mutex); w1_write_8(sl->master, HDQ_CMD_READ | reg); val = w1_read_8(sl->master); + mutex_unlock(&sl->master->bus_mutex); return val; } -EXPORT_SYMBOL(w1_bq27000_read); + +static struct bq27000_platform_data bq27000_battery_info = { + .read = w1_bq27000_read, + .name = "bq27000-battery", +}; static int w1_bq27000_add_slave(struct w1_slave *sl) { int ret; - int id = 1; struct platform_device *pdev; - pdev = platform_device_alloc("bq27000-battery", id); + pdev = platform_device_alloc("bq27000-battery", -1); if (!pdev) { ret = -ENOMEM; return ret; } + ret = platform_device_add_data(pdev, + &bq27000_battery_info, + sizeof(bq27000_battery_info)); + if (ret) + goto pdev_add_failed; pdev->dev.parent = &sl->dev; ret = platform_device_add(pdev); @@ -76,7 +70,7 @@ static int w1_bq27000_add_slave(struct w1_slave *sl) goto success; pdev_add_failed: - platform_device_unregister(pdev); + platform_device_put(pdev); success: return ret; } diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 7c8cdb8aed2..7dfa0e11688 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -22,6 +22,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>"); MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408)); #define W1_F29_RETRIES 3 @@ -52,11 +53,11 @@ static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) if (!buf) return -EINVAL; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); dev_dbg(&sl->dev, "mutex locked"); if (w1_reset_select_slave(sl)) { - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return -EIO; } @@ -66,15 +67,14 @@ static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) w1_write_block(sl->master, wrbuf, 3); *buf = w1_read_8(sl->master); - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); dev_dbg(&sl->dev, "mutex unlocked"); return 1; } -static ssize_t w1_f29_read_state( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t state_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", @@ -84,10 +84,9 @@ static ssize_t w1_f29_read_state( return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf); } -static ssize_t w1_f29_read_output( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t output_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", @@ -98,10 +97,9 @@ static ssize_t w1_f29_read_output( W1_F29_REG_OUTPUT_LATCH_STATE, buf); } -static ssize_t w1_f29_read_activity( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t activity_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", @@ -112,10 +110,9 @@ static ssize_t w1_f29_read_activity( W1_F29_REG_ACTIVITY_LATCH_STATE, buf); } -static ssize_t w1_f29_read_cond_search_mask( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", @@ -126,10 +123,10 @@ static ssize_t w1_f29_read_cond_search_mask( W1_F29_REG_COND_SEARCH_SELECT_MASK, buf); } -static ssize_t w1_f29_read_cond_search_polarity( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t cond_search_polarity_read(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { if (count != 1 || off != 0) return -EFAULT; @@ -137,10 +134,9 @@ static ssize_t w1_f29_read_cond_search_polarity( W1_F29_REG_COND_SEARCH_POL_SELECT, buf); } -static ssize_t w1_f29_read_status_control( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t status_control_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { if (count != 1 || off != 0) return -EFAULT; @@ -148,13 +144,9 @@ static ssize_t w1_f29_read_status_control( W1_F29_REG_CONTROL_AND_STATUS, buf); } - - - -static ssize_t w1_f29_write_output( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t output_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); u8 w1_buf[3]; @@ -165,7 +157,7 @@ static ssize_t w1_f29_write_output( return -EFAULT; dev_dbg(&sl->dev, "locking mutex for write_output"); - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); dev_dbg(&sl->dev, "mutex locked"); if (w1_reset_select_slave(sl)) @@ -178,6 +170,15 @@ static ssize_t w1_f29_write_output( w1_write_block(sl->master, w1_buf, 3); readBack = w1_read_8(sl->master); + + if (readBack != W1_F29_SUCCESS_CONFIRM_BYTE) { + if (w1_reset_resume_command(sl->master)) + goto error; + /* try again, the slave is ready for a command */ + continue; + } + +#ifdef CONFIG_W1_SLAVE_DS2408_READBACK /* here the master could read another byte which would be the PIO reg (the actual pin logic state) since in this driver we don't know which pins are @@ -186,11 +187,6 @@ static ssize_t w1_f29_write_output( if (w1_reset_resume_command(sl->master)) goto error; - if (readBack != 0xAA) { - /* try again, the slave is ready for a command */ - continue; - } - /* go read back the output latches */ /* (the direct effect of the write above) */ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; @@ -198,16 +194,18 @@ static ssize_t w1_f29_write_output( w1_buf[2] = 0; w1_write_block(sl->master, w1_buf, 3); /* read the result of the READ_PIO_REGS command */ - if (w1_read_8(sl->master) == *buf) { + if (w1_read_8(sl->master) == *buf) +#endif + { /* success! */ - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); return 1; } } error: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); return -EIO; @@ -217,10 +215,9 @@ error: /** * Writing to the activity file resets the activity latches. */ -static ssize_t w1_f29_write_activity( - struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t activity_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); unsigned int retries = W1_F29_RETRIES; @@ -228,7 +225,7 @@ static ssize_t w1_f29_write_activity( if (count != 1 || off != 0) return -EFAULT; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); if (w1_reset_select_slave(sl)) goto error; @@ -236,7 +233,7 @@ static ssize_t w1_f29_write_activity( while (retries--) { w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES); if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) { - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return 1; } if (w1_reset_resume_command(sl->master)) @@ -244,17 +241,13 @@ static ssize_t w1_f29_write_activity( } error: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return -EIO; } -static ssize_t w1_f29_write_status_control( - struct file *filp, - struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, - loff_t off, - size_t count) +static ssize_t status_control_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); u8 w1_buf[4]; @@ -263,7 +256,7 @@ static ssize_t w1_f29_write_status_control( if (count != 1 || off != 0) return -EFAULT; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); if (w1_reset_select_slave(sl)) goto error; @@ -285,102 +278,73 @@ static ssize_t w1_f29_write_status_control( w1_write_block(sl->master, w1_buf, 3); if (w1_read_8(sl->master) == *buf) { /* success! */ - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return 1; } } error: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return -EIO; } +/* + * This is a special sequence we must do to ensure the P0 output is not stuck + * in test mode. This is described in rev 2 of the ds2408's datasheet + * (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under + * "APPLICATION INFORMATION/Power-up timing". + */ +static int w1_f29_disable_test_mode(struct w1_slave *sl) +{ + int res; + u8 magic[10] = {0x96, }; + u64 rn = le64_to_cpu(*((u64*)&sl->reg_num)); + memcpy(&magic[1], &rn, 8); + magic[9] = 0x3C; -#define NB_SYSFS_BIN_FILES 6 -static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { - { - .attr = { - .name = "state", - .mode = S_IRUGO, - }, - .size = 1, - .read = w1_f29_read_state, - }, - { - .attr = { - .name = "output", - .mode = S_IRUGO | S_IWUSR | S_IWGRP, - }, - .size = 1, - .read = w1_f29_read_output, - .write = w1_f29_write_output, - }, - { - .attr = { - .name = "activity", - .mode = S_IRUGO, - }, - .size = 1, - .read = w1_f29_read_activity, - .write = w1_f29_write_activity, - }, - { - .attr = { - .name = "cond_search_mask", - .mode = S_IRUGO, - }, - .size = 1, - .read = w1_f29_read_cond_search_mask, - .write = 0, - }, - { - .attr = { - .name = "cond_search_polarity", - .mode = S_IRUGO, - }, - .size = 1, - .read = w1_f29_read_cond_search_polarity, - .write = 0, - }, - { - .attr = { - .name = "status_control", - .mode = S_IRUGO | S_IWUSR | S_IWGRP, - }, - .size = 1, - .read = w1_f29_read_status_control, - .write = w1_f29_write_status_control, - } -}; + mutex_lock(&sl->master->bus_mutex); -static int w1_f29_add_slave(struct w1_slave *sl) -{ - int err = 0; - int i; - - for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) - err = sysfs_create_bin_file( - &sl->dev.kobj, - &(w1_f29_sysfs_bin_files[i])); - if (err) - while (--i >= 0) - sysfs_remove_bin_file(&sl->dev.kobj, - &(w1_f29_sysfs_bin_files[i])); - return err; -} + res = w1_reset_bus(sl->master); + if (res) + goto out; + w1_write_block(sl->master, magic, ARRAY_SIZE(magic)); -static void w1_f29_remove_slave(struct w1_slave *sl) -{ - int i; - for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) - sysfs_remove_bin_file(&sl->dev.kobj, - &(w1_f29_sysfs_bin_files[i])); + res = w1_reset_bus(sl->master); +out: + mutex_unlock(&sl->master->bus_mutex); + return res; } +static BIN_ATTR_RO(state, 1); +static BIN_ATTR_RW(output, 1); +static BIN_ATTR_RW(activity, 1); +static BIN_ATTR_RO(cond_search_mask, 1); +static BIN_ATTR_RO(cond_search_polarity, 1); +static BIN_ATTR_RW(status_control, 1); + +static struct bin_attribute *w1_f29_bin_attrs[] = { + &bin_attr_state, + &bin_attr_output, + &bin_attr_activity, + &bin_attr_cond_search_mask, + &bin_attr_cond_search_polarity, + &bin_attr_status_control, + NULL, +}; + +static const struct attribute_group w1_f29_group = { + .bin_attrs = w1_f29_bin_attrs, +}; + +static const struct attribute_group *w1_f29_groups[] = { + &w1_f29_group, + NULL, +}; + static struct w1_family_ops w1_f29_fops = { - .add_slave = w1_f29_add_slave, - .remove_slave = w1_f29_remove_slave, + .add_slave = w1_f29_disable_test_mode, + .groups = w1_f29_groups, }; static struct w1_family w1_family_29 = { diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c new file mode 100644 index 00000000000..ee28fc1ff39 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2413.c @@ -0,0 +1,150 @@ +/* + * w1_ds2413.c - w1 family 3a (DS2413) driver + * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); +MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413)); + +#define W1_F3A_RETRIES 3 +#define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 +#define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A +#define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA + +static ssize_t state_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + dev_dbg(&sl->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + + if (off != 0) + return 0; + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->bus_mutex); + return -EIO; + } + + w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); + *buf = w1_read_8(sl->master); + + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked"); + + /* check for correct complement */ + if ((*buf & 0x0F) != ((~*buf >> 4) & 0x0F)) + return -EIO; + else + return 1; +} + +static BIN_ATTR_RO(state, 1); + +static ssize_t output_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 w1_buf[3]; + unsigned int retries = W1_F3A_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + dev_dbg(&sl->dev, "locking mutex for write_output"); + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) + goto error; + + /* according to the DS2413 datasheet the most significant 6 bits + should be set to "1"s, so do it now */ + *buf = *buf | 0xFC; + + while (retries--) { + w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE; + w1_buf[1] = *buf; + w1_buf[2] = ~(*buf); + w1_write_block(sl->master, w1_buf, 3); + + if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); + return 1; + } + if (w1_reset_resume_command(sl->master)) + goto error; + } + +error: + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + return -EIO; +} + +static BIN_ATTR(output, S_IRUGO | S_IWUSR | S_IWGRP, NULL, output_write, 1); + +static struct bin_attribute *w1_f3a_bin_attrs[] = { + &bin_attr_state, + &bin_attr_output, + NULL, +}; + +static const struct attribute_group w1_f3a_group = { + .bin_attrs = w1_f3a_bin_attrs, +}; + +static const struct attribute_group *w1_f3a_groups[] = { + &w1_f3a_group, + NULL, +}; + +static struct w1_family_ops w1_f3a_fops = { + .groups = w1_f3a_groups, +}; + +static struct w1_family w1_family_3a = { + .fid = W1_FAMILY_DS2413, + .fops = &w1_f3a_fops, +}; + +static int __init w1_f3a_init(void) +{ + return w1_register_family(&w1_family_3a); +} + +static void __exit w1_f3a_exit(void) +{ + w1_unregister_family(&w1_family_3a); +} + +module_init(w1_f3a_init); +module_exit(w1_f3a_exit); diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c index 7a7dbe5026f..7e41b7d91fb 100644 --- a/drivers/w1/slaves/w1_ds2423.c +++ b/drivers/w1/slaves/w1_ds2423.c @@ -40,14 +40,8 @@ #define COUNTER_COUNT 4 #define READ_BYTE_COUNT 42 -static ssize_t w1_counter_read(struct device *device, - struct device_attribute *attr, char *buf); - -static struct device_attribute w1_counter_attr = - __ATTR(w1_slave, S_IRUGO, w1_counter_read, NULL); - -static ssize_t w1_counter_read(struct device *device, - struct device_attribute *attr, char *out_buf) +static ssize_t w1_slave_show(struct device *device, + struct device_attribute *attr, char *out_buf) { struct w1_slave *sl = dev_to_w1_slave(device); struct w1_master *dev = sl->master; @@ -66,7 +60,7 @@ static ssize_t w1_counter_read(struct device *device, wrbuf[0] = 0xA5; wrbuf[1] = rom_addr & 0xFF; wrbuf[2] = rom_addr >> 8; - mutex_lock(&dev->mutex); + mutex_lock(&dev->bus_mutex); if (!w1_reset_select_slave(sl)) { w1_write_block(dev, wrbuf, 3); read_byte_count = 0; @@ -124,23 +118,20 @@ static ssize_t w1_counter_read(struct device *device, } else { c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error"); } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->bus_mutex); return PAGE_SIZE - c; } -static int w1_f1d_add_slave(struct w1_slave *sl) -{ - return device_create_file(&sl->dev, &w1_counter_attr); -} +static DEVICE_ATTR_RO(w1_slave); -static void w1_f1d_remove_slave(struct w1_slave *sl) -{ - device_remove_file(&sl->dev, &w1_counter_attr); -} +static struct attribute *w1_f1d_attrs[] = { + &dev_attr_w1_slave.attr, + NULL, +}; +ATTRIBUTE_GROUPS(w1_f1d); static struct w1_family_ops w1_f1d_fops = { - .add_slave = w1_f1d_add_slave, - .remove_slave = w1_f1d_remove_slave, + .groups = w1_f1d_groups, }; static struct w1_family w1_family_1d = { @@ -164,3 +155,4 @@ module_exit(w1_f1d_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>"); MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram"); +MODULE_ALIAS("w1-family-" __stringify(W1_COUNTER_DS2423)); diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 84e2410aec1..9c4ff9d28ad 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -96,9 +96,9 @@ static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) return -1; } -static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); int todo = count; @@ -107,7 +107,7 @@ static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj, if (count == 0) return 0; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */ while (todo > 0) { @@ -126,7 +126,7 @@ static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj, off += W1_F2D_READ_MAXLEN; } - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } @@ -202,9 +202,9 @@ retry: return 0; } -static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); int addr, len; @@ -214,7 +214,7 @@ static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj, if (count == 0) return 0; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); /* Can only write data in blocks of the size of the scratchpad */ addr = off; @@ -259,34 +259,29 @@ static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj, } out_up: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } -static struct bin_attribute w1_f2d_bin_attr = { - .attr = { - .name = "eeprom", - .mode = S_IRUGO | S_IWUSR, - }, - .size = W1_F2D_EEPROM_SIZE, - .read = w1_f2d_read_bin, - .write = w1_f2d_write_bin, +static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE); + +static struct bin_attribute *w1_f2d_bin_attrs[] = { + &bin_attr_eeprom, + NULL, }; -static int w1_f2d_add_slave(struct w1_slave *sl) -{ - return sysfs_create_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); -} +static const struct attribute_group w1_f2d_group = { + .bin_attrs = w1_f2d_bin_attrs, +}; -static void w1_f2d_remove_slave(struct w1_slave *sl) -{ - sysfs_remove_bin_file(&sl->dev.kobj, &w1_f2d_bin_attr); -} +static const struct attribute_group *w1_f2d_groups[] = { + &w1_f2d_group, + NULL, +}; static struct w1_family_ops w1_f2d_fops = { - .add_slave = w1_f2d_add_slave, - .remove_slave = w1_f2d_remove_slave, + .groups = w1_f2d_groups, }; static struct w1_family w1_family_2d = { @@ -310,3 +305,4 @@ module_exit(w1_f2d_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>"); MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431)); diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 0f7b8f9c509..72319a968a9 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -29,6 +29,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433)); #define W1_EEPROM_SIZE 512 #define W1_PAGE_COUNT 16 @@ -92,9 +93,9 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, } #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ -static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -107,7 +108,7 @@ static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj, if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) return 0; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -138,7 +139,7 @@ static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj, #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ out_up: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } @@ -206,9 +207,9 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) return 0; } -static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); int addr, len, idx; @@ -233,7 +234,7 @@ static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj, } #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); /* Can only write data to one page at a time */ idx = 0; @@ -251,24 +252,29 @@ static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj, } out_up: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } -static struct bin_attribute w1_f23_bin_attr = { - .attr = { - .name = "eeprom", - .mode = S_IRUGO | S_IWUSR, - }, - .size = W1_EEPROM_SIZE, - .read = w1_f23_read_bin, - .write = w1_f23_write_bin, +static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); + +static struct bin_attribute *w1_f23_bin_attributes[] = { + &bin_attr_eeprom, + NULL, +}; + +static const struct attribute_group w1_f23_group = { + .bin_attrs = w1_f23_bin_attributes, +}; + +static const struct attribute_group *w1_f23_groups[] = { + &w1_f23_group, + NULL, }; static int w1_f23_add_slave(struct w1_slave *sl) { - int err; #ifdef CONFIG_W1_SLAVE_DS2433_CRC struct w1_f23_data *data; @@ -278,15 +284,7 @@ static int w1_f23_add_slave(struct w1_slave *sl) sl->family_data = data; #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - - err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); - -#ifdef CONFIG_W1_SLAVE_DS2433_CRC - if (err) - kfree(data); -#endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - - return err; + return 0; } static void w1_f23_remove_slave(struct w1_slave *sl) @@ -295,12 +293,12 @@ static void w1_f23_remove_slave(struct w1_slave *sl) kfree(sl->family_data); sl->family_data = NULL; #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ - sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); } static struct w1_family_ops w1_f23_fops = { .add_slave = w1_f23_add_slave, .remove_slave = w1_f23_remove_slave, + .groups = w1_f23_groups, }; static struct w1_family w1_family_23 = { diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index 5754c9a4f58..65f90dccd60 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -31,7 +31,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, if (!dev) return 0; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); if (addr > DS2760_DATA_SIZE || addr < 0) { count = 0; @@ -54,7 +54,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, } out: - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return count; } @@ -76,14 +76,14 @@ static int w1_ds2760_eeprom_cmd(struct device *dev, int addr, int cmd) if (!dev) return -EINVAL; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); if (w1_reset_select_slave(sl) == 0) { w1_write_8(sl->master, cmd); w1_write_8(sl->master, addr); } - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return 0; } @@ -97,21 +97,28 @@ int w1_ds2760_recall_eeprom(struct device *dev, int addr) return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_RECALL_DATA); } -static ssize_t w1_ds2760_read_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); return w1_ds2760_read(dev, buf, off, count); } -static struct bin_attribute w1_ds2760_bin_attr = { - .attr = { - .name = "w1_slave", - .mode = S_IRUGO, - }, - .size = DS2760_DATA_SIZE, - .read = w1_ds2760_read_bin, +static BIN_ATTR_RO(w1_slave, DS2760_DATA_SIZE); + +static struct bin_attribute *w1_ds2760_bin_attrs[] = { + &bin_attr_w1_slave, + NULL, +}; + +static const struct attribute_group w1_ds2760_group = { + .bin_attrs = w1_ds2760_bin_attrs, +}; + +static const struct attribute_group *w1_ds2760_groups[] = { + &w1_ds2760_group, + NULL, }; static DEFINE_IDA(bat_ida); @@ -139,17 +146,12 @@ static int w1_ds2760_add_slave(struct w1_slave *sl) if (ret) goto pdev_add_failed; - ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); - if (ret) - goto bin_attr_failed; - dev_set_drvdata(&sl->dev, pdev); goto success; -bin_attr_failed: pdev_add_failed: - platform_device_unregister(pdev); + platform_device_put(pdev); pdev_alloc_failed: ida_simple_remove(&bat_ida, id); noid: @@ -164,12 +166,12 @@ static void w1_ds2760_remove_slave(struct w1_slave *sl) platform_device_unregister(pdev); ida_simple_remove(&bat_ida, id); - sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); } static struct w1_family_ops w1_ds2760_fops = { .add_slave = w1_ds2760_add_slave, .remove_slave = w1_ds2760_remove_slave, + .groups = w1_ds2760_groups, }; static struct w1_family w1_ds2760_family = { @@ -202,3 +204,4 @@ module_exit(w1_ds2760_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>"); MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2760)); diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index 39f78c0b143..50e85f7929d 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -60,30 +60,16 @@ int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, if (!dev) return -ENODEV; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); ret = w1_ds2780_do_io(dev, buf, addr, count, io); - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return ret; } EXPORT_SYMBOL(w1_ds2780_io); -int w1_ds2780_io_nolock(struct device *dev, char *buf, int addr, size_t count, - int io) -{ - int ret; - - if (!dev) - return -ENODEV; - - ret = w1_ds2780_do_io(dev, buf, addr, count, io); - - return ret; -} -EXPORT_SYMBOL(w1_ds2780_io_nolock); - int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd) { struct w1_slave *sl = container_of(dev, struct w1_slave, dev); @@ -91,34 +77,40 @@ int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd) if (!dev) return -EINVAL; - mutex_lock(&sl->master->mutex); + mutex_lock(&sl->master->bus_mutex); if (w1_reset_select_slave(sl) == 0) { w1_write_8(sl->master, cmd); w1_write_8(sl->master, addr); } - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->master->bus_mutex); return 0; } EXPORT_SYMBOL(w1_ds2780_eeprom_cmd); -static ssize_t w1_ds2780_read_bin(struct file *filp, - struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); return w1_ds2780_io(dev, buf, off, count, 0); } -static struct bin_attribute w1_ds2780_bin_attr = { - .attr = { - .name = "w1_slave", - .mode = S_IRUGO, - }, - .size = DS2780_DATA_SIZE, - .read = w1_ds2780_read_bin, +static BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE); + +static struct bin_attribute *w1_ds2780_bin_attrs[] = { + &bin_attr_w1_slave, + NULL, +}; + +static const struct attribute_group w1_ds2780_group = { + .bin_attrs = w1_ds2780_bin_attrs, +}; + +static const struct attribute_group *w1_ds2780_groups[] = { + &w1_ds2780_group, + NULL, }; static DEFINE_IDA(bat_ida); @@ -146,17 +138,12 @@ static int w1_ds2780_add_slave(struct w1_slave *sl) if (ret) goto pdev_add_failed; - ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr); - if (ret) - goto bin_attr_failed; - dev_set_drvdata(&sl->dev, pdev); return 0; -bin_attr_failed: pdev_add_failed: - platform_device_unregister(pdev); + platform_device_put(pdev); pdev_alloc_failed: ida_simple_remove(&bat_ida, id); noid: @@ -170,12 +157,12 @@ static void w1_ds2780_remove_slave(struct w1_slave *sl) platform_device_unregister(pdev); ida_simple_remove(&bat_ida, id); - sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr); } static struct w1_family_ops w1_ds2780_fops = { .add_slave = w1_ds2780_add_slave, .remove_slave = w1_ds2780_remove_slave, + .groups = w1_ds2780_groups, }; static struct w1_family w1_ds2780_family = { @@ -201,3 +188,4 @@ module_exit(w1_ds2780_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>"); MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780)); diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h index 73737936502..a1fba79eb1b 100644 --- a/drivers/w1/slaves/w1_ds2780.h +++ b/drivers/w1/slaves/w1_ds2780.h @@ -124,8 +124,6 @@ extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count, int io); -extern int w1_ds2780_io_nolock(struct device *dev, char *buf, int addr, - size_t count, int io); extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd); #endif /* !_W1_DS2780_H */ diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c new file mode 100644 index 00000000000..1eb98fb1688 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.c @@ -0,0 +1,189 @@ +/* + * 1-Wire implementation for the ds2781 chip + * + * Author: Renata Sayakhova <renata@oktetlabs.ru> + * + * Based on w1-ds2780 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2781.h" + +static int w1_ds2781_do_io(struct device *dev, char *buf, int addr, + size_t count, int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (addr > DS2781_DATA_SIZE || addr < 0) + return 0; + + count = min_t(int, count, DS2781_DATA_SIZE - addr); + + if (w1_reset_select_slave(sl) == 0) { + if (io) { + w1_write_8(sl->master, W1_DS2781_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + } else { + w1_write_8(sl->master, W1_DS2781_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } + } + + return count; +} + +int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret; + + if (!dev) + return -ENODEV; + + mutex_lock(&sl->master->bus_mutex); + + ret = w1_ds2781_do_io(dev, buf, addr, count, io); + + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} +EXPORT_SYMBOL(w1_ds2781_io); + +int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_reset_select_slave(sl) == 0) { + w1_write_8(sl->master, cmd); + w1_write_8(sl->master, addr); + } + + mutex_unlock(&sl->master->bus_mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2781_eeprom_cmd); + +static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2781_io(dev, buf, off, count, 0); +} + +static BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE); + +static struct bin_attribute *w1_ds2781_bin_attrs[] = { + &bin_attr_w1_slave, + NULL, +}; + +static const struct attribute_group w1_ds2781_group = { + .bin_attrs = w1_ds2781_bin_attrs, +}; + +static const struct attribute_group *w1_ds2781_groups[] = { + &w1_ds2781_group, + NULL, +}; + +static DEFINE_IDA(bat_ida); + +static int w1_ds2781_add_slave(struct w1_slave *sl) +{ + int ret; + int id; + struct platform_device *pdev; + + id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc("ds2781-battery", id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + return 0; + +pdev_add_failed: + platform_device_put(pdev); +pdev_alloc_failed: + ida_simple_remove(&bat_ida, id); +noid: + return ret; +} + +static void w1_ds2781_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + ida_simple_remove(&bat_ida, id); +} + +static struct w1_family_ops w1_ds2781_fops = { + .add_slave = w1_ds2781_add_slave, + .remove_slave = w1_ds2781_remove_slave, + .groups = w1_ds2781_groups, +}; + +static struct w1_family w1_ds2781_family = { + .fid = W1_FAMILY_DS2781, + .fops = &w1_ds2781_fops, +}; + +static int __init w1_ds2781_init(void) +{ + ida_init(&bat_ida); + return w1_register_family(&w1_ds2781_family); +} + +static void __exit w1_ds2781_exit(void) +{ + w1_unregister_family(&w1_ds2781_family); + ida_destroy(&bat_ida); +} + +module_init(w1_ds2781_init); +module_exit(w1_ds2781_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>"); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2781)); diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h new file mode 100644 index 00000000000..557dfb0b4f6 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.h @@ -0,0 +1,134 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Author: Renata Sayakhova <renata@oktetlabs.ru> + * + * Based on w1-ds2760 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _W1_DS2781_H +#define _W1_DS2781_H + +/* Function commands */ +#define W1_DS2781_READ_DATA 0x69 +#define W1_DS2781_WRITE_DATA 0x6C +#define W1_DS2781_COPY_DATA 0x48 +#define W1_DS2781_RECALL_DATA 0xB8 +#define W1_DS2781_LOCK 0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2781_STATUS 0x01 +#define DS2781_RAAC_MSB 0x02 +#define DS2781_RAAC_LSB 0x03 +#define DS2781_RSAC_MSB 0x04 +#define DS2781_RSAC_LSB 0x05 +#define DS2781_RARC 0x06 +#define DS2781_RSRC 0x07 +#define DS2781_IAVG_MSB 0x08 +#define DS2781_IAVG_LSB 0x09 +#define DS2781_TEMP_MSB 0x0A +#define DS2781_TEMP_LSB 0x0B +#define DS2781_VOLT_MSB 0x0C +#define DS2781_VOLT_LSB 0x0D +#define DS2781_CURRENT_MSB 0x0E +#define DS2781_CURRENT_LSB 0x0F +#define DS2781_ACR_MSB 0x10 +#define DS2781_ACR_LSB 0x11 +#define DS2781_ACRL_MSB 0x12 +#define DS2781_ACRL_LSB 0x13 +#define DS2781_AS 0x14 +#define DS2781_SFR 0x15 +#define DS2781_FULL_MSB 0x16 +#define DS2781_FULL_LSB 0x17 +#define DS2781_AE_MSB 0x18 +#define DS2781_AE_LSB 0x19 +#define DS2781_SE_MSB 0x1A +#define DS2781_SE_LSB 0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2781_EEPROM 0x1F +#define DS2781_EEPROM_BLOCK0_START 0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2781_EEPROM_BLOCK0_END 0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2781_EEPROM_BLOCK1_START 0x60 +#define DS2781_CONTROL 0x60 +#define DS2781_AB 0x61 +#define DS2781_AC_MSB 0x62 +#define DS2781_AC_LSB 0x63 +#define DS2781_VCHG 0x64 +#define DS2781_IMIN 0x65 +#define DS2781_VAE 0x66 +#define DS2781_IAE 0x67 +#define DS2781_AE_40 0x68 +#define DS2781_RSNSP 0x69 +#define DS2781_FULL_40_MSB 0x6A +#define DS2781_FULL_40_LSB 0x6B +#define DS2781_FULL_4_SLOPE 0x6C +#define DS2781_FULL_3_SLOPE 0x6D +#define DS2781_FULL_2_SLOPE 0x6E +#define DS2781_FULL_1_SLOPE 0x6F +#define DS2781_AE_4_SLOPE 0x70 +#define DS2781_AE_3_SLOPE 0x71 +#define DS2781_AE_2_SLOPE 0x72 +#define DS2781_AE_1_SLOPE 0x73 +#define DS2781_SE_4_SLOPE 0x74 +#define DS2781_SE_3_SLOPE 0x75 +#define DS2781_SE_2_SLOPE 0x76 +#define DS2781_SE_1_SLOPE 0x77 +#define DS2781_RSGAIN_MSB 0x78 +#define DS2781_RSGAIN_LSB 0x79 +#define DS2781_RSTC 0x7A +#define DS2781_COB 0x7B +#define DS2781_TBP34 0x7C +#define DS2781_TBP23 0x7D +#define DS2781_TBP12 0x7E +#define DS2781_EEPROM_BLOCK1_END 0x7F +/* Register 0x7D - 0xFF Reserved */ + +#define DS2781_FSGAIN_MSB 0xB0 +#define DS2781_FSGAIN_LSB 0xB1 + +/* Number of valid register addresses */ +#define DS2781_DATA_SIZE 0xB2 + +/* Status register bits */ +#define DS2781_STATUS_CHGTF (1 << 7) +#define DS2781_STATUS_AEF (1 << 6) +#define DS2781_STATUS_SEF (1 << 5) +#define DS2781_STATUS_LEARNF (1 << 4) +/* Bit 3 Reserved */ +#define DS2781_STATUS_UVF (1 << 2) +#define DS2781_STATUS_PORF (1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2781_CONTROL_NBEN (1 << 7) +#define DS2781_CONTROL_UVEN (1 << 6) +#define DS2781_CONTROL_PMOD (1 << 5) +#define DS2781_CONTROL_RNAOP (1 << 4) +#define DS1781_CONTROL_UVTH (1 << 3) +/* Bit 0 - 2 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2781_SFR_PIOSC (1 << 0) + +/* EEPROM register bits */ +#define DS2781_EEPROM_EEC (1 << 7) +#define DS2781_EEPROM_LOCK (1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2781_EEPROM_BL1 (1 << 1) +#define DS2781_EEPROM_BL0 (1 << 0) + +extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, + int io); +extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2781_H */ diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c new file mode 100644 index 00000000000..365d6dff21d --- /dev/null +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -0,0 +1,442 @@ +/* + * w1_ds28e04.c - w1 family 1C (DS28E04) driver + * + * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/crc16.h> +#include <linux/uaccess.h> + +#define CRC16_INIT 0 +#define CRC16_VALID 0xb001 + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>"); +MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04)); + +/* Allow the strong pullup to be disabled, but default to enabled. + * If it was disabled a parasite powered device might not get the required + * current to copy the data from the scratchpad to EEPROM. If it is enabled + * parasite powered devices have a better chance of getting the current + * required. + */ +static int w1_strong_pullup = 1; +module_param_named(strong_pullup, w1_strong_pullup, int, 0); + +/* enable/disable CRC checking on DS28E04-100 memory accesses */ +static char w1_enable_crccheck = 1; + +#define W1_EEPROM_SIZE 512 +#define W1_PAGE_COUNT 16 +#define W1_PAGE_SIZE 32 +#define W1_PAGE_BITS 5 +#define W1_PAGE_MASK 0x1F + +#define W1_F1C_READ_EEPROM 0xF0 +#define W1_F1C_WRITE_SCRATCH 0x0F +#define W1_F1C_READ_SCRATCH 0xAA +#define W1_F1C_COPY_SCRATCH 0x55 +#define W1_F1C_ACCESS_WRITE 0x5A + +#define W1_1C_REG_LOGIC_STATE 0x220 + +struct w1_f1C_data { + u8 memory[W1_EEPROM_SIZE]; + u32 validcrc; +}; + +/** + * Check the file size bounds and adjusts count as needed. + * This would not be needed if the file size didn't reset to 0 after a write. + */ +static inline size_t w1_f1C_fix_count(loff_t off, size_t count, size_t size) +{ + if (off > size) + return 0; + + if ((off + count) > size) + return size - off; + + return count; +} + +static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_data *data, + int block) +{ + u8 wrbuf[3]; + int off = block * W1_PAGE_SIZE; + + if (data->validcrc & (1 << block)) + return 0; + + if (w1_reset_select_slave(sl)) { + data->validcrc = 0; + return -EIO; + } + + wrbuf[0] = W1_F1C_READ_EEPROM; + wrbuf[1] = off & 0xff; + wrbuf[2] = off >> 8; + w1_write_block(sl->master, wrbuf, 3); + w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE); + + /* cache the block if the CRC is valid */ + if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) + data->validcrc |= (1 << block); + + return 0; +} + +static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data) +{ + u8 wrbuf[3]; + + /* read directly from the EEPROM */ + if (w1_reset_select_slave(sl)) + return -EIO; + + wrbuf[0] = W1_F1C_READ_EEPROM; + wrbuf[1] = addr & 0xff; + wrbuf[2] = addr >> 8; + + w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); + return w1_read_block(sl->master, data, len); +} + +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + struct w1_f1C_data *data = sl->family_data; + int i, min_page, max_page; + + count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->mutex); + + if (w1_enable_crccheck) { + min_page = (off >> W1_PAGE_BITS); + max_page = (off + count - 1) >> W1_PAGE_BITS; + for (i = min_page; i <= max_page; i++) { + if (w1_f1C_refresh_block(sl, data, i)) { + count = -EIO; + goto out_up; + } + } + memcpy(buf, &data->memory[off], count); + } else { + count = w1_f1C_read(sl, off, count, buf); + } + +out_up: + mutex_unlock(&sl->master->mutex); + + return count; +} + +/** + * Writes to the scratchpad and reads it back for verification. + * Then copies the scratchpad to EEPROM. + * The data must be on one page. + * The master must be locked. + * + * @param sl The slave structure + * @param addr Address for the write + * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK)) + * @param data The data to write + * @return 0=Success -1=failure + */ +static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data) +{ + u8 wrbuf[4]; + u8 rdbuf[W1_PAGE_SIZE + 3]; + u8 es = (addr + len - 1) & 0x1f; + unsigned int tm = 10; + int i; + struct w1_f1C_data *f1C = sl->family_data; + + /* Write the data to the scratchpad */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F1C_WRITE_SCRATCH; + wrbuf[1] = addr & 0xff; + wrbuf[2] = addr >> 8; + + w1_write_block(sl->master, wrbuf, 3); + w1_write_block(sl->master, data, len); + + /* Read the scratchpad and verify */ + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_8(sl->master, W1_F1C_READ_SCRATCH); + w1_read_block(sl->master, rdbuf, len + 3); + + /* Compare what was read against the data written */ + if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) || + (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0)) + return -1; + + /* Copy the scratchpad to EEPROM */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F1C_COPY_SCRATCH; + wrbuf[3] = es; + + for (i = 0; i < sizeof(wrbuf); ++i) { + /* issue 10ms strong pullup (or delay) on the last byte + for writing the data from the scratchpad to EEPROM */ + if (w1_strong_pullup && i == sizeof(wrbuf)-1) + w1_next_pullup(sl->master, tm); + + w1_write_8(sl->master, wrbuf[i]); + } + + if (!w1_strong_pullup) + msleep(tm); + + if (w1_enable_crccheck) { + /* invalidate cached data */ + f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS)); + } + + /* Reset the bus to wake up the EEPROM (this may not be needed) */ + w1_reset_bus(sl->master); + + return 0; +} + +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) + +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int addr, len, idx; + + count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE); + if (count == 0) + return 0; + + if (w1_enable_crccheck) { + /* can only write full blocks in cached mode */ + if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) { + dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n", + (int)off, count); + return -EINVAL; + } + + /* make sure the block CRCs are valid */ + for (idx = 0; idx < count; idx += W1_PAGE_SIZE) { + if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) + != CRC16_VALID) { + dev_err(&sl->dev, "bad CRC at offset %d\n", + (int)off); + return -EINVAL; + } + } + } + + mutex_lock(&sl->master->mutex); + + /* Can only write data to one page at a time */ + idx = 0; + while (idx < count) { + addr = off + idx; + len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK); + if (len > (count - idx)) + len = count - idx; + + if (w1_f1C_write(sl, addr, len, &buf[idx]) < 0) { + count = -EIO; + goto out_up; + } + idx += len; + } + +out_up: + mutex_unlock(&sl->master->mutex); + + return count; +} + +static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); + +static ssize_t pio_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) + +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int ret; + + /* check arguments */ + if (off != 0 || count != 1 || buf == NULL) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf); + mutex_unlock(&sl->master->mutex); + + return ret; +} + +static ssize_t pio_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) + +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + u8 wrbuf[3]; + u8 ack; + + /* check arguments */ + if (off != 0 || count != 1 || buf == NULL) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + + /* Write the PIO data */ + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->mutex); + return -1; + } + + /* set bit 7..2 to value '1' */ + *buf = *buf | 0xFC; + + wrbuf[0] = W1_F1C_ACCESS_WRITE; + wrbuf[1] = *buf; + wrbuf[2] = ~(*buf); + w1_write_block(sl->master, wrbuf, 3); + + w1_read_block(sl->master, &ack, sizeof(ack)); + + mutex_unlock(&sl->master->mutex); + + /* check for acknowledgement */ + if (ack != 0xAA) + return -EIO; + + return count; +} + +static BIN_ATTR_RW(pio, 1); + +static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + if (put_user(w1_enable_crccheck + 0x30, buf)) + return -EFAULT; + + return sizeof(w1_enable_crccheck); +} + +static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + char val; + + if (count != 1 || !buf) + return -EINVAL; + + if (get_user(val, buf)) + return -EFAULT; + + /* convert to decimal */ + val = val - 0x30; + if (val != 0 && val != 1) + return -EINVAL; + + /* set the new value */ + w1_enable_crccheck = val; + + return sizeof(w1_enable_crccheck); +} + +static DEVICE_ATTR_RW(crccheck); + +static struct attribute *w1_f1C_attrs[] = { + &dev_attr_crccheck.attr, + NULL, +}; + +static struct bin_attribute *w1_f1C_bin_attrs[] = { + &bin_attr_eeprom, + &bin_attr_pio, + NULL, +}; + +static const struct attribute_group w1_f1C_group = { + .attrs = w1_f1C_attrs, + .bin_attrs = w1_f1C_bin_attrs, +}; + +static const struct attribute_group *w1_f1C_groups[] = { + &w1_f1C_group, + NULL, +}; + +static int w1_f1C_add_slave(struct w1_slave *sl) +{ + struct w1_f1C_data *data = NULL; + + if (w1_enable_crccheck) { + data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + sl->family_data = data; + } + + return 0; +} + +static void w1_f1C_remove_slave(struct w1_slave *sl) +{ + kfree(sl->family_data); + sl->family_data = NULL; +} + +static struct w1_family_ops w1_f1C_fops = { + .add_slave = w1_f1C_add_slave, + .remove_slave = w1_f1C_remove_slave, + .groups = w1_f1C_groups, +}; + +static struct w1_family w1_family_1C = { + .fid = W1_FAMILY_DS28E04, + .fops = &w1_f1C_fops, +}; + +static int __init w1_f1C_init(void) +{ + return w1_register_family(&w1_family_1C); +} + +static void __exit w1_f1C_fini(void) +{ + w1_unregister_family(&w1_family_1C); +} + +module_init(w1_f1C_init); +module_exit(w1_f1C_fini); diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c index 84655625c87..ed4c87506de 100644 --- a/drivers/w1/slaves/w1_smem.c +++ b/drivers/w1/slaves/w1_smem.c @@ -34,6 +34,8 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family."); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_01)); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_81)); static struct w1_family w1_smem_family_01 = { .fid = W1_FAMILY_SMEM_01, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index ff29ae747ee..1f11a20a8ab 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -27,6 +27,7 @@ #include <linux/sched.h> #include <linux/device.h> #include <linux/types.h> +#include <linux/slab.h> #include <linux/delay.h> #include "../w1.h" @@ -36,39 +37,57 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family."); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825)); +MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00)); /* Allow the strong pullup to be disabled, but default to enabled. * If it was disabled a parasite powered device might not get the require * current to do a temperature conversion. If it is enabled parasite powered * devices have a better chance of getting the current required. + * In case the parasite power-detection is not working (seems to be the case + * for some DS18S20) the strong pullup can also be forced, regardless of the + * power state of the devices. + * + * Summary of options: + * - strong_pullup = 0 Disable strong pullup completely + * - strong_pullup = 1 Enable automatic strong pullup detection + * - strong_pullup = 2 Force strong pullup */ static int w1_strong_pullup = 1; module_param_named(strong_pullup, w1_strong_pullup, int, 0); -static u8 bad_roms[][9] = { - {0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87}, - {} - }; - -static ssize_t w1_therm_read(struct device *device, - struct device_attribute *attr, char *buf); - -static struct device_attribute w1_therm_attr = - __ATTR(w1_slave, S_IRUGO, w1_therm_read, NULL); - static int w1_therm_add_slave(struct w1_slave *sl) { - return device_create_file(&sl->dev, &w1_therm_attr); + sl->family_data = kzalloc(9, GFP_KERNEL); + if (!sl->family_data) + return -ENOMEM; + return 0; } static void w1_therm_remove_slave(struct w1_slave *sl) { - device_remove_file(&sl->dev, &w1_therm_attr); + kfree(sl->family_data); + sl->family_data = NULL; } +static ssize_t w1_slave_show(struct device *device, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR_RO(w1_slave); + +static struct attribute *w1_therm_attrs[] = { + &dev_attr_w1_slave.attr, + NULL, +}; +ATTRIBUTE_GROUPS(w1_therm); + static struct w1_family_ops w1_therm_fops = { .add_slave = w1_therm_add_slave, .remove_slave = w1_therm_remove_slave, + .groups = w1_therm_groups, }; static struct w1_family w1_therm_family_DS18S20 = { @@ -91,6 +110,11 @@ static struct w1_family w1_therm_family_DS28EA00 = { .fops = &w1_therm_fops, }; +static struct w1_family w1_therm_family_DS1825 = { + .fid = W1_THERM_DS1825, + .fops = &w1_therm_fops, +}; + struct w1_therm_family_converter { u8 broken; @@ -120,6 +144,10 @@ static struct w1_therm_family_converter w1_therm_families[] = { .f = &w1_therm_family_DS28EA00, .convert = w1_DS18B20_convert_temp }, + { + .f = &w1_therm_family_DS1825, + .convert = w1_DS18B20_convert_temp + } }; static inline int w1_DS18B20_convert_temp(u8 rom[9]) @@ -159,18 +187,8 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid) return 0; } -static int w1_therm_check_rom(u8 rom[9]) -{ - int i; - - for (i=0; i<sizeof(bad_roms)/9; ++i) - if (!memcmp(bad_roms[i], rom, 9)) - return 1; - return 0; -} - -static ssize_t w1_therm_read(struct device *device, +static ssize_t w1_slave_show(struct device *device, struct device_attribute *attr, char *buf) { struct w1_slave *sl = dev_to_w1_slave(device); @@ -179,16 +197,17 @@ static ssize_t w1_therm_read(struct device *device, int i, max_trying = 10; ssize_t c = PAGE_SIZE; - i = mutex_lock_interruptible(&dev->mutex); + i = mutex_lock_interruptible(&dev->bus_mutex); if (i != 0) return i; memset(rom, 0, sizeof(rom)); - verdict = 0; - crc = 0; - while (max_trying--) { + + verdict = 0; + crc = 0; + if (!w1_reset_select_slave(sl)) { int count = 0; unsigned int tm = 750; @@ -201,25 +220,26 @@ static ssize_t w1_therm_read(struct device *device, continue; /* 750ms strong pullup (or delay) after the convert */ - if (!external_power && w1_strong_pullup) + if (w1_strong_pullup == 2 || + (!external_power && w1_strong_pullup)) w1_next_pullup(dev, tm); w1_write_8(dev, W1_CONVERT_TEMP); if (external_power) { - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->bus_mutex); sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) return -EINTR; - i = mutex_lock_interruptible(&dev->mutex); + i = mutex_lock_interruptible(&dev->bus_mutex); if (i != 0) return i; } else if (!w1_strong_pullup) { sleep_rem = msleep_interruptible(tm); if (sleep_rem != 0) { - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->bus_mutex); return -EINTR; } } @@ -240,7 +260,7 @@ static ssize_t w1_therm_read(struct device *device, } } - if (!w1_therm_check_rom(rom)) + if (verdict) break; } @@ -249,16 +269,17 @@ static ssize_t w1_therm_read(struct device *device, c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", crc, (verdict) ? "YES" : "NO"); if (verdict) - memcpy(sl->rom, rom, sizeof(sl->rom)); + memcpy(sl->family_data, rom, sizeof(rom)); else - dev_warn(device, "18S20 doesn't respond to CONVERT_TEMP.\n"); + dev_warn(device, "Read failed CRC check\n"); for (i = 0; i < 9; ++i) - c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]); + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", + ((u8 *)sl->family_data)[i]); c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", w1_convert_temp(rom, sl->family->fid)); - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->bus_mutex); return PAGE_SIZE - c; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 9761950697b..5d734152054 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -46,18 +46,29 @@ MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol."); static int w1_timeout = 10; -int w1_max_slave_count = 10; +int w1_max_slave_count = 64; int w1_max_slave_ttl = 10; module_param_named(timeout, w1_timeout, int, 0); +MODULE_PARM_DESC(timeout, "time in seconds between automatic slave searches"); +/* A search stops when w1_max_slave_count devices have been found in that + * search. The next search will start over and detect the same set of devices + * on a static 1-wire bus. Memory is not allocated based on this number, just + * on the number of devices known to the kernel. Having a high number does not + * consume additional resources. As a special case, if there is only one + * device on the network and w1_max_slave_count is set to 1, the device id can + * be read directly skipping the normal slower search process. + */ module_param_named(max_slave_count, w1_max_slave_count, int, 0); +MODULE_PARM_DESC(max_slave_count, + "maximum number of slaves detected in a search"); module_param_named(slave_ttl, w1_max_slave_ttl, int, 0); +MODULE_PARM_DESC(slave_ttl, + "Number of searches not seeing a slave before it will be removed"); DEFINE_MUTEX(w1_mlock); LIST_HEAD(w1_masters); -static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); - static int w1_master_match(struct device *dev, struct device_driver *drv) { return 1; @@ -81,29 +92,21 @@ static void w1_slave_release(struct device *dev) { struct w1_slave *sl = dev_to_w1_slave(dev); - dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name); - - while (atomic_read(&sl->refcnt)) { - dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n", - sl->name, atomic_read(&sl->refcnt)); - if (msleep_interruptible(1000)) - flush_signals(current); - } + dev_dbg(dev, "%s: Releasing %s [%p]\n", __func__, sl->name, sl); w1_family_put(sl->family); sl->master->slave_count--; - - complete(&sl->released); } -static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct w1_slave *sl = dev_to_w1_slave(dev); return sprintf(buf, "%s\n", sl->name); } +static DEVICE_ATTR_RO(name); -static ssize_t w1_slave_read_id(struct device *dev, +static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct w1_slave *sl = dev_to_w1_slave(dev); @@ -112,17 +115,20 @@ static ssize_t w1_slave_read_id(struct device *dev, memcpy(buf, (u8 *)&sl->reg_num, count); return count; } +static DEVICE_ATTR_RO(id); -static struct device_attribute w1_slave_attr_name = - __ATTR(name, S_IRUGO, w1_slave_read_name, NULL); -static struct device_attribute w1_slave_attr_id = - __ATTR(id, S_IRUGO, w1_slave_read_id, NULL); +static struct attribute *w1_slave_attrs[] = { + &dev_attr_name.attr, + &dev_attr_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(w1_slave); /* Default family */ -static ssize_t w1_default_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t rw_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -139,9 +145,9 @@ out_up: return count; } -static ssize_t w1_default_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static ssize_t rw_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -151,29 +157,24 @@ static ssize_t w1_default_read(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute w1_default_attr = { - .attr = { - .name = "rw", - .mode = S_IRUGO | S_IWUSR, - }, - .size = PAGE_SIZE, - .read = w1_default_read, - .write = w1_default_write, +static BIN_ATTR_RW(rw, PAGE_SIZE); + +static struct bin_attribute *w1_slave_bin_attrs[] = { + &bin_attr_rw, + NULL, }; -static int w1_default_add_slave(struct w1_slave *sl) -{ - return sysfs_create_bin_file(&sl->dev.kobj, &w1_default_attr); -} +static const struct attribute_group w1_slave_default_group = { + .bin_attrs = w1_slave_bin_attrs, +}; -static void w1_default_remove_slave(struct w1_slave *sl) -{ - sysfs_remove_bin_file(&sl->dev.kobj, &w1_default_attr); -} +static const struct attribute_group *w1_slave_default_groups[] = { + &w1_slave_default_group, + NULL, +}; static struct w1_family_ops w1_default_fops = { - .add_slave = w1_default_add_slave, - .remove_slave = w1_default_remove_slave, + .groups = w1_slave_default_groups, }; static struct w1_family w1_default_family = { @@ -235,14 +236,18 @@ static ssize_t w1_master_attribute_store_search(struct device * dev, { long tmp; struct w1_master *md = dev_to_w1_master(dev); + int ret; - if (strict_strtol(buf, 0, &tmp) == -EINVAL) - return -EINVAL; + ret = kstrtol(buf, 0, &tmp); + if (ret) + return ret; mutex_lock(&md->mutex); md->search_count = tmp; mutex_unlock(&md->mutex); - wake_up_process(md->thread); + /* Only wake if it is going to be searching. */ + if (tmp) + wake_up_process(md->thread); return count; } @@ -267,14 +272,15 @@ static ssize_t w1_master_attribute_store_pullup(struct device *dev, { long tmp; struct w1_master *md = dev_to_w1_master(dev); + int ret; - if (strict_strtol(buf, 0, &tmp) == -EINVAL) - return -EINVAL; + ret = kstrtol(buf, 0, &tmp); + if (ret) + return ret; mutex_lock(&md->mutex); md->enable_pullup = tmp; mutex_unlock(&md->mutex); - wake_up_process(md->thread); return count; } @@ -311,6 +317,24 @@ static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct devic return count; } +static ssize_t w1_master_attribute_store_max_slave_count(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int tmp; + struct w1_master *md = dev_to_w1_master(dev); + + if (kstrtoint(buf, 0, &tmp) == -EINVAL || tmp < 1) + return -EINVAL; + + mutex_lock(&md->mutex); + md->max_slave_count = tmp; + /* allow each time the max_slave_count is updated */ + clear_bit(W1_WARN_MAX_COUNT, &md->flags); + mutex_unlock(&md->mutex); + + return count; +} + static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf) { struct w1_master *md = dev_to_w1_master(dev); @@ -349,23 +373,20 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, { struct w1_master *md = dev_to_w1_master(dev); int c = PAGE_SIZE; + struct list_head *ent, *n; + struct w1_slave *sl = NULL; - mutex_lock(&md->mutex); + mutex_lock(&md->list_mutex); - if (md->slave_count == 0) - c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); - else { - struct list_head *ent, *n; - struct w1_slave *sl; + list_for_each_safe(ent, n, &md->slist) { + sl = list_entry(ent, struct w1_slave, w1_slave_entry); - list_for_each_safe(ent, n, &md->slist) { - sl = list_entry(ent, struct w1_slave, w1_slave_entry); - - c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); - } + c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); } + if (!sl) + c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); - mutex_unlock(&md->mutex); + mutex_unlock(&md->list_mutex); return PAGE_SIZE - c; } @@ -419,19 +440,22 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, } /* Searches the slaves in the w1_master and returns a pointer or NULL. - * Note: must hold the mutex + * Note: must not hold list_mutex */ -static struct w1_slave *w1_slave_search_device(struct w1_master *dev, +struct w1_slave *w1_slave_search_device(struct w1_master *dev, struct w1_reg_num *rn) { struct w1_slave *sl; + mutex_lock(&dev->list_mutex); list_for_each_entry(sl, &dev->slist, w1_slave_entry) { if (sl->reg_num.family == rn->family && sl->reg_num.id == rn->id && sl->reg_num.crc == rn->crc) { + mutex_unlock(&dev->list_mutex); return sl; } } + mutex_unlock(&dev->list_mutex); return NULL; } @@ -488,7 +512,10 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev, mutex_lock(&md->mutex); sl = w1_slave_search_device(md, &rn); if (sl) { - w1_slave_detach(sl); + result = w1_slave_detach(sl); + /* refcnt 0 means it was detached in the call */ + if (result == 0) + result = count; } else { dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family, (unsigned long long)rn.id); @@ -513,7 +540,7 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev, static W1_MASTER_ATTR_RO(name, S_IRUGO); static W1_MASTER_ATTR_RO(slaves, S_IRUGO); static W1_MASTER_ATTR_RO(slave_count, S_IRUGO); -static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO); +static W1_MASTER_ATTR_RW(max_slave_count, S_IRUGO | S_IWUSR | S_IWGRP); static W1_MASTER_ATTR_RO(attempts, S_IRUGO); static W1_MASTER_ATTR_RO(timeout, S_IRUGO); static W1_MASTER_ATTR_RO(pointer, S_IRUGO); @@ -551,13 +578,12 @@ void w1_destroy_master_attributes(struct w1_master *master) sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group); } -#ifdef CONFIG_HOTPLUG static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) { struct w1_master *md = NULL; struct w1_slave *sl = NULL; char *event_owner, *name; - int err; + int err = 0; if (dev->driver == &w1_master_driver) { md = container_of(dev, struct w1_master, dev); @@ -576,25 +602,60 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) event_owner, name, dev_name(dev)); if (dev->driver != &w1_slave_driver || !sl) - return 0; + goto end; err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family); if (err) - return err; + goto end; err = add_uevent_var(env, "W1_SLAVE_ID=%024LX", (unsigned long long)sl->reg_num.id); - if (err) - return err; +end: + return err; +} - return 0; -}; -#else -static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) +static int w1_family_notify(unsigned long action, struct w1_slave *sl) { + struct w1_family_ops *fops; + int err; + + fops = sl->family->fops; + + if (!fops) + return 0; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + /* if the family driver needs to initialize something... */ + if (fops->add_slave) { + err = fops->add_slave(sl); + if (err < 0) { + dev_err(&sl->dev, + "add_slave() call failed. err=%d\n", + err); + return err; + } + } + if (fops->groups) { + err = sysfs_create_groups(&sl->dev.kobj, fops->groups); + if (err) { + dev_err(&sl->dev, + "sysfs group creation failed. err=%d\n", + err); + return err; + } + } + + break; + case BUS_NOTIFY_DEL_DEVICE: + if (fops->remove_slave) + sl->family->fops->remove_slave(sl); + if (fops->groups) + sysfs_remove_groups(&sl->dev.kobj, fops->groups); + break; + } return 0; } -#endif static int __w1_attach_slave_device(struct w1_slave *sl) { @@ -604,6 +665,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) sl->dev.driver = &w1_slave_driver; sl->dev.bus = &w1_bus_type; sl->dev.release = &w1_slave_release; + sl->dev.groups = w1_slave_groups; dev_set_name(&sl->dev, "%02x-%012llx", (unsigned int) sl->reg_num.family, @@ -616,6 +678,9 @@ static int __w1_attach_slave_device(struct w1_slave *sl) dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__, dev_name(&sl->dev), sl); + /* suppress for w1_family_notify before sending KOBJ_ADD */ + dev_set_uevent_suppress(&sl->dev, true); + err = device_register(&sl->dev); if (err < 0) { dev_err(&sl->dev, @@ -623,48 +688,19 @@ static int __w1_attach_slave_device(struct w1_slave *sl) dev_name(&sl->dev), err); return err; } + w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl); - /* Create "name" entry */ - err = device_create_file(&sl->dev, &w1_slave_attr_name); - if (err < 0) { - dev_err(&sl->dev, - "sysfs file creation for [%s] failed. err=%d\n", - dev_name(&sl->dev), err); - goto out_unreg; - } - - /* Create "id" entry */ - err = device_create_file(&sl->dev, &w1_slave_attr_id); - if (err < 0) { - dev_err(&sl->dev, - "sysfs file creation for [%s] failed. err=%d\n", - dev_name(&sl->dev), err); - goto out_rem1; - } - - /* if the family driver needs to initialize something... */ - if (sl->family->fops && sl->family->fops->add_slave && - ((err = sl->family->fops->add_slave(sl)) < 0)) { - dev_err(&sl->dev, - "sysfs file creation for [%s] failed. err=%d\n", - dev_name(&sl->dev), err); - goto out_rem2; - } + dev_set_uevent_suppress(&sl->dev, false); + kobject_uevent(&sl->dev.kobj, KOBJ_ADD); + mutex_lock(&sl->master->list_mutex); list_add_tail(&sl->w1_slave_entry, &sl->master->slist); + mutex_unlock(&sl->master->list_mutex); return 0; - -out_rem2: - device_remove_file(&sl->dev, &w1_slave_attr_id); -out_rem1: - device_remove_file(&sl->dev, &w1_slave_attr_name); -out_unreg: - device_unregister(&sl->dev); - return err; } -static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) +int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) { struct w1_slave *sl; struct w1_family *f; @@ -682,12 +718,17 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) sl->owner = THIS_MODULE; sl->master = dev; - set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); + set_bit(W1_SLAVE_ACTIVE, &sl->flags); memset(&msg, 0, sizeof(msg)); memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); - atomic_set(&sl->refcnt, 0); - init_completion(&sl->released); + atomic_set(&sl->refcnt, 1); + atomic_inc(&sl->master->refcnt); + + /* slave modules need to be loaded in a context with unlocked mutex */ + mutex_unlock(&dev->mutex); + request_module("w1-family-0x%0x", rn->family); + mutex_lock(&dev->mutex); spin_lock(&w1_flock); f = w1_family_registered(rn->family); @@ -722,28 +763,49 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) return 0; } -void w1_slave_detach(struct w1_slave *sl) +int w1_unref_slave(struct w1_slave *sl) { - struct w1_netlink_msg msg; - - dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl); - - list_del(&sl->w1_slave_entry); - - if (sl->family->fops && sl->family->fops->remove_slave) - sl->family->fops->remove_slave(sl); - - memset(&msg, 0, sizeof(msg)); - memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); - msg.type = W1_SLAVE_REMOVE; - w1_netlink_send(sl->master, &msg); - - device_remove_file(&sl->dev, &w1_slave_attr_id); - device_remove_file(&sl->dev, &w1_slave_attr_name); - device_unregister(&sl->dev); + struct w1_master *dev = sl->master; + int refcnt; + mutex_lock(&dev->list_mutex); + refcnt = atomic_sub_return(1, &sl->refcnt); + if (refcnt == 0) { + struct w1_netlink_msg msg; + + dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, + sl->name, sl); + + list_del(&sl->w1_slave_entry); + + memset(&msg, 0, sizeof(msg)); + memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); + msg.type = W1_SLAVE_REMOVE; + w1_netlink_send(sl->master, &msg); + + w1_family_notify(BUS_NOTIFY_DEL_DEVICE, sl); + device_unregister(&sl->dev); + #ifdef DEBUG + memset(sl, 0, sizeof(*sl)); + #endif + kfree(sl); + } + atomic_dec(&dev->refcnt); + mutex_unlock(&dev->list_mutex); + return refcnt; +} - wait_for_completion(&sl->released); - kfree(sl); +int w1_slave_detach(struct w1_slave *sl) +{ + /* Only detach a slave once as it decreases the refcnt each time. */ + int destroy_now; + mutex_lock(&sl->master->list_mutex); + destroy_now = !test_bit(W1_SLAVE_DETACH, &sl->flags); + set_bit(W1_SLAVE_DETACH, &sl->flags); + mutex_unlock(&sl->master->list_mutex); + + if (destroy_now) + destroy_now = !w1_unref_slave(sl); + return destroy_now ? 0 : -EBUSY; } struct w1_master *w1_search_master_id(u32 id) @@ -772,7 +834,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) mutex_lock(&w1_mlock); list_for_each_entry(dev, &w1_masters, w1_master_entry) { - mutex_lock(&dev->mutex); + mutex_lock(&dev->list_mutex); list_for_each_entry(sl, &dev->slist, w1_slave_entry) { if (sl->reg_num.family == id->family && sl->reg_num.id == id->id && @@ -783,7 +845,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) break; } } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->list_mutex); if (found) break; @@ -803,6 +865,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) dev_dbg(&dev->dev, "Reconnecting slaves in device %s " "for family %02x.\n", dev->name, f->fid); mutex_lock(&dev->mutex); + mutex_lock(&dev->list_mutex); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { /* If it is a new family, slaves with the default * family driver and are that family will be @@ -814,14 +877,19 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) (!attach && sl->family->fid == f->fid)) { struct w1_reg_num rn; + mutex_unlock(&dev->list_mutex); memcpy(&rn, &sl->reg_num, sizeof(rn)); - w1_slave_detach(sl); - - w1_attach_slave_device(dev, &rn); + /* If it was already in use let the automatic + * scan pick it up again later. + */ + if (!w1_slave_detach(sl)) + w1_attach_slave_device(dev, &rn); + mutex_lock(&dev->list_mutex); } } dev_dbg(&dev->dev, "Reconnecting slaves in device %s " "has been finished.\n", dev->name); + mutex_unlock(&dev->list_mutex); mutex_unlock(&dev->mutex); } mutex_unlock(&w1_mlock); @@ -839,7 +907,7 @@ void w1_slave_found(struct w1_master *dev, u64 rn) sl = w1_slave_search_device(dev, tmp); if (sl) { - set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); + set_bit(W1_SLAVE_ACTIVE, &sl->flags); } else { if (rn && tmp->crc == w1_calc_crc8((u8 *)&rn_le, 7)) w1_attach_slave_device(dev, tmp); @@ -849,7 +917,12 @@ void w1_slave_found(struct w1_master *dev, u64 rn) } /** - * Performs a ROM Search & registers any devices found. + * w1_search() - Performs a ROM Search & registers any devices found. + * @dev: The master device to search + * @search_type: W1_SEARCH to search all devices, or W1_ALARM_SEARCH + * to return only devices in the alarmed state + * @cb: Function to call when a device is found + * * The 1-wire search is a simple binary tree search. * For each bit of the address, we read two bits and write one bit. * The bit written will put to sleep all devies that don't match that bit. @@ -859,8 +932,6 @@ void w1_slave_found(struct w1_master *dev, u64 rn) * * See "Application note 187 1-wire search algorithm" at www.maxim-ic.com * - * @dev The master device to search - * @cb Function to call when a device is found */ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb) { @@ -871,7 +942,8 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb u8 triplet_ret = 0; search_bit = 0; - rn = last_rn = 0; + rn = dev->search_id; + last_rn = 0; last_device = 0; last_zero = -1; @@ -887,16 +959,21 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb * * Return 0 - device(s) present, 1 - no devices present. */ + mutex_lock(&dev->bus_mutex); if (w1_reset_bus(dev)) { + mutex_unlock(&dev->bus_mutex); dev_dbg(&dev->dev, "No devices present on the wire.\n"); break; } /* Do fast search on single slave bus */ if (dev->max_slave_count == 1) { + int rv; w1_write_8(dev, W1_READ_ROM); + rv = w1_read_block(dev, (u8 *)&rn, 8); + mutex_unlock(&dev->bus_mutex); - if (w1_read_block(dev, (u8 *)&rn, 8) == 8 && rn) + if (rv == 8 && rn) cb(dev, rn); break; @@ -913,7 +990,7 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb else search_bit = ((last_rn >> i) & 0x1); - /** Read two bits and write one bit */ + /* Read two bits and write one bit */ triplet_ret = w1_triplet(dev, search_bit); /* quit if no device responded */ @@ -928,18 +1005,39 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb tmp64 = (triplet_ret >> 2); rn |= (tmp64 << i); - if (kthread_should_stop()) { + if (test_bit(W1_ABORT_SEARCH, &dev->flags)) { + mutex_unlock(&dev->bus_mutex); dev_dbg(&dev->dev, "Abort w1_search\n"); return; } } + mutex_unlock(&dev->bus_mutex); if ( (triplet_ret & 0x03) != 0x03 ) { - if ( (desc_bit == last_zero) || (last_zero < 0)) + if ((desc_bit == last_zero) || (last_zero < 0)) { last_device = 1; + dev->search_id = 0; + } else { + dev->search_id = rn; + } desc_bit = last_zero; cb(dev, rn); } + + if (!last_device && slave_count == dev->max_slave_count && + !test_bit(W1_WARN_MAX_COUNT, &dev->flags)) { + /* Only max_slave_count will be scanned in a search, + * but it will start where it left off next search + * until all ids are identified and then it will start + * over. A continued search will report the previous + * last id as the first id (provided it is still on the + * bus). + */ + dev_info(&dev->dev, "%s: max_slave_count %d reached, " + "will continue next search.\n", __func__, + dev->max_slave_count); + set_bit(W1_WARN_MAX_COUNT, &dev->flags); + } } } @@ -948,17 +1046,24 @@ void w1_search_process_cb(struct w1_master *dev, u8 search_type, { struct w1_slave *sl, *sln; + mutex_lock(&dev->list_mutex); list_for_each_entry(sl, &dev->slist, w1_slave_entry) - clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); + clear_bit(W1_SLAVE_ACTIVE, &sl->flags); + mutex_unlock(&dev->list_mutex); w1_search_devices(dev, search_type, cb); + mutex_lock(&dev->list_mutex); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { - if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl) + if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) { + mutex_unlock(&dev->list_mutex); w1_slave_detach(sl); - else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags)) + mutex_lock(&dev->list_mutex); + } + else if (test_bit(W1_SLAVE_ACTIVE, &sl->flags)) sl->ttl = dev->slave_ttl; } + mutex_unlock(&dev->list_mutex); if (dev->search_count > 0) dev->search_count--; @@ -969,6 +1074,34 @@ static void w1_search_process(struct w1_master *dev, u8 search_type) w1_search_process_cb(dev, search_type, w1_slave_found); } +/** + * w1_process_callbacks() - execute each dev->async_list callback entry + * @dev: w1_master device + * + * The w1 master list_mutex must be held. + * + * Return: 1 if there were commands to executed 0 otherwise + */ +int w1_process_callbacks(struct w1_master *dev) +{ + int ret = 0; + struct w1_async_cmd *async_cmd, *async_n; + + /* The list can be added to in another thread, loop until it is empty */ + while (!list_empty(&dev->async_list)) { + list_for_each_entry_safe(async_cmd, async_n, &dev->async_list, + async_entry) { + /* drop the lock, if it is a search it can take a long + * time */ + mutex_unlock(&dev->list_mutex); + async_cmd->cb(dev, async_cmd); + ret = 1; + mutex_lock(&dev->list_mutex); + } + } + return ret; +} + int w1_process(void *data) { struct w1_master *dev = (struct w1_master *) data; @@ -976,23 +1109,46 @@ int w1_process(void *data) * time can be calculated in jiffies once. */ const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000); + /* remainder if it woke up early */ + unsigned long jremain = 0; - while (!kthread_should_stop()) { - if (dev->search_count) { + for (;;) { + + if (!jremain && dev->search_count) { mutex_lock(&dev->mutex); w1_search_process(dev, W1_SEARCH); mutex_unlock(&dev->mutex); } + mutex_lock(&dev->list_mutex); + /* Note, w1_process_callback drops the lock while processing, + * but locks it again before returning. + */ + if (!w1_process_callbacks(dev) && jremain) { + /* a wake up is either to stop the thread, process + * callbacks, or search, it isn't process callbacks, so + * schedule a search. + */ + jremain = 1; + } + try_to_freeze(); __set_current_state(TASK_INTERRUPTIBLE); + /* hold list_mutex until after interruptible to prevent loosing + * the wakeup signal when async_cmd is added. + */ + mutex_unlock(&dev->list_mutex); + if (kthread_should_stop()) break; /* Only sleep when the search is active. */ - if (dev->search_count) - schedule_timeout(jtime); + if (dev->search_count) { + if (!jremain) + jremain = jtime; + jremain = schedule_timeout(jremain); + } else schedule(); } @@ -1027,7 +1183,7 @@ static int __init w1_init(void) retval = driver_register(&w1_slave_driver); if (retval) { printk(KERN_ERR - "Failed to register master driver. err=%d.\n", + "Failed to register slave driver. err=%d.\n", retval); goto err_out_master_unregister; } diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 4d012ca3f32..56a49ba41d8 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -22,6 +22,13 @@ #ifndef __W1_H #define __W1_H +/** + * struct w1_reg_num - broken out slave device id + * + * @family: identifies the type of device + * @id: along with family is the unique device id + * @crc: checksum of the other bytes + */ struct w1_reg_num { #if defined(__LITTLE_ENDIAN_BITFIELD) @@ -58,7 +65,24 @@ struct w1_reg_num #define W1_RESUME_CMD 0xA5 #define W1_SLAVE_ACTIVE 0 +#define W1_SLAVE_DETACH 1 +/** + * struct w1_slave - holds a single slave device on the bus + * + * @owner: Points to the one wire "wire" kernel module. + * @name: Device id is ascii. + * @w1_slave_entry: data for the linked list + * @reg_num: the slave id in binary + * @refcnt: reference count, delete when 0 + * @flags: bit flags for W1_SLAVE_ACTIVE W1_SLAVE_DETACH + * @ttl: decrement per search this slave isn't found, deatch at 0 + * @master: bus which this slave is on + * @family: module for device family type + * @family_data: pointer for use by the family module + * @dev: kernel device identifier + * + */ struct w1_slave { struct module *owner; @@ -66,120 +90,166 @@ struct w1_slave struct list_head w1_slave_entry; struct w1_reg_num reg_num; atomic_t refcnt; - u8 rom[9]; - u32 flags; int ttl; + unsigned long flags; struct w1_master *master; struct w1_family *family; void *family_data; struct device dev; - struct completion released; }; typedef void (*w1_slave_found_callback)(struct w1_master *, u64); /** + * struct w1_bus_master - operations available on a bus master + * + * @data: the first parameter in all the functions below + * + * @read_bit: Sample the line level @return the level read (0 or 1) + * + * @write_bit: Sets the line level + * + * @touch_bit: the lowest-level function for devices that really support the + * 1-wire protocol. + * touch_bit(0) = write-0 cycle + * touch_bit(1) = write-1 / read cycle + * @return the bit read (0 or 1) + * + * @read_byte: Reads a bytes. Same as 8 touch_bit(1) calls. + * @return the byte read + * + * @write_byte: Writes a byte. Same as 8 touch_bit(x) calls. + * + * @read_block: Same as a series of read_byte() calls + * @return the number of bytes read + * + * @write_block: Same as a series of write_byte() calls + * + * @triplet: Combines two reads and a smart write for ROM searches + * @return bit0=Id bit1=comp_id bit2=dir_taken + * + * @reset_bus: long write-0 with a read for the presence pulse detection + * @return -1=Error, 0=Device present, 1=No device present + * + * @set_pullup: Put out a strong pull-up pulse of the specified duration. + * @return -1=Error, 0=completed + * + * @search: Really nice hardware can handles the different types of ROM search + * w1_master* is passed to the slave found callback. + * u8 is search_type, W1_SEARCH or W1_ALARM_SEARCH + * * Note: read_bit and write_bit are very low level functions and should only * be used with hardware that doesn't really support 1-wire operations, * like a parallel/serial port. * Either define read_bit and write_bit OR define, at minimum, touch_bit and * reset_bus. + * */ struct w1_bus_master { - /** the first parameter in all the functions below */ void *data; - /** - * Sample the line level - * @return the level read (0 or 1) - */ u8 (*read_bit)(void *); - /** Sets the line level */ void (*write_bit)(void *, u8); - /** - * touch_bit is the lowest-level function for devices that really - * support the 1-wire protocol. - * touch_bit(0) = write-0 cycle - * touch_bit(1) = write-1 / read cycle - * @return the bit read (0 or 1) - */ u8 (*touch_bit)(void *, u8); - /** - * Reads a bytes. Same as 8 touch_bit(1) calls. - * @return the byte read - */ u8 (*read_byte)(void *); - /** - * Writes a byte. Same as 8 touch_bit(x) calls. - */ void (*write_byte)(void *, u8); - /** - * Same as a series of read_byte() calls - * @return the number of bytes read - */ u8 (*read_block)(void *, u8 *, int); - /** Same as a series of write_byte() calls */ void (*write_block)(void *, const u8 *, int); - /** - * Combines two reads and a smart write for ROM searches - * @return bit0=Id bit1=comp_id bit2=dir_taken - */ u8 (*triplet)(void *, u8); - /** - * long write-0 with a read for the presence pulse detection - * @return -1=Error, 0=Device present, 1=No device present - */ u8 (*reset_bus)(void *); - /** - * Put out a strong pull-up pulse of the specified duration. - * @return -1=Error, 0=completed - */ u8 (*set_pullup)(void *, int); - /** Really nice hardware can handles the different types of ROM search - * w1_master* is passed to the slave found callback. - */ void (*search)(void *, struct w1_master *, u8, w1_slave_found_callback); }; +/** + * enum w1_master_flags - bitfields used in w1_master.flags + * @W1_ABORT_SEARCH: abort searching early on shutdown + * @W1_WARN_MAX_COUNT: limit warning when the maximum count is reached + */ +enum w1_master_flags { + W1_ABORT_SEARCH = 0, + W1_WARN_MAX_COUNT = 1, +}; + +/** + * struct w1_master - one per bus master + * @w1_master_entry: master linked list + * @owner: module owner + * @name: dynamically allocate bus name + * @list_mutex: protect slist and async_list + * @slist: linked list of slaves + * @async_list: linked list of netlink commands to execute + * @max_slave_count: maximum number of slaves to search for at a time + * @slave_count: current number of slaves known + * @attempts: number of searches ran + * @slave_ttl: number of searches before a slave is timed out + * @initialized: prevent init/removal race conditions + * @id: w1 bus number + * @search_count: number of automatic searches to run, -1 unlimited + * @search_id: allows continuing a search + * @refcnt: reference count + * @priv: private data storage + * @enable_pullup: allows a strong pullup + * @pullup_duration: time for the next strong pullup + * @flags: one of w1_master_flags + * @thread: thread for bus search and netlink commands + * @mutex: protect most of w1_master + * @bus_mutex: pretect concurrent bus access + * @driver: sysfs driver + * @dev: sysfs device + * @bus_master: io operations available + * @seq: sequence number used for netlink broadcasts + */ struct w1_master { struct list_head w1_master_entry; struct module *owner; unsigned char name[W1_MAXNAMELEN]; + /* list_mutex protects just slist and async_list so slaves can be + * searched for and async commands added while the master has + * w1_master.mutex locked and is operating on the bus. + * lock order w1_mlock, w1_master.mutex, w1_master.list_mutex + */ + struct mutex list_mutex; struct list_head slist; + struct list_head async_list; int max_slave_count, slave_count; unsigned long attempts; int slave_ttl; int initialized; u32 id; int search_count; + /* id to start searching on, to continue a search or 0 to restart */ + u64 search_id; atomic_t refcnt; void *priv; - int priv_size; /** 5V strong pullup enabled flag, 1 enabled, zero disabled. */ int enable_pullup; /** 5V strong pullup duration in milliseconds, zero disabled. */ int pullup_duration; + long flags; + struct task_struct *thread; struct mutex mutex; + struct mutex bus_mutex; struct device_driver *driver; struct device dev; @@ -189,14 +259,34 @@ struct w1_master u32 seq; }; +/** + * struct w1_async_cmd - execute callback from the w1_process kthread + * @async_entry: link entry + * @cb: callback function, must list_del and destroy this list before + * returning + * + * When inserted into the w1_master async_list, w1_process will execute + * the callback. Embed this into the structure with the command details. + */ +struct w1_async_cmd { + struct list_head async_entry; + void (*cb)(struct w1_master *dev, struct w1_async_cmd *async_cmd); +}; + int w1_create_master_attributes(struct w1_master *); void w1_destroy_master_attributes(struct w1_master *master); void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +/* call w1_unref_slave to release the reference counts w1_search_slave added */ struct w1_slave *w1_search_slave(struct w1_reg_num *id); +/* decrements the reference on sl->master and sl, and cleans up if zero + * returns the reference count after it has been decremented */ +int w1_unref_slave(struct w1_slave *sl); void w1_slave_found(struct w1_master *dev, u64 rn); void w1_search_process_cb(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +struct w1_slave *w1_slave_search_device(struct w1_master *dev, + struct w1_reg_num *rn); struct w1_master *w1_search_master_id(u32 id); /* Disconnect and reconnect devices in the given family. Used for finding @@ -205,7 +295,9 @@ struct w1_master *w1_search_master_id(u32 id); * has just been registered, to 0 when it has been unregistered. */ void w1_reconnect_slaves(struct w1_family *f, int attach); -void w1_slave_detach(struct w1_slave *sl); +int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); +/* 0 success, otherwise EBUSY */ +int w1_slave_detach(struct w1_slave *sl); u8 w1_triplet(struct w1_master *dev, int bdir); void w1_write_8(struct w1_master *, u8); @@ -241,6 +333,7 @@ extern int w1_max_slave_ttl; extern struct list_head w1_masters; extern struct mutex w1_mlock; +extern int w1_process_callbacks(struct w1_master *dev); extern int w1_process(void *); #endif /* __KERNEL__ */ diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index e9309778ee7..3651ec801f4 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -31,6 +31,10 @@ DEFINE_SPINLOCK(w1_flock); static LIST_HEAD(w1_families); +/** + * w1_register_family() - register a device family driver + * @newf: family to register + */ int w1_register_family(struct w1_family *newf) { struct list_head *ent, *n; @@ -59,6 +63,10 @@ int w1_register_family(struct w1_family *newf) return ret; } +/** + * w1_unregister_family() - unregister a device family driver + * @fent: family to unregister + */ void w1_unregister_family(struct w1_family *fent) { struct list_head *ent, *n; @@ -131,9 +139,9 @@ void w1_family_get(struct w1_family *f) void __w1_family_get(struct w1_family *f) { - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&f->refcnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } EXPORT_SYMBOL(w1_unregister_family); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 490cda2281b..26ca1343055 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -30,6 +30,7 @@ #define W1_FAMILY_SMEM_01 0x01 #define W1_FAMILY_SMEM_81 0x81 #define W1_THERM_DS18S20 0x10 +#define W1_FAMILY_DS28E04 0x1C #define W1_COUNTER_DS2423 0x1D #define W1_THERM_DS1822 0x22 #define W1_EEPROM_DS2433 0x23 @@ -38,18 +39,35 @@ #define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 #define W1_FAMILY_DS2780 0x32 +#define W1_FAMILY_DS2413 0x3A +#define W1_THERM_DS1825 0x3B +#define W1_FAMILY_DS2781 0x3D #define W1_THERM_DS28EA00 0x42 #define MAXNAMELEN 32 struct w1_slave; +/** + * struct w1_family_ops - operations for a family type + * @add_slave: add_slave + * @remove_slave: remove_slave + * @groups: sysfs group + */ struct w1_family_ops { int (* add_slave)(struct w1_slave *); void (* remove_slave)(struct w1_slave *); + const struct attribute_group **groups; }; +/** + * struct w1_family - reference counted family structure. + * @family_entry: family linked list + * @fid: 8 bit family identifier + * @fops: operations for this family + * @refcnt: reference counter + */ struct w1_family { struct list_head family_entry; diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 68288355727..728039d2efe 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -75,7 +75,10 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, atomic_set(&dev->refcnt, 2); INIT_LIST_HEAD(&dev->slist); + INIT_LIST_HEAD(&dev->async_list); mutex_init(&dev->mutex); + mutex_init(&dev->bus_mutex); + mutex_init(&dev->list_mutex); memcpy(&dev->dev, device, sizeof(struct device)); dev_set_name(&dev->dev, "w1_bus_master%u", dev->id); @@ -102,6 +105,10 @@ static void w1_free_dev(struct w1_master *dev) device_unregister(&dev->dev); } +/** + * w1_add_master_device() - registers a new master device + * @master: master bus device to register + */ int w1_add_master_device(struct w1_bus_master *master) { struct w1_master *dev, *entry; @@ -116,18 +123,6 @@ int w1_add_master_device(struct w1_bus_master *master) printk(KERN_ERR "w1_add_master_device: invalid function set\n"); return(-EINVAL); } - /* While it would be electrically possible to make a device that - * generated a strong pullup in bit bang mode, only hardare that - * controls 1-wire time frames are even expected to support a strong - * pullup. w1_io.c would need to support calling set_pullup before - * the last write_bit operation of a w1_write_8 which it currently - * doesn't. - */ - if (!master->write_byte && !master->touch_bit && master->set_pullup) { - printk(KERN_ERR "w1_add_master_device: set_pullup requires " - "write_byte or touch_bit, disabling\n"); - master->set_pullup = NULL; - } /* Lock until the device is added (or not) to w1_masters. */ mutex_lock(&w1_mlock); @@ -183,6 +178,7 @@ int w1_add_master_device(struct w1_bus_master *master) #if 0 /* Thread cleanup code, not required currently. */ err_out_kill_thread: + set_bit(W1_ABORT_SEARCH, &dev->flags); kthread_stop(dev->thread); #endif err_out_rm_attr: @@ -198,16 +194,22 @@ void __w1_remove_master_device(struct w1_master *dev) struct w1_netlink_msg msg; struct w1_slave *sl, *sln; - kthread_stop(dev->thread); - mutex_lock(&w1_mlock); list_del(&dev->w1_master_entry); mutex_unlock(&w1_mlock); + set_bit(W1_ABORT_SEARCH, &dev->flags); + kthread_stop(dev->thread); + mutex_lock(&dev->mutex); - list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) + mutex_lock(&dev->list_mutex); + list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { + mutex_unlock(&dev->list_mutex); w1_slave_detach(sl); + mutex_lock(&dev->list_mutex); + } w1_destroy_master_attributes(dev); + mutex_unlock(&dev->list_mutex); mutex_unlock(&dev->mutex); atomic_dec(&dev->refcnt); @@ -217,7 +219,13 @@ void __w1_remove_master_device(struct w1_master *dev) if (msleep_interruptible(1000)) flush_signals(current); + mutex_lock(&dev->list_mutex); + w1_process_callbacks(dev); + mutex_unlock(&dev->list_mutex); } + mutex_lock(&dev->list_mutex); + w1_process_callbacks(dev); + mutex_unlock(&dev->list_mutex); memset(&msg, 0, sizeof(msg)); msg.id.mst.id = dev->id; @@ -227,6 +235,10 @@ void __w1_remove_master_device(struct w1_master *dev) w1_free_dev(dev); } +/** + * w1_remove_master_device() - unregister a master device + * @bm: master bus device to remove + */ void w1_remove_master_device(struct w1_bus_master *bm) { struct w1_master *dev, *found = NULL; diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 3135b2c6399..282092421cc 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -31,6 +31,9 @@ static int w1_delay_parm = 1; module_param_named(delay_coef, w1_delay_parm, int, 0); +static int w1_disable_irqs = 0; +module_param_named(disable_irqs, w1_disable_irqs, int, 0); + static u8 w1_crc8_table[] = { 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, @@ -59,7 +62,9 @@ static void w1_write_bit(struct w1_master *dev, int bit); static u8 w1_read_bit(struct w1_master *dev); /** - * Generates a write-0 or write-1 cycle and samples the level. + * w1_touch_bit() - Generates a write-0 or write-1 cycle and samples the level. + * @dev: the master device + * @bit: 0 - write a 0, 1 - write a 0 read the level */ static u8 w1_touch_bit(struct w1_master *dev, int bit) { @@ -74,11 +79,18 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit) } /** - * Generates a write-0 or write-1 cycle. + * w1_write_bit() - Generates a write-0 or write-1 cycle. + * @dev: the master device + * @bit: bit to write + * * Only call if dev->bus_master->touch_bit is NULL */ static void w1_write_bit(struct w1_master *dev, int bit) { + unsigned long flags = 0; + + if(w1_disable_irqs) local_irq_save(flags); + if (bit) { dev->bus_master->write_bit(dev->bus_master->data, 0); w1_delay(6); @@ -90,14 +102,17 @@ static void w1_write_bit(struct w1_master *dev, int bit) dev->bus_master->write_bit(dev->bus_master->data, 1); w1_delay(10); } + + if(w1_disable_irqs) local_irq_restore(flags); } /** + * w1_pre_write() - pre-write operations + * @dev: the master device + * * Pre-write operation, currently only supporting strong pullups. * Program the hardware for a strong pullup, if one has been requested and * the hardware supports it. - * - * @param dev the master device */ static void w1_pre_write(struct w1_master *dev) { @@ -109,11 +124,12 @@ static void w1_pre_write(struct w1_master *dev) } /** + * w1_post_write() - post-write options + * @dev: the master device + * * Post-write operation, currently only supporting strong pullups. * If a strong pullup was requested, clear it if the hardware supports * them, or execute the delay otherwise, in either case clear the request. - * - * @param dev the master device */ static void w1_post_write(struct w1_master *dev) { @@ -127,10 +143,9 @@ static void w1_post_write(struct w1_master *dev) } /** - * Writes 8 bits. - * - * @param dev the master device - * @param byte the byte to write + * w1_write_8() - Writes 8 bits. + * @dev: the master device + * @byte: the byte to write */ void w1_write_8(struct w1_master *dev, u8 byte) { @@ -152,13 +167,15 @@ EXPORT_SYMBOL_GPL(w1_write_8); /** - * Generates a write-1 cycle and samples the level. + * w1_read_bit() - Generates a write-1 cycle and samples the level. + * @dev: the master device + * * Only call if dev->bus_master->touch_bit is NULL */ static u8 w1_read_bit(struct w1_master *dev) { int result; - unsigned long flags; + unsigned long flags = 0; /* sample timing is critical here */ local_irq_save(flags); @@ -176,16 +193,17 @@ static u8 w1_read_bit(struct w1_master *dev) } /** - * Does a triplet - used for searching ROM addresses. + * w1_triplet() - * Does a triplet - used for searching ROM addresses. + * @dev: the master device + * @bdir: the bit to write if both id_bit and comp_bit are 0 + * * Return bits: * bit 0 = id_bit * bit 1 = comp_bit * bit 2 = dir_taken * If both bits 0 & 1 are set, the search should be restarted. * - * @param dev the master device - * @param bdir the bit to write if both id_bit and comp_bit are 0 - * @return bit fields - see above + * Return: bit fields - see above */ u8 w1_triplet(struct w1_master *dev, int bdir) { @@ -217,10 +235,10 @@ u8 w1_triplet(struct w1_master *dev, int bdir) } /** - * Reads 8 bits. + * w1_read_8() - Reads 8 bits. + * @dev: the master device * - * @param dev the master device - * @return the byte read + * Return: the byte read */ u8 w1_read_8(struct w1_master *dev) { @@ -238,11 +256,10 @@ u8 w1_read_8(struct w1_master *dev) EXPORT_SYMBOL_GPL(w1_read_8); /** - * Writes a series of bytes. - * - * @param dev the master device - * @param buf pointer to the data to write - * @param len the number of bytes to write + * w1_write_block() - Writes a series of bytes. + * @dev: the master device + * @buf: pointer to the data to write + * @len: the number of bytes to write */ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) { @@ -260,11 +277,10 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_write_block); /** - * Touches a series of bytes. - * - * @param dev the master device - * @param buf pointer to the data to write - * @param len the number of bytes to write + * w1_touch_block() - Touches a series of bytes. + * @dev: the master device + * @buf: pointer to the data to write + * @len: the number of bytes to write */ void w1_touch_block(struct w1_master *dev, u8 *buf, int len) { @@ -285,12 +301,11 @@ void w1_touch_block(struct w1_master *dev, u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_touch_block); /** - * Reads a series of bytes. - * - * @param dev the master device - * @param buf pointer to the buffer to fill - * @param len the number of bytes to read - * @return the number of bytes read + * w1_read_block() - Reads a series of bytes. + * @dev: the master device + * @buf: pointer to the buffer to fill + * @len: the number of bytes to read + * Return: the number of bytes read */ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len) { @@ -310,14 +325,16 @@ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_read_block); /** - * Issues a reset bus sequence. - * - * @param dev The bus master pointer - * @return 0=Device present, 1=No device present or error + * w1_reset_bus() - Issues a reset bus sequence. + * @dev: the master device + * Return: 0=Device present, 1=No device present or error */ int w1_reset_bus(struct w1_master *dev) { int result; + unsigned long flags = 0; + + if(w1_disable_irqs) local_irq_save(flags); if (dev->bus_master->reset_bus) result = dev->bus_master->reset_bus(dev->bus_master->data) & 0x1; @@ -330,19 +347,21 @@ int w1_reset_bus(struct w1_master *dev) * cpu for such a short amount of time AND get it back in * the maximum amount of time. */ - w1_delay(480); + w1_delay(500); dev->bus_master->write_bit(dev->bus_master->data, 1); w1_delay(70); result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1; - /* minmum 70 (above) + 410 = 480 us + /* minmum 70 (above) + 430 = 500 us * There aren't any timing requirements between a reset and * the following transactions. Sleeping is safe here. */ - /* w1_delay(410); min required time */ + /* w1_delay(430); min required time */ msleep(1); } + if(w1_disable_irqs) local_irq_restore(flags); + return result; } EXPORT_SYMBOL_GPL(w1_reset_bus); @@ -369,12 +388,15 @@ void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_cal } /** + * w1_reset_select_slave() - reset and select a slave + * @sl: the slave to select + * * Resets the bus and then selects the slave by sending either a skip rom - * or a rom match. + * or a rom match. A skip rom is issued if there is only one device + * registered on the bus. * The w1 master lock must be held. * - * @param sl the slave to select - * @return 0=success, anything else=error + * Return: 0=success, anything else=error */ int w1_reset_select_slave(struct w1_slave *sl) { @@ -395,6 +417,9 @@ int w1_reset_select_slave(struct w1_slave *sl) EXPORT_SYMBOL_GPL(w1_reset_select_slave); /** + * w1_reset_resume_command() - resume instead of another match ROM + * @dev: the master device + * * When the workflow with a slave amongst many requires several * successive commands a reset between each, this function is similar * to doing a reset then a match ROM for the last matched ROM. The @@ -406,8 +431,6 @@ EXPORT_SYMBOL_GPL(w1_reset_select_slave); * doesn't work of course, but the resume command is the next best thing. * * The w1 master lock must be held. - * - * @param dev the master device */ int w1_reset_resume_command(struct w1_master *dev) { @@ -421,6 +444,10 @@ int w1_reset_resume_command(struct w1_master *dev) EXPORT_SYMBOL_GPL(w1_reset_resume_command); /** + * w1_next_pullup() - register for a strong pullup + * @dev: the master device + * @delay: time in milliseconds + * * Put out a strong pull-up of the specified duration after the next write * operation. Not all hardware supports strong pullups. Hardware that * doesn't support strong pullups will sleep for the given time after the @@ -428,8 +455,7 @@ EXPORT_SYMBOL_GPL(w1_reset_resume_command); * the next write, specifying zero will clear a previous request. * The w1 master lock must be held. * - * @param delay time in milliseconds - * @return 0=success, anything else=error + * Return: 0=success, anything else=error */ void w1_next_pullup(struct w1_master *dev, int delay) { diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 40788c925d1..351a2978ba7 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -29,128 +29,294 @@ #include "w1_netlink.h" #if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE))) -void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) -{ - char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)]; - struct cn_msg *m = (struct cn_msg *)buf; - struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1); - - memset(buf, 0, sizeof(buf)); - m->id.idx = CN_W1_IDX; - m->id.val = CN_W1_VAL; +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) - m->seq = dev->seq++; - m->len = sizeof(struct w1_netlink_msg); - - memcpy(w, msg, sizeof(struct w1_netlink_msg)); +/* Bundle together everything required to process a request in one memory + * allocation. + */ +struct w1_cb_block { + atomic_t refcnt; + u32 portid; /* Sending process port ID */ + /* maximum value for first_cn->len */ + u16 maxlen; + /* pointers to building up the reply message */ + struct cn_msg *first_cn; /* fixed once the structure is populated */ + struct cn_msg *cn; /* advances as cn_msg is appeneded */ + struct w1_netlink_msg *msg; /* advances as w1_netlink_msg is appened */ + struct w1_netlink_cmd *cmd; /* advances as cmds are appened */ + struct w1_netlink_msg *cur_msg; /* currently message being processed */ + /* copy of the original request follows */ + struct cn_msg request_cn; + /* followed by variable length: + * cn_msg, data (w1_netlink_msg and w1_netlink_cmd) + * one or more struct w1_cb_node + * reply first_cn, data (w1_netlink_msg and w1_netlink_cmd) + */ +}; +struct w1_cb_node { + struct w1_async_cmd async; + /* pointers within w1_cb_block and cn data */ + struct w1_cb_block *block; + struct w1_netlink_msg *msg; + struct w1_slave *sl; + struct w1_master *dev; +}; - cn_netlink_send(m, 0, GFP_KERNEL); +/** + * w1_reply_len() - calculate current reply length, compare to maxlen + * @block: block to calculate + * + * Calculates the current message length including possible multiple + * cn_msg and data, excludes the first sizeof(struct cn_msg). Direclty + * compariable to maxlen and usable to send the message. + */ +static u16 w1_reply_len(struct w1_cb_block *block) +{ + if (!block->cn) + return 0; + return (u8 *)block->cn - (u8 *)block->first_cn + block->cn->len; } -static void w1_send_slave(struct w1_master *dev, u64 rn) +static void w1_unref_block(struct w1_cb_block *block) { - struct cn_msg *msg = dev->priv; - struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); - struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); - int avail; - - /* update kernel slave list */ - w1_slave_found(dev, rn); + if (atomic_sub_return(1, &block->refcnt) == 0) { + u16 len = w1_reply_len(block); + if (len) { + cn_netlink_send_mult(block->first_cn, len, + block->portid, 0, GFP_KERNEL); + } + kfree(block); + } +} - avail = dev->priv_size - cmd->len; +/** + * w1_reply_make_space() - send message if needed to make space + * @block: block to make space on + * @space: how many bytes requested + * + * Verify there is enough room left for the caller to add "space" bytes to the + * message, if there isn't send the message and reset. + */ +static void w1_reply_make_space(struct w1_cb_block *block, u16 space) +{ + u16 len = w1_reply_len(block); + if (len + space >= block->maxlen) { + cn_netlink_send_mult(block->first_cn, len, block->portid, 0, GFP_KERNEL); + block->first_cn->len = 0; + block->cn = NULL; + block->msg = NULL; + block->cmd = NULL; + } +} - if (avail > 8) { - u64 *data = (void *)(cmd + 1) + cmd->len; +/* Early send when replies aren't bundled. */ +static void w1_netlink_check_send(struct w1_cb_block *block) +{ + if (!(block->request_cn.flags & W1_CN_BUNDLE) && block->cn) + w1_reply_make_space(block, block->maxlen); +} - *data = rn; - cmd->len += 8; - hdr->len += 8; - msg->len += 8; - return; +/** + * w1_netlink_setup_msg() - prepare to write block->msg + * @block: block to operate on + * @ack: determines if cn can be reused + * + * block->cn will be setup with the correct ack, advancing if needed + * block->cn->len does not include space for block->msg + * block->msg advances but remains uninitialized + */ +static void w1_netlink_setup_msg(struct w1_cb_block *block, u32 ack) +{ + if (block->cn && block->cn->ack == ack) { + block->msg = (struct w1_netlink_msg *)(block->cn->data + block->cn->len); + } else { + /* advance or set to data */ + if (block->cn) + block->cn = (struct cn_msg *)(block->cn->data + + block->cn->len); + else + block->cn = block->first_cn; + + memcpy(block->cn, &block->request_cn, sizeof(*block->cn)); + block->cn->len = 0; + block->cn->ack = ack; + block->msg = (struct w1_netlink_msg *)block->cn->data; } +} - msg->ack++; - cn_netlink_send(msg, 0, GFP_KERNEL); +/* Append cmd to msg, include cmd->data as well. This is because + * any following data goes with the command and in the case of a read is + * the results. + */ +static void w1_netlink_queue_cmd(struct w1_cb_block *block, + struct w1_netlink_cmd *cmd) +{ + u32 space; + w1_reply_make_space(block, sizeof(struct cn_msg) + + sizeof(struct w1_netlink_msg) + sizeof(*cmd) + cmd->len); + + /* There's a status message sent after each command, so no point + * in trying to bundle this cmd after an existing one, because + * there won't be one. Allocate and copy over a new cn_msg. + */ + w1_netlink_setup_msg(block, block->request_cn.seq + 1); + memcpy(block->msg, block->cur_msg, sizeof(*block->msg)); + block->cn->len += sizeof(*block->msg); + block->msg->len = 0; + block->cmd = (struct w1_netlink_cmd *)(block->msg->data); + + space = sizeof(*cmd) + cmd->len; + if (block->cmd != cmd) + memcpy(block->cmd, cmd, space); + block->cn->len += space; + block->msg->len += space; +} - msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); - hdr->len = sizeof(struct w1_netlink_cmd); - cmd->len = 0; +/* Append req_msg and req_cmd, no other commands and no data from req_cmd are + * copied. + */ +static void w1_netlink_queue_status(struct w1_cb_block *block, + struct w1_netlink_msg *req_msg, struct w1_netlink_cmd *req_cmd, + int error) +{ + u16 space = sizeof(struct cn_msg) + sizeof(*req_msg) + sizeof(*req_cmd); + w1_reply_make_space(block, space); + w1_netlink_setup_msg(block, block->request_cn.ack); + + memcpy(block->msg, req_msg, sizeof(*req_msg)); + block->cn->len += sizeof(*req_msg); + block->msg->len = 0; + block->msg->status = (u8)-error; + if (req_cmd) { + struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)block->msg->data; + memcpy(cmd, req_cmd, sizeof(*cmd)); + block->cn->len += sizeof(*cmd); + block->msg->len += sizeof(*cmd); + cmd->len = 0; + } + w1_netlink_check_send(block); } -static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg, - unsigned int avail) +/** + * w1_netlink_send_error() - sends the error message now + * @cn: original cn_msg + * @msg: original w1_netlink_msg + * @portid: where to send it + * @error: error status + * + * Use when a block isn't available to queue the message to and cn, msg + * might not be contiguous. + */ +static void w1_netlink_send_error(struct cn_msg *cn, struct w1_netlink_msg *msg, + int portid, int error) { - struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); - struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); - int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH; + struct { + struct cn_msg cn; + struct w1_netlink_msg msg; + } packet; + memcpy(&packet.cn, cn, sizeof(packet.cn)); + memcpy(&packet.msg, msg, sizeof(packet.msg)); + packet.cn.len = sizeof(packet.msg); + packet.msg.len = 0; + packet.msg.status = (u8)-error; + cn_netlink_send(&packet.cn, portid, 0, GFP_KERNEL); +} - dev->priv = msg; - dev->priv_size = avail; +/** + * w1_netlink_send() - sends w1 netlink notifications + * @dev: w1_master the even is associated with or for + * @msg: w1_netlink_msg message to be sent + * + * This are notifications generated from the kernel. + */ +void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) +{ + struct { + struct cn_msg cn; + struct w1_netlink_msg msg; + } packet; + memset(&packet, 0, sizeof(packet)); - w1_search_process_cb(dev, search_type, w1_send_slave); + packet.cn.id.idx = CN_W1_IDX; + packet.cn.id.val = CN_W1_VAL; - msg->ack = 0; - cn_netlink_send(msg, 0, GFP_KERNEL); + packet.cn.seq = dev->seq++; + packet.cn.len = sizeof(*msg); - dev->priv = NULL; - dev->priv_size = 0; + memcpy(&packet.msg, msg, sizeof(*msg)); + packet.msg.len = 0; - return 0; + cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL); } -static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, - struct w1_netlink_cmd *cmd) +static void w1_send_slave(struct w1_master *dev, u64 rn) { - void *data; - struct w1_netlink_msg *h; - struct w1_netlink_cmd *c; - struct cn_msg *cm; - int err; - - data = kzalloc(sizeof(struct cn_msg) + - sizeof(struct w1_netlink_msg) + - sizeof(struct w1_netlink_cmd) + - cmd->len, GFP_KERNEL); - if (!data) - return -ENOMEM; + struct w1_cb_block *block = dev->priv; + struct w1_netlink_cmd *cache_cmd = block->cmd; + u64 *data; - cm = (struct cn_msg *)(data); - h = (struct w1_netlink_msg *)(cm + 1); - c = (struct w1_netlink_cmd *)(h + 1); + w1_reply_make_space(block, sizeof(*data)); - memcpy(cm, msg, sizeof(struct cn_msg)); - memcpy(h, hdr, sizeof(struct w1_netlink_msg)); - memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); + /* Add cmd back if the packet was sent */ + if (!block->cmd) { + cache_cmd->len = 0; + w1_netlink_queue_cmd(block, cache_cmd); + } - cm->ack = msg->seq+1; - cm->len = sizeof(struct w1_netlink_msg) + - sizeof(struct w1_netlink_cmd) + cmd->len; + data = (u64 *)(block->cmd->data + block->cmd->len); - h->len = sizeof(struct w1_netlink_cmd) + cmd->len; + *data = rn; + block->cn->len += sizeof(*data); + block->msg->len += sizeof(*data); + block->cmd->len += sizeof(*data); +} - memcpy(c->data, cmd->data, c->len); +static void w1_found_send_slave(struct w1_master *dev, u64 rn) +{ + /* update kernel slave list */ + w1_slave_found(dev, rn); - err = cn_netlink_send(cm, 0, GFP_KERNEL); + w1_send_slave(dev, rn); +} - kfree(data); +/* Get the current slave list, or search (with or without alarm) */ +static int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd) +{ + struct w1_slave *sl; - return err; + req_cmd->len = 0; + w1_netlink_queue_cmd(dev->priv, req_cmd); + + if (req_cmd->cmd == W1_CMD_LIST_SLAVES) { + u64 rn; + mutex_lock(&dev->list_mutex); + list_for_each_entry(sl, &dev->slist, w1_slave_entry) { + memcpy(&rn, &sl->reg_num, sizeof(rn)); + w1_send_slave(dev, rn); + } + mutex_unlock(&dev->list_mutex); + } else { + w1_search_process_cb(dev, req_cmd->cmd == W1_CMD_ALARM_SEARCH ? + W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave); + } + + return 0; } -static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, - struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +static int w1_process_command_io(struct w1_master *dev, + struct w1_netlink_cmd *cmd) { int err = 0; switch (cmd->cmd) { case W1_CMD_TOUCH: w1_touch_block(dev, cmd->data, cmd->len); - w1_send_read_reply(msg, hdr, cmd); + w1_netlink_queue_cmd(dev->priv, cmd); break; case W1_CMD_READ: w1_read_block(dev, cmd->data, cmd->len); - w1_send_read_reply(msg, hdr, cmd); + w1_netlink_queue_cmd(dev->priv, cmd); break; case W1_CMD_WRITE: w1_write_block(dev, cmd->data, cmd->len); @@ -163,79 +329,97 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, return err; } -static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg, - struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd) +static int w1_process_command_addremove(struct w1_master *dev, + struct w1_netlink_cmd *cmd) { - int err = -EINVAL; - struct cn_msg *msg; - struct w1_netlink_msg *hdr; - struct w1_netlink_cmd *cmd; + struct w1_slave *sl; + int err = 0; + struct w1_reg_num *id; - msg = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; + if (cmd->len != sizeof(*id)) + return -EINVAL; - msg->id = req_msg->id; - msg->seq = req_msg->seq; - msg->ack = 0; - msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); + id = (struct w1_reg_num *)cmd->data; - hdr = (struct w1_netlink_msg *)(msg + 1); - cmd = (struct w1_netlink_cmd *)(hdr + 1); + sl = w1_slave_search_device(dev, id); + switch (cmd->cmd) { + case W1_CMD_SLAVE_ADD: + if (sl) + err = -EINVAL; + else + err = w1_attach_slave_device(dev, id); + break; + case W1_CMD_SLAVE_REMOVE: + if (sl) + w1_slave_detach(sl); + else + err = -EINVAL; + break; + default: + err = -EINVAL; + break; + } - hdr->type = W1_MASTER_CMD; - hdr->id = req_hdr->id; - hdr->len = sizeof(struct w1_netlink_cmd); + return err; +} - cmd->cmd = req_cmd->cmd; - cmd->len = 0; +static int w1_process_command_master(struct w1_master *dev, + struct w1_netlink_cmd *req_cmd) +{ + int err = -EINVAL; - switch (cmd->cmd) { + /* drop bus_mutex for search (does it's own locking), and add/remove + * which doesn't use the bus + */ + switch (req_cmd->cmd) { case W1_CMD_SEARCH: case W1_CMD_ALARM_SEARCH: - err = w1_process_search_command(dev, msg, - PAGE_SIZE - msg->len - sizeof(struct cn_msg)); + case W1_CMD_LIST_SLAVES: + mutex_unlock(&dev->bus_mutex); + err = w1_get_slaves(dev, req_cmd); + mutex_lock(&dev->bus_mutex); break; case W1_CMD_READ: case W1_CMD_WRITE: case W1_CMD_TOUCH: - err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd); + err = w1_process_command_io(dev, req_cmd); break; case W1_CMD_RESET: err = w1_reset_bus(dev); break; + case W1_CMD_SLAVE_ADD: + case W1_CMD_SLAVE_REMOVE: + mutex_unlock(&dev->bus_mutex); + mutex_lock(&dev->mutex); + err = w1_process_command_addremove(dev, req_cmd); + mutex_unlock(&dev->mutex); + mutex_lock(&dev->bus_mutex); + break; default: err = -EINVAL; break; } - kfree(msg); return err; } -static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, - struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +static int w1_process_command_slave(struct w1_slave *sl, + struct w1_netlink_cmd *cmd) { dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc, cmd->cmd, cmd->len); - return w1_process_command_io(sl->master, msg, hdr, cmd); + return w1_process_command_io(sl->master, cmd); } -static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd) +static int w1_process_command_root(struct cn_msg *req_cn, u32 portid) { - struct w1_master *m; + struct w1_master *dev; struct cn_msg *cn; - struct w1_netlink_msg *w; + struct w1_netlink_msg *msg; u32 *id; - if (mcmd->type != W1_LIST_MASTERS) { - printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n", - __func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len); - return -EPROTO; - } - cn = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!cn) return -ENOMEM; @@ -243,108 +427,264 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc cn->id.idx = CN_W1_IDX; cn->id.val = CN_W1_VAL; - cn->seq = msg->seq; - cn->ack = 1; + cn->seq = req_cn->seq; + cn->ack = req_cn->seq + 1; cn->len = sizeof(struct w1_netlink_msg); - w = (struct w1_netlink_msg *)(cn + 1); + msg = (struct w1_netlink_msg *)cn->data; - w->type = W1_LIST_MASTERS; - w->status = 0; - w->len = 0; - id = (u32 *)(w + 1); + msg->type = W1_LIST_MASTERS; + msg->status = 0; + msg->len = 0; + id = (u32 *)msg->data; mutex_lock(&w1_mlock); - list_for_each_entry(m, &w1_masters, w1_master_entry) { + list_for_each_entry(dev, &w1_masters, w1_master_entry) { if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) { - cn_netlink_send(cn, 0, GFP_KERNEL); - cn->ack++; + cn_netlink_send(cn, portid, 0, GFP_KERNEL); cn->len = sizeof(struct w1_netlink_msg); - w->len = 0; - id = (u32 *)(w + 1); + msg->len = 0; + id = (u32 *)msg->data; } - *id = m->id; - w->len += sizeof(*id); + *id = dev->id; + msg->len += sizeof(*id); cn->len += sizeof(*id); id++; } - cn->ack = 0; - cn_netlink_send(cn, 0, GFP_KERNEL); + cn_netlink_send(cn, portid, 0, GFP_KERNEL); mutex_unlock(&w1_mlock); kfree(cn); return 0; } -static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg, - struct w1_netlink_cmd *rcmd, int error) +static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd) { - struct cn_msg *cmsg; - struct w1_netlink_msg *msg; - struct w1_netlink_cmd *cmd; + struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node, + async); + u16 mlen = node->msg->len; + u16 len; + int err = 0; + struct w1_slave *sl = node->sl; + struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)node->msg->data; - cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL); - if (!cmsg) - return -ENOMEM; + mutex_lock(&dev->bus_mutex); + dev->priv = node->block; + if (sl && w1_reset_select_slave(sl)) + err = -ENODEV; + node->block->cur_msg = node->msg; - msg = (struct w1_netlink_msg *)(cmsg + 1); - cmd = (struct w1_netlink_cmd *)(msg + 1); + while (mlen && !err) { + if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { + err = -E2BIG; + break; + } - memcpy(cmsg, rcmsg, sizeof(*cmsg)); - cmsg->len = sizeof(*msg); + if (sl) + err = w1_process_command_slave(sl, cmd); + else + err = w1_process_command_master(dev, cmd); + w1_netlink_check_send(node->block); - memcpy(msg, rmsg, sizeof(*msg)); - msg->len = 0; - msg->status = (short)-error; + w1_netlink_queue_status(node->block, node->msg, cmd, err); + err = 0; - if (rcmd) { - memcpy(cmd, rcmd, sizeof(*cmd)); - cmd->len = 0; - msg->len += sizeof(*cmd); - cmsg->len += sizeof(*cmd); + len = sizeof(*cmd) + cmd->len; + cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len); + mlen -= len; } - error = cn_netlink_send(cmsg, 0, GFP_KERNEL); - kfree(cmsg); + if (!cmd || err) + w1_netlink_queue_status(node->block, node->msg, cmd, err); + + /* ref taken in w1_search_slave or w1_search_master_id when building + * the block + */ + if (sl) + w1_unref_slave(sl); + else + atomic_dec(&dev->refcnt); + dev->priv = NULL; + mutex_unlock(&dev->bus_mutex); + + mutex_lock(&dev->list_mutex); + list_del(&async_cmd->async_entry); + mutex_unlock(&dev->list_mutex); - return error; + w1_unref_block(node->block); } -static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +static void w1_list_count_cmds(struct w1_netlink_msg *msg, int *cmd_count, + u16 *slave_len) { - struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); - struct w1_netlink_cmd *cmd; + struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)msg->data; + u16 mlen = msg->len; + u16 len; + int slave_list = 0; + while (mlen) { + if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) + break; + + switch (cmd->cmd) { + case W1_CMD_SEARCH: + case W1_CMD_ALARM_SEARCH: + case W1_CMD_LIST_SLAVES: + ++slave_list; + } + ++*cmd_count; + len = sizeof(*cmd) + cmd->len; + cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len); + mlen -= len; + } + + if (slave_list) { + struct w1_master *dev = w1_search_master_id(msg->id.mst.id); + if (dev) { + /* Bytes, and likely an overstimate, and if it isn't + * the results can still be split between packets. + */ + *slave_len += sizeof(struct w1_reg_num) * slave_list * + (dev->slave_count + dev->max_slave_count); + /* search incremented it */ + atomic_dec(&dev->refcnt); + } + } +} + +static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp) +{ + struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1); struct w1_slave *sl; struct w1_master *dev; + u16 msg_len; + u16 slave_len = 0; int err = 0; + struct w1_cb_block *block = NULL; + struct w1_cb_node *node = NULL; + int node_count = 0; + int cmd_count = 0; + + /* If any unknown flag is set let the application know, that way + * applications can detect the absence of features in kernels that + * don't know about them. http://lwn.net/Articles/587527/ + */ + if (cn->flags & ~(W1_CN_BUNDLE)) { + w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL); + return; + } + + /* Count the number of master or slave commands there are to allocate + * space for one cb_node each. + */ + msg_len = cn->len; + while (msg_len && !err) { + if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) { + err = -E2BIG; + break; + } - while (msg->len && !err) { - struct w1_reg_num id; - u16 mlen = m->len; - u8 *cmd_data = m->data; + /* count messages for nodes and allocate any additional space + * required for slave lists + */ + if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) { + ++node_count; + w1_list_count_cmds(msg, &cmd_count, &slave_len); + } + + msg_len -= sizeof(struct w1_netlink_msg) + msg->len; + msg = (struct w1_netlink_msg *)(((u8 *)msg) + + sizeof(struct w1_netlink_msg) + msg->len); + } + msg = (struct w1_netlink_msg *)(cn + 1); + if (node_count) { + int size; + u16 reply_size = sizeof(*cn) + cn->len + slave_len; + if (cn->flags & W1_CN_BUNDLE) { + /* bundling duplicats some of the messages */ + reply_size += 2 * cmd_count * (sizeof(struct cn_msg) + + sizeof(struct w1_netlink_msg) + + sizeof(struct w1_netlink_cmd)); + } + reply_size = MIN(CONNECTOR_MAX_MSG_SIZE, reply_size); + + /* allocate space for the block, a copy of the original message, + * one node per cmd to point into the original message, + * space for replies which is the original message size plus + * space for any list slave data and status messages + * cn->len doesn't include itself which is part of the block + * */ + size = /* block + original message */ + sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len + + /* space for nodes */ + node_count * sizeof(struct w1_cb_node) + + /* replies */ + sizeof(struct cn_msg) + reply_size; + block = kzalloc(size, GFP_KERNEL); + if (!block) { + /* if the system is already out of memory, + * (A) will this work, and (B) would it be better + * to not try? + */ + w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM); + return; + } + atomic_set(&block->refcnt, 1); + block->portid = nsp->portid; + memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len); + node = (struct w1_cb_node *)(block->request_cn.data + cn->len); + + /* Sneeky, when not bundling, reply_size is the allocated space + * required for the reply, cn_msg isn't part of maxlen so + * it should be reply_size - sizeof(struct cn_msg), however + * when checking if there is enough space, w1_reply_make_space + * is called with the full message size including cn_msg, + * because it isn't known at that time if an additional cn_msg + * will need to be allocated. So an extra cn_msg is added + * above in "size". + */ + block->maxlen = reply_size; + block->first_cn = (struct cn_msg *)(node + node_count); + memset(block->first_cn, 0, sizeof(*block->first_cn)); + } + + msg_len = cn->len; + while (msg_len && !err) { dev = NULL; sl = NULL; - cmd = NULL; - memcpy(&id, m->id.id, sizeof(id)); -#if 0 - printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", - __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); -#endif - if (m->len + sizeof(struct w1_netlink_msg) > msg->len) { + if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) { err = -E2BIG; break; } - if (m->type == W1_MASTER_CMD) { - dev = w1_search_master_id(m->id.mst.id); - } else if (m->type == W1_SLAVE_CMD) { - sl = w1_search_slave(&id); + /* execute on this thread, no need to process later */ + if (msg->type == W1_LIST_MASTERS) { + err = w1_process_command_root(cn, nsp->portid); + goto out_cont; + } + + /* All following message types require additional data, + * check here before references are taken. + */ + if (!msg->len) { + err = -EPROTO; + goto out_cont; + } + + /* both search calls take references */ + if (msg->type == W1_MASTER_CMD) { + dev = w1_search_master_id(msg->id.mst.id); + } else if (msg->type == W1_SLAVE_CMD) { + sl = w1_search_slave((struct w1_reg_num *)msg->id.id); if (sl) dev = sl->master; } else { - err = w1_process_command_root(msg, m); + printk(KERN_NOTICE + "%s: cn: %x.%x, wrong type: %u, len: %u.\n", + __func__, cn->id.idx, cn->id.val, + msg->type, msg->len); + err = -EPROTO; goto out_cont; } @@ -354,45 +694,31 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) } err = 0; - if (!mlen) - goto out_cont; - - mutex_lock(&dev->mutex); - if (sl && w1_reset_select_slave(sl)) { - err = -ENODEV; - goto out_up; - } + atomic_inc(&block->refcnt); + node->async.cb = w1_process_cb; + node->block = block; + node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn + + (size_t)((u8 *)msg - (u8 *)cn)); + node->sl = sl; + node->dev = dev; - while (mlen) { - cmd = (struct w1_netlink_cmd *)cmd_data; + mutex_lock(&dev->list_mutex); + list_add_tail(&node->async.async_entry, &dev->async_list); + wake_up_process(dev->thread); + mutex_unlock(&dev->list_mutex); + ++node; - if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { - err = -E2BIG; - break; - } - - if (sl) - err = w1_process_command_slave(sl, msg, m, cmd); - else - err = w1_process_command_master(dev, msg, m, cmd); - - w1_netlink_send_error(msg, m, cmd, err); - err = 0; - - cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); - mlen -= cmd->len + sizeof(struct w1_netlink_cmd); - } -out_up: - atomic_dec(&dev->refcnt); - if (sl) - atomic_dec(&sl->refcnt); - mutex_unlock(&dev->mutex); out_cont: - if (!cmd || err) - w1_netlink_send_error(msg, m, cmd, err); - msg->len -= sizeof(struct w1_netlink_msg) + m->len; - m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); + /* Can't queue because that modifies block and another + * thread could be processing the messages by now and + * there isn't a lock, send directly. + */ + if (err) + w1_netlink_send_error(cn, msg, nsp->portid, err); + msg_len -= sizeof(struct w1_netlink_msg) + msg->len; + msg = (struct w1_netlink_msg *)(((u8 *)msg) + + sizeof(struct w1_netlink_msg) + msg->len); /* * Let's allow requests for nonexisting devices. @@ -400,6 +726,8 @@ out_cont: if (err == -ENODEV) err = 0; } + if (block) + w1_unref_block(block); } int w1_init_netlink(void) @@ -416,7 +744,7 @@ void w1_fini_netlink(void) cn_del_callback(&w1_id); } #else -void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) +void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *cn) { } diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index b0922dc2965..c99a9ce05e6 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -27,6 +27,29 @@ #include "w1.h" +/** + * enum w1_cn_msg_flags - bitfield flags for struct cn_msg.flags + * + * @W1_CN_BUNDLE: Request bundling replies into fewer messagse. Be prepared + * to handle multiple struct cn_msg, struct w1_netlink_msg, and + * struct w1_netlink_cmd in one packet. + */ +enum w1_cn_msg_flags { + W1_CN_BUNDLE = 1, +}; + +/** + * enum w1_netlink_message_types - message type + * + * @W1_SLAVE_ADD: notification that a slave device was added + * @W1_SLAVE_REMOVE: notification that a slave device was removed + * @W1_MASTER_ADD: notification that a new bus master was added + * @W1_MASTER_REMOVE: notification that a bus masterwas removed + * @W1_MASTER_CMD: initiate operations on a specific master + * @W1_SLAVE_CMD: sends reset, selects the slave, then does a read/write/touch + * operation + * @W1_LIST_MASTERS: used to determine the bus master identifiers + */ enum w1_netlink_message_types { W1_SLAVE_ADD = 0, W1_SLAVE_REMOVE, @@ -37,6 +60,19 @@ enum w1_netlink_message_types { W1_LIST_MASTERS, }; +/** + * struct w1_netlink_msg - holds w1 message type, id, and result + * + * @type: one of enum w1_netlink_message_types + * @status: kernel feedback for success 0 or errno failure value + * @len: length of data following w1_netlink_msg + * @id: union holding master bus id (msg.id) and slave device id (id[8]). + * @data: start address of any following data + * + * The base message structure for w1 messages over netlink. + * The netlink connector data sequence is, struct nlmsghdr, struct cn_msg, + * then one or more struct w1_netlink_msg (each with optional data). + */ struct w1_netlink_msg { __u8 type; @@ -52,6 +88,23 @@ struct w1_netlink_msg __u8 data[0]; }; +/** + * enum w1_commands - commands available for master or slave operations + * + * @W1_CMD_READ: read len bytes + * @W1_CMD_WRITE: write len bytes + * @W1_CMD_SEARCH: initiate a standard search, returns only the slave + * devices found during that search + * @W1_CMD_ALARM_SEARCH: search for devices that are currently alarming + * @W1_CMD_TOUCH: Touches a series of bytes. + * @W1_CMD_RESET: sends a bus reset on the given master + * @W1_CMD_SLAVE_ADD: adds a slave to the given master, + * 8 byte slave id at data[0] + * @W1_CMD_SLAVE_REMOVE: removes a slave to the given master, + * 8 byte slave id at data[0] + * @W1_CMD_LIST_SLAVES: list of slaves registered on this master + * @W1_CMD_MAX: number of available commands + */ enum w1_commands { W1_CMD_READ = 0, W1_CMD_WRITE, @@ -59,9 +112,23 @@ enum w1_commands { W1_CMD_ALARM_SEARCH, W1_CMD_TOUCH, W1_CMD_RESET, - W1_CMD_MAX, + W1_CMD_SLAVE_ADD, + W1_CMD_SLAVE_REMOVE, + W1_CMD_LIST_SLAVES, + W1_CMD_MAX }; +/** + * struct w1_netlink_cmd - holds the command and data + * + * @cmd: one of enum w1_commands + * @res: reserved + * @len: length of data following w1_netlink_cmd + * @data: start address of any following data + * + * One or more struct w1_netlink_cmd is placed starting at w1_netlink_msg.data + * each with optional data. + */ struct w1_netlink_cmd { __u8 cmd; |
