且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

I.MX6 ar1020 SPI device driver hacking

更新时间:2022-08-12 20:32:23

/************************************************************************************
 *                    I.MX6 ar1020 SPI device driver hacking
 * 声明:
 *     1. 本文主要是解读I.MX6中ar1020 SPI设备注册,以及驱动调用流程;
 *     2. 本文主要使用了vim+ctags进行代码跟踪,所以几乎都是函数原型之间的调用;
 *
 *                                             2015-9-5 晴 深圳 南山平山村 曾剑锋
 ***********************************************************************************/



/* * initialize __mach_desc_MX6Q_SABRESD data structure. */ MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board") /* Maintainer: Freescale Semiconductor, Inc. */ .boot_params = MX6_PHYS_OFFSET + 0x100, .fixup = fixup_mxc_board, .map_io = mx6_map_io, .init_irq = mx6_init_irq, .init_machine = mx6_sabresd_board_init, ---------------+ .timer = &mx6_sabresd_timer, | .reserve = mx6q_sabresd_reserve, | MACHINE_END | | /*! | * Board specific initialization. | */ | static void __init mx6_sabresd_board_init(void) <----------+ { ...... imx6q_add_ecspi(0, &mx6q_sabresd_spi_data); ---------------+---+ imx6q_add_ecspi(1, &mx6q_sabresd_spi2_data); ---------------*-+ | spi_device_init(); ---------------*-*-*-+ ...... | | | | gpio_request(SABRESD_AR1020_INT, "ar1020-interrupt"); | | | | gpio_direction_input(SABRESD_AR1020_INT); | | | | ...... | | | | } | | | | | | | | static const struct spi_imx_master mx6q_sabresd_spi_data __initconst = { <--+ | | | .chipselect = mx6q_sabresd_spi_cs, -----+ | | | .num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi_cs), | | | | }; | | | | | | | | static int mx6q_sabresd_spi_cs[] = { <----------+ | | | SABRESD_ECSPI1_CS1, | | | }; | | | | | | static const struct spi_imx_master mx6q_sabresd_spi2_data __initconst = { <----+ | | .chipselect = mx6q_sabresd_spi2_cs, ------+ | | .num_chipselect = ARRAY_SIZE(mx6q_sabresd_spi2_cs), | | | }; | | | | | | static int mx6q_sabresd_spi2_cs[] = { <----------+ | | SABRESD_ECSPI2_CS0, | | }; | | | | extern const struct imx_spi_imx_data imx6q_ecspi_data[] __initconst; ------+ | | #define imx6q_add_ecspi(id, pdata) \ <----------------*----+ | imx_add_spi_imx(&imx6q_ecspi_data[id], pdata) <-----+ ---*-----+ | | | const struct imx_spi_imx_data imx6q_ecspi_data[] __initconst = { <-----+ | | #define imx6q_ecspi_data_entry(_id, _hwid) \ | | imx_spi_imx_data_entry(MX6Q, ECSPI, "imx6q-ecspi", _id, _hwid, SZ_4K) ----+ | | imx6q_ecspi_data_entry(0, 1), | | | imx6q_ecspi_data_entry(1, 2), | | | imx6q_ecspi_data_entry(2, 3), | | | imx6q_ecspi_data_entry(3, 4), | | | imx6q_ecspi_data_entry(4, 5), | | | }; | | | #endif /* ifdef CONFIG_SOC_IMX6Q */ | | | | | | #define imx_spi_imx_data_entry_single(soc, type, _devid, _id, hwid, _size) \ | <-*--+ | { \ | | | | .devid = _devid, \ | | | | .id = _id, \ | | | | .iobase = soc ## _ ## type ## hwid ## _BASE_ADDR, \ | | | | .iosize = _size, \ | | | | .irq = soc ## _INT_ ## type ## hwid, \ | | | | } | | | | | | | | #define imx_spi_imx_data_entry(soc, type, devid, id, hwid, size) \ <-----+ | | | [id] = imx_spi_imx_data_entry_single(soc, type, devid, id, hwid, size) --------*--+ | | | struct platform_device *__init imx_add_spi_imx( <-------------------------*-----+ const struct imx_spi_imx_data *data, | const struct spi_imx_master *pdata) | { | struct resource res[] = { | { | .start = data->iobase, | .end = data->iobase + data->iosize - 1, | .flags = IORESOURCE_MEM, | }, { | .start = data->irq, | .end = data->irq, | .flags = IORESOURCE_IRQ, | }, | }; | return imx_add_platform_device(data->devid, data->id, ------+ | res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); | | } | | | | static inline struct platform_device *imx_add_platform_device( <----+ | const char *name, int id, | const struct resource *res, unsigned int num_resources, | const void *data, size_t size_data) | { | return imx_add_platform_device_dmamask( -----+ | name, id, res, num_resources, data, size_data, 0); | | } | | | | struct platform_device *__init imx_add_platform_device_dmamask( <----+ | const char *name, int id, | const struct resource *res, unsigned int num_resources, | const void *data, size_t size_data, u64 dmamask) | { | int ret = -ENOMEM; | struct platform_device *pdev; | | pdev = platform_device_alloc(name, id); | if (!pdev) | goto err; | | if (dmamask) { | /* | * This memory isn't freed when the device is put, | * I don't have a nice idea for that though. Conceptually | * dma_mask in struct device should not be a pointer. | * See http://thread.gmane.org/gmane.linux.kernel.pci/9081 | */ | pdev->dev.dma_mask = | kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL); | if (!pdev->dev.dma_mask) | /* ret is still -ENOMEM; */ | goto err; | | *pdev->dev.dma_mask = dmamask; | pdev->dev.coherent_dma_mask = dmamask; | } | | if (res) { | ret = platform_device_add_resources(pdev, res, num_resources); | if (ret) | goto err; | } | | if (data) { | ret = platform_device_add_data(pdev, data, size_data); | if (ret) | goto err; | } | | ret = platform_device_add(pdev); ------------------------+ | if (ret) { | | err: | | if (dmamask) | | kfree(pdev->dev.dma_mask); | | platform_device_put(pdev); | | return ERR_PTR(ret); | | } | | | | return pdev; | | } | | | | /** | | * platform_device_add - add a platform device to device hierarchy | | * @pdev: platform device we're adding | | * | | * This is part 2 of platform_device_register(), though may be called | | * separately _iff_ pdev was allocated by platform_device_alloc(). | | */ | | int platform_device_add(struct platform_device *pdev) <-----------+ | { | int i, ret = 0; | | if (!pdev) | return -EINVAL; | | if (!pdev->dev.parent) | pdev->dev.parent = &platform_bus; | | pdev->dev.bus = &platform_bus_type; | | if (pdev->id != -1) | dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); | else | dev_set_name(&pdev->dev, "%s", pdev->name); | | for (i = 0; i < pdev->num_resources; i++) { | struct resource *p, *r = &pdev->resource[i]; | | if (r->name == NULL) | r->name = dev_name(&pdev->dev); | | p = r->parent; | if (!p) { | if (resource_type(r) == IORESOURCE_MEM) | p = &iomem_resource; | else if (resource_type(r) == IORESOURCE_IO) | p = &ioport_resource; | } | | if (p && insert_resource(p, r)) { | printk(KERN_ERR | "%s: failed to claim resource %d\n", | dev_name(&pdev->dev), i); | ret = -EBUSY; | goto failed; | } | } | | pr_debug("Registering platform device '%s'. Parent at %s\n", | dev_name(&pdev->dev), dev_name(pdev->dev.parent)); | | ret = device_add(&pdev->dev); | if (ret == 0) | return ret; | | failed: | while (--i >= 0) { | struct resource *r = &pdev->resource[i]; | unsigned long type = resource_type(r); | | if (type == IORESOURCE_MEM || type == IORESOURCE_IO) | release_resource(r); | } | | return ret; | } | EXPORT_SYMBOL_GPL(platform_device_add); | | static void spi_device_init(void) <--------------------------+ { spi_register_board_info(imx6_sabresd_spi_nor_device, -----------------+ ARRAY_SIZE(imx6_sabresd_spi_nor_device)); | } | | static struct spi_board_info imx6_sabresd_spi_nor_device[] __initdata = { <-----+ { ^-----------------------------------------------------+ | .modalias = "ar1020-spi", | | .max_speed_hz = 50000, /* max spi clock (SCK) speed in HZ */ | | .bus_num = 1, | | .chip_select = 0, | | .mode = SPI_MODE_1, | | .irq = gpio_to_irq(SABRESD_AR1020_INT), ----------------+ | | | | | //.platform_data = &imx6_sabresd__spi_flash_data, | | | }, | | | +---------------------------------------------------------|-+ | }; | | | V | | struct spi_board_info { | | /* the device name and module name are coupled, like platform_bus; | | * "modalias" is normally the driver name. | | * | | * platform_data goes to spi_device.dev.platform_data, | | * controller_data goes to spi_device.controller_data, | | * irq is copied too | | */ | | char modalias[SPI_NAME_SIZE]; | | const void *platform_data; | | void *controller_data; | | int irq; | | | | /* slower signaling on noisy or low voltage boards */ | | u32 max_speed_hz; | | | | | | /* bus_num is board specific and matches the bus_num of some | | * spi_master that will probably be registered later. | | * | | * chip_select reflects how this chip is wired to that master; | | * it's less than num_chipselect. | | */ | | u16 bus_num; | | u16 chip_select; | | | | /* mode becomes spi_device.mode, and is essential for chips | | * where the default of SPI_CS_HIGH = 0 is wrong. | | */ | | u8 mode; | | | | /* ... may need additional spi_device chip config data here. | | * avoid stuff protocol drivers can set; but include stuff | | * needed to behave without being bound to a driver: | | * - quirks like clock rate mattering when not selected | | */ | | }; | | | | #define SABRESD_AR1020_INT IMX_GPIO_NR(1, 17) <------------+ | | int __init | spi_register_board_info(struct spi_board_info const *info, unsigned n) <-------+ { struct boardinfo *bi; int i; bi = kzalloc(n * sizeof(*bi), GFP_KERNEL); if (!bi) return -ENOMEM; for (i = 0; i < n; i++, bi++, info++) { struct spi_master *master; memcpy(&bi->board_info, info, sizeof(*info)); mutex_lock(&board_lock); list_add_tail(&bi->list, &board_list); list_for_each_entry(master, &spi_master_list, list) spi_match_master_to_boardinfo(master, &bi->board_info); -----+ mutex_unlock(&board_lock); | } | | return 0; | } | | static void spi_match_master_to_boardinfo(struct spi_master *master, <----+ struct spi_board_info *bi) { struct spi_device *dev; if (master->bus_num != bi->bus_num) return; dev = spi_new_device(master, bi); -----------+ if (!dev) | dev_err(master->dev.parent, "can't create new device for %s\n", | bi->modalias); | } | | struct spi_device *spi_new_device(struct spi_master *master, <----------+ struct spi_board_info *chip) { struct spi_device *proxy; int status; /* NOTE: caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). */ proxy = spi_alloc_device(master); -------------+ if (!proxy) | return NULL; | | WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); | | proxy->chip_select = chip->chip_select; | proxy->max_speed_hz = chip->max_speed_hz; | proxy->mode = chip->mode; | proxy->irq = chip->irq; | strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); | proxy->dev.platform_data = (void *) chip->platform_data; | proxy->controller_data = chip->controller_data; | proxy->controller_state = NULL; | | status = spi_add_device(proxy); ---------------*----------+ if (status < 0) { | | spi_dev_put(proxy); | | return NULL; | | } | | | | return proxy; | | } | | EXPORT_SYMBOL_GPL(spi_new_device); | | | | struct spi_device *spi_alloc_device(struct spi_master *master) <------+ | { | struct spi_device *spi; | struct device *dev = master->dev.parent; | | if (!spi_master_get(master)) | return NULL; | | spi = kzalloc(sizeof *spi, GFP_KERNEL); | if (!spi) { | dev_err(dev, "cannot alloc spi_device\n"); | spi_master_put(master); | return NULL; | } | | spi->master = master; | spi->dev.parent = &master->dev; | spi->dev.bus = &spi_bus_type; -----+ | spi->dev.release = spidev_release; | | device_initialize(&spi->dev); | | return spi; | | } | | EXPORT_SYMBOL_GPL(spi_alloc_device); | | | | struct bus_type spi_bus_type = { <----+ | .name = "spi", | .dev_attrs = spi_dev_attrs, | .match = spi_match_device, ----------------------------------+ | .uevent = spi_uevent, | | .pm = &spi_pm, | | }; | | EXPORT_SYMBOL_GPL(spi_bus_type); | | | | static int spi_match_device(struct device *dev, struct device_driver *drv) <--+ | { | const struct spi_device *spi = to_spi_device(dev); | const struct spi_driver *sdrv = to_spi_driver(drv); | | /* Attempt an OF style match */ | if (of_driver_match_device(dev, drv)) | return 1; | | if (sdrv->id_table) | return !!spi_match_id(sdrv->id_table, spi); --------------------------+ | | | return strcmp(spi->modalias, drv->name) == 0; | | } | | | | /** | | * modalias support makes "modprobe $MODALIAS" new-style hotplug work, | | * and the sysfs version makes coldplug work too. | | */ | | static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, <-+ | const struct spi_device *sdev) | { | while (id->name[0]) { | if (!strcmp(sdev->modalias, id->name)) // this very important | return id; // for match driver name | id++; | } | return NULL; | } | | int spi_add_device(struct spi_device *spi) <----------------------------+ { static DEFINE_MUTEX(spi_add_lock); struct device *dev = spi->master->dev.parent; struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ if (spi->chip_select >= spi->master->num_chipselect) { dev_err(dev, "cs%d >= max %d\n", spi->chip_select, spi->master->num_chipselect); return -EINVAL; } /* Set the bus ID string */ dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), spi->chip_select); /* We need to make sure there's no other device with this * chipselect **BEFORE** we call setup(), else we'll trash * its configuration. Lock against concurrent add() calls. */ mutex_lock(&spi_add_lock); d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); if (d != NULL) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); put_device(d); status = -EBUSY; goto done; } /* Drivers may modify this initial i/o setup, but will * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... */ status = spi_setup(spi); if (status < 0) { dev_err(dev, "can't setup %s, status %d\n", dev_name(&spi->dev), status); goto done; } /* Device may be bound to an active driver when this returns */ status = device_add(&spi->dev); if (status < 0) dev_err(dev, "can't add %s, status %d\n", dev_name(&spi->dev), status); else dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); done: mutex_unlock(&spi_add_lock); return status; } EXPORT_SYMBOL_GPL(spi_add_device); hacking ar1020-spi driver: cat drivers/input/touchscreen/ar1020-spi.c /* Enable the ar1020_spi_init() to be run by the kernel during initialization */ module_init(ar1020_spi_init); -----------------------+ | /* Enables the ar1020_spi_exit() to be called during cleanup. This only | has an effect if the driver is compiled as a kernel module. */ | module_exit(ar1020_spi_exit); | | static int __init ar1020_spi_init(void) <----------------------+ { int retval; printk("AR1020 SPI: ar1020_spi_init: begin\n"); strcpy(receiveBuffer,""); strcpy(sendBuffer,""); /* * Creates a kobject "ar1020" that appears as a sub-directory * under "/sys/kernel". */ ar1020_kobj = kobject_create_and_add("ar1020", kernel_kobj); if (!ar1020_kobj) { printk(KERN_ERR "AR1020 SPI: cannot create kobject\n"); return -ENOMEM; } /* Create the files associated with this kobject */ retval = sysfs_create_group(ar1020_kobj, &attr_group); if (retval) { printk(KERN_ERR "AR1020 SPI: error registering ar1020-spi driver's sysfs interface\n"); kobject_put(ar1020_kobj); } return spi_register_driver(&ar1020_spi_driver); --------+ } | | static struct spi_driver ar1020_spi_driver = { <-------+ .driver = { .name = "ar1020-spi", .bus = &spi_bus_type, -------------------------+ .owner = THIS_MODULE, | }, | .probe = ar1020_spi_probe, -------------------------*-+ .remove = ar1020_spi_remove, | | /* suspend/resume functions not needed since controller automatically | | put's itself to sleep mode after configurable short period of time */ | | .suspend = NULL, | | .resume = NULL, | | }; | | | | struct bus_type spi_bus_type = { <-------------------------+ | .name = "spi", | .dev_attrs = spi_dev_attrs, | .match = spi_match_device, | .uevent = spi_uevent, | .pm = &spi_pm, | }; | EXPORT_SYMBOL_GPL(spi_bus_type); | | static int __devinit ar1020_spi_probe(struct spi_device *client) <-------------+ { struct ar1020_spi_priv *priv=NULL; struct input_dev *input_dev=NULL; int err=0; int i; int j; char buff[5]; int ret; printk("AR1020 SPI: ar1020_spi_probe: begin\n"); for (i=0;i<5;i++) { buff[i]=0; } if (!client) { printk(KERN_ERR "AR1020 SPI: client pointer is NULL\n"); err = -EINVAL; goto error; } if ((!client->irq) && (touchIRQ == -1) && (!testSPIdata) && (!probeForIRQ)) { printk(KERN_ERR "AR1020 SPI: no IRQ set for touch controller\n"); err = -EINVAL; goto error; } priv = kzalloc(sizeof(struct ar1020_spi_priv), GFP_KERNEL); input_dev = input_allocate_device(); if (!priv) { printk(KERN_ERR "AR1020 SPI: kzalloc error\n"); err = -ENOMEM; goto error; } /* Backup pointer so sysfs helper functions may also have access to private data */ privRef=priv; if (!input_dev) { printk(KERN_ERR "AR1020 SPI: input allocate error\n"); err = -ENOMEM; goto error; } priv->client = client; priv->irq = client->irq; priv->input = input_dev; /* Verify raw SPI data stream to ensure bus is setup correctly in the platform settings. */ if (testSPIdata) { printk("AR1020 SPI: In testing mode to verify packet. To inhibit this mode,\n"); printk("unset the \"testSPIdata\" kernel parameter.\n"); while (1) { msleep(1); spi_read(priv->client,&buff[0],1); if (!(0x80 & buff[0])) { if ((buff[0]!= 0x4d) && (buff[0]!=0x00)) { printk("0x%02x ",buff[0]); } } else { printk("\n0x%02x ",buff[0]); for (i=1;i<5;i++) { spi_read(priv->client,&buff[i],1); printk("0x%02x ",buff[i]); } printk("\n"); } } } /* Detect IRQ id that controller IRQ line is attached to. This detection only works if the controller's IRQ line is attached to a GPIO line configured as an input. These lines are often marked as EINT (external interrupt) on the board schematic. This probe assumes that SPI read communication with the controller is working correctly. */ if (probeForIRQ) { printk("AR1020 SPI: Probing for interrupt id.\n"); printk("AR1020 SPI: Please touch screen before IRQ probe for successful detection.\n"); printk("AR1020 SPI: Probing will commence in five seconds.\n\n"); printk("AR1020 SPI: Kernel exception messages may appear during the\n"); printk("AR1020 SPI: probing process.\n"); msleep(5000); for (i=probeMin;i<probeMax;i++) { printk("AR1020 SPI: Testing IRQ %d\n",i); priv->irq=i; /* set type on new handler and register gpio pin as our interrupt */ //danny modify //set_irq_type(i, IRQ_TYPE_EDGE_RISING); //err = request_threaded_irq(client->irq, NULL, // ft5x06_ts_interrupt, IRQF_TRIGGER_FALLING, // client->dev.driver->name, data); // if (0 >= (ret=request_threaded_irq(i, NULL, // test_irq_handler_func,IRQF_TRIGGER_RISING, "AR1020 IRQ", priv))) if (0 >= (ret=request_irq(i, test_irq_handler_func,IRQF_TRIGGER_RISING, "AR1020 IRQ", priv))) { priv->testCount=0; /* read SPI data to ensure IRQ line is not asserted */ for (j=0;j<5;j++) { spi_read(priv->client,&buff[j],1); } msleep(1000); if (ret>=0) { free_irq(i, priv); } /* successful detection if count within this range */ if ((priv->testCount > 0) && (priv->testCount < 3)) { printk("AR1020 SPI: Touch IRQ detected at ID: %d.\n",i); priv->irq=i; break; } } else { printk("AR1020 SPI: IRQ %d not available.\n", i); } } if (i==probeMax) { printk("AR1020 SPI: Touch IRQ not detected. Using IRQ %d.\n",priv->irq); } } /* Use default settings */ else if (touchIRQ == -1) { printk("AR1020 SPI: Using IRQ %d set via board's platform setting.\n", priv->irq); } else { printk("AR1020 SPI: Using IRQ %d set via kernel parameter\n", touchIRQ); priv->irq=touchIRQ; } INIT_WORK(&priv->work, ar1020_spi_readdata); --------------------------------------+ | input_dev->name = "AR1020 Touchscreen"; | | input_dev->open = ar1020_spi_open; | input_dev->close = ar1020_spi_close; | | __set_bit(EV_KEY, input_dev->evbit); | __set_bit(EV_ABS, input_dev->evbit); | __set_bit(BTN_TOUCH, input_dev->keybit); | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); | //-------------add for virtualkeys | set_bit(EV_SYN, input_dev->evbit); | set_bit(KEY_HOME, input_dev->keybit); | //set_bit(KEY_SEARCH, input_dev->keybit); | set_bit(KEY_BACK, input_dev->keybit); | set_bit(KEY_MENU, input_dev->keybit); | | /* | // input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | // input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | | input_set_abs_params(input_dev, ABS_X, 0, 4095, 0, 0); | input_set_abs_params(input_dev, ABS_Y, 0, 4095, 0, 0); | input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0x7f, 0, 0); | */ | input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, | 4095, 0, 0); | input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, | 4095, 0, 0); | input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, | 1, 0, 0); | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0X7F, 0, 0); | input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0x7f, 0, 0); | | err = input_register_device(input_dev); | if (err) | { | printk(KERN_ERR "AR1020 SPI: error registering input device\n"); | goto error; | } | | for (i=0;i<5;i++) | { | spi_read(priv->client,&buff[i],1); | } | | /* set type and register gpio pin as our interrupt */ | //danny modify | //err = request_threaded_irq(client->irq, NULL, | // ft5x06_ts_interrupt, IRQF_TRIGGER_FALLING, | // client->dev.driver->name, data); | //set_irq_type(priv->irq, IRQ_TYPE_EDGE_RISING); | request_irq(priv->irq, touch_irq_handler_func, IRQF_TRIGGER_RISING, "AR1020 SPI IRQ", priv); | // request_threaded_irq(priv->irq, NULL, ^----------------------------------+ | // touch_irq_handler_func, IRQF_TRIGGER_RISING, | | // "AR1020 SPI IRQ", priv); | | printk("zengjf AR1020 SPI probe over.\n"); | | | | return 0; | | | | error: | | | | if (input_dev) | | input_free_device(input_dev); | | | | if (priv) | | kfree(priv); | | | | return err; | | | | return 0; | | } | | | | static irqreturn_t touch_irq_handler_func(int irq, void *dev_id) <------------+ | { | struct ar1020_spi_priv *priv = (struct ar1020_spi_priv *)dev_id; | char buff[5]; | int i; | int err; | for (i=0;i<5;i++) | { | buff[i]=0; | } | | printk("<danny debug> ar1020 interrupt up\n"); | if (!priv) { | printk(KERN_ERR "AR1020 SPI: touch_irq_handler_funct: no private data\n"); | err = -EINVAL; | return err; | } | | /* delegate SPI transactions since hardware interupts need to be handled very fast */ | schedule_work(&priv->work); | | return IRQ_HANDLED; | } | | static void ar1020_spi_readdata(struct work_struct *work) <-----------------------------+ { struct ar1020_spi_priv *priv = container_of(work, struct ar1020_spi_priv, work); int index=0; char buff[9]; int ret; int i; /* We want to ensure we only read packets when we are not in the middle of command communication. Disable command mode after receiving command response to resume receiving packets. */ if (commandMode) { commandDataPending=1; /* process up to 9 bytes */ strcpy(receiveBuffer,""); /* header byte */ spi_read(priv->client,&buff[0],1); snprintf(receiveBuffer,sizeof(receiveBuffer),"0x%02x",0xff&buff[0]); if (0x55 != buff[0]) { printk("AR1020 SPI: invalid header byte\n"); return; } /* num data bytes */ spi_read(priv->client,&buff[1],1); snprintf(receiveBuffer,sizeof(receiveBuffer),"%s 0x%02x",receiveBuffer,0xff&buff[1]); if (buff[1] > 6) { printk("AR1020 SPI: invalid byte count\n"); return; } for (i=0;i<buff[1];i++) { spi_read(priv->client,&buff[i+2],1); snprintf(receiveBuffer,sizeof(receiveBuffer),"%s 0x%02x",receiveBuffer,0xff&buff[i+2]); } snprintf(receiveBuffer,sizeof(receiveBuffer),"%s\n",receiveBuffer); printk(KERN_DEBUG "AR1020 SPI: command response: %s",receiveBuffer); return; } for (i=0;i<5;i++) { buff[i]=0; } /* process up to 9 bytes */ for (i=0;i<9;i++) { spi_read(priv->client,&buff[index],1); ret=decodeAR1020Packet(priv,buff, &index, buff[index]); /* if a one is returned, then we have a full packet */ if (1==ret) {   break; } } }