📄 ovfx2.c
字号:
int rc; PDEBUG(5, "0x%02X:0x%02X", reg, value); /* We don't use cbuf here, but the lock ensures we're done before * disconnect() completes */ down(&ov->cbuf_lock); if (!ov->cbuf) { up(&ov->cbuf_lock); return -ENODEV; } rc = usb_control_msg(ov->dev, usb_sndctrlpipe(ov->dev, 0), OVFX2_REQ_REG_WRITE, USB_TYPE_VENDOR | USB_RECIP_DEVICE,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12) (__u16)value, (__u16)reg, NULL, 0, 1000);#else (__u16)value, (__u16)reg, NULL, 0, HZ);#endif up(&ov->cbuf_lock); if (rc < 0) err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc)); return rc;}/* Read from an OVFX2 register *//* returns: negative is error, pos or zero is data */static intreg_r(struct usb_ovfx2 *ov, unsigned char reg){ int rc; down(&ov->cbuf_lock); if (!ov->cbuf) { up(&ov->cbuf_lock); return -ENODEV; } rc = usb_control_msg(ov->dev, usb_rcvctrlpipe(ov->dev, 0), OVFX2_REQ_REG_READ, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12) 0, (__u16)reg, &ov->cbuf[0], 1, 1000);#else 0, (__u16)reg, &ov->cbuf[0], 1, HZ);#endif if (rc < 0) { err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc)); } else { rc = ov->cbuf[0]; PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); } up(&ov->cbuf_lock); return rc;}/* * Writes bits at positions specified by mask to an OVFX2 reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits * that are in the same position as 0's in "mask" are preserved, regardless * of their respective state in "value". */static intreg_w_mask(struct usb_ovfx2 *ov, unsigned char reg, unsigned char value, unsigned char mask){ int ret; unsigned char oldval, newval; if (mask == 0xff) { newval = value; } else { ret = reg_r(ov, reg); if (ret < 0) return ret; oldval = (unsigned char) ret; oldval &= (~mask); /* Clear the masked bits */ value &= mask; /* Enforce mask on value */ newval = oldval | value; /* Set the desired bits */ } return (reg_w(ov, reg, newval));}static inti2c_w(struct usb_ovfx2 *ov, unsigned char reg, unsigned char value){ return i2c_smbus_write_byte_data(&ov->internal_client, reg, value);}static inti2c_w_mask(struct usb_ovfx2 *ov, unsigned char reg, unsigned char value, unsigned char mask){ int rc; unsigned char oldval, newval; if (mask == 0xff) { newval = value; } else { rc = i2c_smbus_read_byte_data(&ov->internal_client, reg); if (rc < 0) return rc; oldval = (unsigned char) rc; oldval &= (~mask); /* Clear the masked bits */ value &= mask; /* Enforce mask on value */ newval = oldval | value; /* Set the desired bits */ } return i2c_smbus_write_byte_data(&ov->internal_client, reg, newval);}/********************************************************************** * * Low-level I2C I/O functions * **********************************************************************//* NOTE: Do not call this function directly! * Sets I2C read and write slave IDs, if they have changed. * Returns <0 for error */static intovfx2_i2c_adap_set_slave(struct usb_ovfx2 *ov, unsigned char slave){ int rc; if (ov->last_slave == slave) return 0; /* invalidate slave */ ov->last_slave = 0; rc = reg_w(ov, REG_I2C_ADDR, (slave<<1)); if (rc < 0) return rc; /* validate slave */ ov->last_slave = slave; return 0;}static intovfx2_i2c_adap_read_byte_data(struct usb_ovfx2 *ov, unsigned char addr, unsigned char subaddr, unsigned char *val){ int rc; /* Set slave addresses */ rc = ovfx2_i2c_adap_set_slave(ov, addr); if (rc < 0) return rc; down(&ov->cbuf_lock); if (!ov->cbuf) { up(&ov->cbuf_lock); return -ENODEV; } rc = usb_control_msg(ov->dev, usb_rcvctrlpipe(ov->dev, 0), OVFX2_REQ_I2C_READ, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12) 0, (__u16)subaddr, &ov->cbuf[0], 1, 1000);#else 0, (__u16)subaddr, &ov->cbuf[0], 1, HZ);#endif if (rc < 0) { PDEBUG(5, "error %d: %s", rc, symbolic(urb_errlist, rc)); } else { *val = ov->cbuf[0]; PDEBUG(5, "0x%02X:0x%02X", subaddr, ov->cbuf[0]); } up(&ov->cbuf_lock); return rc;}static intovfx2_i2c_adap_write_byte_data(struct usb_ovfx2 *ov, unsigned char addr, unsigned char subaddr, unsigned char val){ int rc; PDEBUG(5, "(0x%02X) 0x%02X:0x%02X", addr<<1, subaddr, val); /* Set slave addresses */ rc = ovfx2_i2c_adap_set_slave(ov, addr); if (rc < 0) return rc; /* We don't use cbuf here, but the lock ensures we're done before * disconnect() completes */ down(&ov->cbuf_lock); if (!ov->cbuf) { up(&ov->cbuf_lock); return -ENODEV; } rc = usb_control_msg(ov->dev, usb_sndctrlpipe(ov->dev, 0), OVFX2_REQ_I2C_WRITE, USB_TYPE_VENDOR | USB_RECIP_DEVICE,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12) (__u16)val, (__u16)subaddr, NULL, 0, 1000);#else (__u16)val, (__u16)subaddr, NULL, 0, HZ);#endif up(&ov->cbuf_lock); if (rc < 0) PDEBUG(5, "error %d: %s", rc, symbolic(urb_errlist, rc)); return rc;}static intwrite_regvals(struct usb_ovfx2 *ov, struct regval *regvals){ int rc; while (regvals->mask != 0) { rc = reg_w_mask(ov, regvals->reg, regvals->val, regvals->mask); if (rc < 0) return rc; regvals++; } return 0;}#ifdef OVFX2_DEBUGstatic voiddump_reg_range(struct usb_ovfx2 *ov, int reg1, int regn){ int i, rc; for (i = reg1; i <= regn; i++) { rc = reg_r(ov, i); info("OVFX2[0x%02X] = 0x%02X", i, rc); }}static voidovfx2_dump_regs(struct usb_ovfx2 *ov){ dump_reg_range(ov, 0x00, 0xff);}#endif/********************************************************************** * * Kernel I2C Interface * **********************************************************************/static intovfx2_i2c_validate_addr(struct usb_ovfx2 *ov, u16 addr){ switch (addr) { case OV7xx0_SID: return 0; default: PDEBUG(4, "Rejected slave ID 0x%04X", addr); return -EINVAL; }}static inline intsensor_cmd(struct usb_ovfx2 *ov, unsigned int cmd, void *arg){ struct i2c_client *c = ov->sensor_client; if (c && c->driver->command) return c->driver->command(ov->sensor_client, cmd, arg); else return -ENODEV;}#if 0static voidcall_i2c_clients(struct usb_ovfx2 *ov, unsigned int cmd, void *arg){#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) struct i2c_client *client; int i; for (i = 0; i < I2C_CLIENT_MAX; i++) { client = ov->i2c_adap.clients[i]; /* Sensor_client is not called from here for now, since it has different enumeration for cmd */ if (client == ov->sensor_client) continue; if (client && client->driver->command) client->driver->command(client, cmd, arg); }#else i2c_clients_command(&ov->i2c_adap, cmd, arg);#endif}#endifstatic intovfx2_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data){ struct usb_ovfx2 *ov = i2c_get_adapdata(adapter); int rc = -ENOSYS; if (size == I2C_SMBUS_QUICK) { PDEBUG(4, "Got probed at addr 0x%04X", addr); rc = ovfx2_i2c_validate_addr(ov, addr); } else if (size == I2C_SMBUS_BYTE_DATA) { if (read_write == I2C_SMBUS_WRITE) { rc = ovfx2_i2c_adap_write_byte_data(ov, addr, command, data->byte); } else if (read_write == I2C_SMBUS_READ) { rc = ovfx2_i2c_adap_read_byte_data(ov, addr, command, &data->byte); } } else { warn("Unsupported I2C transfer mode (%d)", size); return -EINVAL; } /* This works around a bug in the I2C core */ if (rc > 0) rc = 0; return rc;}static u32ovfx2_i2c_func(struct i2c_adapter *adap){ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE_DATA;}static inti2c_attach_inform(struct i2c_client *client){ struct usb_ovfx2 *ov = i2c_get_adapdata(client->adapter); int id = client->driver->id; if (id == I2C_DRIVERID_OVCAMCHIP) { int rc, mono = 0; ov->sensor_client = client; rc = sensor_cmd(ov, OVCAMCHIP_CMD_INITIALIZE, &mono); if (rc < 0) { err("ERROR: Sensor init failed (rc=%d)", rc); ov->sensor_client = NULL; return rc; } down(&ov->lock); if (sensor_cmd(ov, OVCAMCHIP_CMD_Q_SUBTYPE, &ov->sensor) < 0) rc = -EIO; else if (client->addr == OV7xx0_SID) rc = ov7xx0_configure(ov); else rc = -EINVAL; up(&ov->lock); if (rc) { ov->sensor_client = NULL; return rc; } } else { PDEBUG(1, "Rejected client [%s] with [%s]",#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) client->name, client->driver->name);#else client->name, client->driver->driver.name);#endif return -1; } PDEBUG(1, "i2c attach client [%s] with [%s]",#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) client->name, client->driver->name);#else client->name, client->driver->driver.name);#endif return 0;}static inti2c_detach_inform(struct i2c_client *client){ struct usb_ovfx2 *ov = i2c_get_adapdata(client->adapter); if (ov->sensor_client == client) { ov->sensor_client = NULL; } PDEBUG(1, "i2c detach [%s]", client->name); return 0;}static int ovfx2_i2c_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg){ return 0;}#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)static voidovfx2_i2c_inc_use(struct i2c_adapter *adap){ MOD_INC_USE_COUNT;}static voidovfx2_i2c_dec_use(struct i2c_adapter *adap){ MOD_DEC_USE_COUNT;}#endifstatic struct i2c_algorithm ovfx2_i2c_algo = {#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) .name = "OVFX2 algorithm", .id = I2C_ALGO_SMBUS,#endif .smbus_xfer = ovfx2_smbus_xfer, .algo_control = ovfx2_i2c_control, .functionality = ovfx2_i2c_func,};static struct i2c_adapter i2c_adap_template = { .name = "(unset)",#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_OVFX2,#else .id = I2C_HW_SMBUS_OVFX2,#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 6) .class = I2C_CLASS_CAM_DIGITAL,#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70) .class = I2C_ADAP_CLASS_CAM_DIGITAL,#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) .inc_use = ovfx2_i2c_inc_use, .dec_use = ovfx2_i2c_dec_use,#else .owner = THIS_MODULE,#endif .client_register = i2c_attach_inform, .client_unregister = i2c_detach_inform,};static intovfx2_init_i2c(struct usb_ovfx2 *ov){ memcpy(&ov->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); /* Temporary name. We'll set the final one when we know the minor # */ sprintf(ov->i2c_adap.name, "OVFX2"); i2c_set_adapdata(&ov->i2c_adap, ov); ov->i2c_adap.algo = &ovfx2_i2c_algo; ov->internal_client.adapter = &ov->i2c_adap; PDEBUG(4, "Registering I2C bus with kernel"); return i2c_add_adapter(&ov->i2c_adap);}/*****************************************************************************//* Pause video streaming */static inline intovfx2_pause(struct usb_ovfx2 *ov){ PDEBUG(4, "pausing stream"); ov->stopped = 1; return reg_w_mask(ov, 0x0f, 0x00, 0x02);}/* Resume video streaming if it was stopped. */static inline intovfx2_resume(struct usb_ovfx2 *ov){ if (ov->stopped) { PDEBUG(4, "resuming stream"); ov->stopped = 0; return reg_w_mask(ov, 0x0f, 0x02, 0x02); } return 0;}/* Returns 1 if image streaming needs to be stopped while setting the specified * control, and returns 0 if not */static intsensor_needs_stop(struct usb_ovfx2 *ov, int cid){ if (!ov->stop_during_set) return 0; /* FIXME: I don't know whether the FX2 needs to be stopped yet. Don't * do it yet, since it might not be safe to pause the stream while bulk * requests are active */ return 0; switch (cid) { case OVCAMCHIP_CID_CONT: case OVCAMCHIP_CID_BRIGHT: case OVCAMCHIP_CID_SAT: case OVCAMCHIP_CID_HUE: case OVCAMCHIP_CID_EXP: return 1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -